Merge changes I02f84f73,I61d6f01c,I6ed7a94b into main

* changes:
  [Networking] Filter out PROFILE_CLASS_PROVISIONING subscriptions
  [Networking] Define FILTER_PRIVISIONING_NETWORK_SUBSCRIPTIONS flag
  [Networking] Add [SubscriptionModel.provisioningClass] field for tracking
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index db51537..e3cbd92 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -14,12 +14,14 @@
 
 aconfig_srcjars = [
     ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+    ":android.app.smartspace.flags-aconfig-java{.generated_srcjars}",
     ":android.companion.flags-aconfig-java{.generated_srcjars}",
     ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
     ":android.content.res.flags-aconfig-java{.generated_srcjars}",
     ":android.hardware.flags-aconfig-java{.generated_srcjars}",
     ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
     ":android.location.flags-aconfig-java{.generated_srcjars}",
+    ":android.net.vcn.flags-aconfig-java{.generated_srcjars}",
     ":android.nfc.flags-aconfig-java{.generated_srcjars}",
     ":android.os.flags-aconfig-java{.generated_srcjars}",
     ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
@@ -567,6 +569,19 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// VCN
+aconfig_declarations {
+    name: "android.net.vcn.flags-aconfig",
+    package: "android.net.vcn",
+    srcs: ["core/java/android/net/vcn/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.net.vcn.flags-aconfig-java",
+    aconfig_declarations: "android.net.vcn.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // DevicePolicy
 aconfig_declarations {
     name: "device_policy_aconfig_flags",
@@ -599,3 +614,16 @@
     aconfig_declarations: "android.service.notification.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Smartspace
+aconfig_declarations {
+    name: "android.app.smartspace.flags-aconfig",
+    package: "android.app.smartspace.flags",
+    srcs: ["core/java/android/app/smartspace/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.app.smartspace.flags-aconfig-java",
+    aconfig_declarations: "android.app.smartspace.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 7d5e788..b5f7e99 100644
--- a/Android.bp
+++ b/Android.bp
@@ -614,30 +614,6 @@
     ],
 }
 
-// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
-metalava_framework_docs_args = "" +
-    "--api-lint-ignore-prefix android.icu. " +
-    "--api-lint-ignore-prefix java. " +
-    "--api-lint-ignore-prefix junit. " +
-    "--api-lint-ignore-prefix org. " +
-    "--error NoSettingsProvider " +
-    "--error UnhiddenSystemApi " +
-    "--error UnflaggedApi " +
-    "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
-    "--hide BroadcastBehavior " +
-    "--hide CallbackInterface " +
-    "--hide DeprecationMismatch " +
-    "--hide HiddenSuperclass " +
-    "--hide HiddenTypeParameter " +
-    "--hide MissingPermission " +
-    "--hide-package android.audio.policy.configuration.V7_0 " +
-    "--hide-package com.android.server " +
-    "--hide RequiresPermission " +
-    "--hide SdkConstant " +
-    "--hide Todo " +
-    "--hide UnavailableSymbol " +
-    "--manifest $(location :frameworks-base-core-AndroidManifest.xml) "
-
 packages_to_document = [
     "android",
     "dalvik",
@@ -706,6 +682,27 @@
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
     ],
+    flags: [
+        "--api-lint-ignore-prefix android.icu.",
+        "--api-lint-ignore-prefix java.",
+        "--api-lint-ignore-prefix junit.",
+        "--api-lint-ignore-prefix org.",
+        "--error NoSettingsProvider",
+        "--error UnhiddenSystemApi",
+        "--error UnflaggedApi",
+        "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
+        "--hide BroadcastBehavior",
+        "--hide CallbackInterface",
+        "--hide DeprecationMismatch",
+        "--hide HiddenSuperclass",
+        "--hide MissingPermission",
+        "--hide RequiresPermission",
+        "--hide SdkConstant",
+        "--hide Todo",
+        "--hide-package android.audio.policy.configuration.V7_0",
+        "--hide-package com.android.server",
+        "--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
+    ],
     filter_packages: packages_to_document,
     high_mem: true, // Lots of sources => high memory use, see b/170701554
     installable: false,
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 3e835b8..eb5502b 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -3,6 +3,6 @@
 flag {
     name: "relax_prefetch_connectivity_constraint_only_on_charger"
     namespace: "backstage_power"
-    description: "Only relax a prefetch job's connectivity constraint when the device is charging"
+    description: "Only relax a prefetch job's connectivity constraint when the device is charging and battery is not low"
     bug: "299329948"
 }
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 5bf2eb9..6550f26 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1518,8 +1518,8 @@
             @WorkType final int workType) {
         final List<StateController> controllers = mService.mControllers;
         final int numControllers = controllers.size();
-        final PowerManager.WakeLock wl =
-                mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, jobStatus.getTag());
+        final PowerManager.WakeLock wl = mPowerManager.newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, jobStatus.getWakelockTag());
         wl.setWorkSource(mService.deriveWorkSource(
                 jobStatus.getSourceUid(), jobStatus.getSourcePackageName()));
         wl.setReferenceCounted(false);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index bbe1485..592aff8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -5512,7 +5512,6 @@
                 pw.print("Evaluated bias: ");
                 pw.println(JobInfo.getBiasString(bias));
 
-                pw.print("Tag: "); pw.println(job.getTag());
                 pw.print("Enq: ");
                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
                 pw.decreaseIndent();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 4357d4f..293088d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -180,7 +180,7 @@
 
                 case "-u":
                 case "--user":
-                    userId = Integer.parseInt(getNextArgRequired());
+                    userId = UserHandle.parseUserArg(getNextArgRequired());
                     break;
 
                 case "-n":
@@ -199,6 +199,10 @@
             return -1;
         }
 
+        if (userId == UserHandle.USER_CURRENT) {
+            userId = ActivityManager.getCurrentUser();
+        }
+
         final String pkgName = getNextArgRequired();
         final int jobId = Integer.parseInt(getNextArgRequired());
 
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 f47766e..79653f0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -398,7 +398,8 @@
             // it was inflated from disk with not-yet-coherent delay/deadline bounds.
             job.clearPersistedUtcTimes();
 
-            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, job.getTag());
+            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    job.getWakelockTag());
             mWakeLock.setWorkSource(
                     mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
             mWakeLock.setReferenceCounted(false);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 45f15db..63eaa63 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -932,7 +932,9 @@
             return false;
         }
         if (relaxPrefetchConnectivityConstraintOnlyOnCharger()) {
-            if (!mService.isBatteryCharging()) {
+            // Since the constraint relaxation isn't required by the job, only do it when the
+            // device is charging and the battery level is above the "low battery" threshold.
+            if (!mService.isBatteryCharging() || !mService.isBatteryNotLow()) {
                 return false;
             }
         }
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 458ff35..1fb54d5 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
@@ -253,7 +253,12 @@
     /** An ID that can be used to uniquely identify the job when logging statsd metrics. */
     private final long mLoggingJobId;
 
-    final String tag;
+    /**
+     * Tag to identify the wakelock held for this job. Lazily loaded in
+     * {@link #getWakelockTag()} since it's not typically needed until the job is about to run.
+     */
+    @Nullable
+    private String mWakelockTag;
 
     /** Whether this job was scheduled by one app on behalf of another. */
     final boolean mIsProxyJob;
@@ -627,7 +632,6 @@
         this.batteryName = this.sourceTag != null
                 ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
                 : bnNamespace + job.getService().flattenToShortString();
-        this.tag = "*job*/" + this.batteryName + "#" + job.getId();
 
         final String componentPackage = job.getService().getPackageName();
         mIsProxyJob = !this.sourcePackageName.equals(componentPackage);
@@ -1321,8 +1325,13 @@
         return batteryName;
     }
 
-    public String getTag() {
-        return tag;
+    /** Return the String to be used as the tag for the wakelock held for this job. */
+    @NonNull
+    public String getWakelockTag() {
+        if (mWakelockTag == null) {
+            mWakelockTag = "*job*/" + this.batteryName;
+        }
+        return mWakelockTag;
     }
 
     public int getBias() {
@@ -2639,7 +2648,7 @@
     @NeverCompile // Avoid size overhead of debugging code.
     public void dump(IndentingPrintWriter pw,  boolean full, long nowElapsed) {
         UserHandle.formatUid(pw, callingUid);
-        pw.print(" tag="); pw.println(tag);
+        pw.print(" tag="); pw.println(getWakelockTag());
 
         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
         pw.print(" user="); pw.print(getSourceUserId());
@@ -2955,7 +2964,7 @@
         final long token = proto.start(fieldId);
 
         proto.write(JobStatusDumpProto.CALLING_UID, callingUid);
-        proto.write(JobStatusDumpProto.TAG, tag);
+        proto.write(JobStatusDumpProto.TAG, getWakelockTag());
         proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
         proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
         proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index e086bfe..5744bdf 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -72,7 +72,6 @@
         "android-non-updatable-doc-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args,
 }
 
 droidstubs {
@@ -81,8 +80,7 @@
         "android-non-updatable-doc-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+    flags: ["--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"],
 }
 
 droidstubs {
@@ -91,9 +89,10 @@
         "android-non-updatable-doc-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) " +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\) ",
+    flags: [
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+    ],
     generate_stubs: false, // We're only using this module for the annotations.zip output, disable doc-stubs.
     write_sdk_values: false,
 }
@@ -104,10 +103,11 @@
         "android-non-updatable-doc-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) " +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\) " +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\) ",
+    flags: [
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)",
+    ],
     generate_stubs: false, // We're only using this module for the annotations.zip output, disable doc-stubs.
     write_sdk_values: false,
 }
@@ -116,7 +116,6 @@
     name: "framework-doc-stubs",
     defaults: ["android-non-updatable-doc-stubs-defaults"],
     srcs: [":all-modules-public-stubs-source"],
-    args: metalava_framework_docs_args,
     api_levels_module: "api_versions_public",
     aidl: {
         include_dirs: [
@@ -129,8 +128,7 @@
 droidstubs {
     name: "framework-doc-system-stubs",
     defaults: ["framework-doc-stubs-sources-default"],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+    flags: ["--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"],
     api_levels_module: "api_versions_system",
 }
 
@@ -139,30 +137,6 @@
 // using droiddoc
 /////////////////////////////////////////////////////////////////////
 
-// doclava contains checks for a few issues that are have been migrated to metalava.
-// disable them in doclava, to avoid mistriggering or double triggering.
-ignore_doclava_errors_checked_by_metalava = "" +
-    "-hide 111 " + // HIDDEN_SUPERCLASS
-    "-hide 113 " + // DEPRECATION_MISMATCH
-    "-hide 125 " + // REQUIRES_PERMISSION
-    "-hide 126 " + // BROADCAST_BEHAVIOR
-    "-hide 127 " + // SDK_CONSTANT
-    "-hide 128 " // TODO
-
-framework_docs_only_args = "-android " +
-    "-manifest $(location :frameworks-base-core-AndroidManifest.xml) " +
-    "-metalavaApiSince " +
-    "-werror " +
-    "-lerror " +
-    ignore_doclava_errors_checked_by_metalava +
-    "-overview $(location :frameworks-base-java-overview) " +
-    // Federate Support Library references against local API file.
-    "-federate SupportLib https://developer.android.com " +
-    "-federationapi SupportLib $(location :current-support-api) " +
-    // Federate Support Library references against local API file.
-    "-federate AndroidX https://developer.android.com " +
-    "-federationapi AndroidX $(location :current-androidx-api) "
-
 doc_defaults {
     name: "framework-docs-default",
     sdk_version: "none",
@@ -182,6 +156,28 @@
     resourcesdir: "docs/html/reference/images/",
     resourcesoutdir: "reference/android/images/",
     lint_baseline: "javadoc-lint-baseline",
+    flags: [
+        "-android",
+        "-manifest $(location :frameworks-base-core-AndroidManifest.xml)",
+        "-metalavaApiSince",
+        "-werror",
+        "-lerror",
+        "-overview $(location :frameworks-base-java-overview)",
+        // Federate Support Library references against local API file.
+        "-federate SupportLib https://developer.android.com",
+        "-federationapi SupportLib $(location :current-support-api)",
+        // Federate Support Library references against local API file.
+        "-federate AndroidX https://developer.android.com",
+        "-federationapi AndroidX $(location :current-androidx-api)",
+        // doclava contains checks for a few issues that are have been migrated to metalava.
+        // disable them in doclava, to avoid mistriggering or double triggering.
+        "-hide 111", // HIDDEN_SUPERCLASS
+        "-hide 113", // DEPRECATION_MISMATCH
+        "-hide 125", // REQUIRES_PERMISSION
+        "-hide 126", // BROADCAST_BEHAVIOR
+        "-hide 127", // SDK_CONSTANT
+        "-hide 128", // TODO
+    ],
     hdf: [
         "dac true",
         "sdk.codename O",
@@ -217,7 +213,10 @@
     ],
     compat_config: ":global-compat-config",
     proofread_file: "offline-sdk-docs-proofread.txt",
-    args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
+    flags: [
+        "-offlinemode",
+        "-title \"Android SDK\"",
+    ],
     static_doc_index_redirect: "docs/docs-preview-index.html",
 }
 
@@ -234,7 +233,11 @@
         "android.whichdoc offline",
     ],
     proofread_file: "offline-sdk-referenceonly-docs-proofread.txt",
-    args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
+    flags: [
+        "-offlinemode",
+        "-title \"Android SDK\"",
+        "-referenceonly",
+    ],
     static_doc_index_redirect: "docs/docs-documentation-redirect.html",
     static_doc_properties: "docs/source.properties",
 }
@@ -252,8 +255,14 @@
         "android.whichdoc offline",
     ],
     proofread_file: "offline-system-sdk-referenceonly-docs-proofread.txt",
-    args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" +
-    " -offlinemode -title \"Android System SDK\" -referenceonly",
+    flags: [
+        "-hide 101",
+        "-hide 104",
+        "-hide 108",
+        "-offlinemode",
+        "-title \"Android System SDK\"",
+        "-referenceonly",
+    ],
     static_doc_index_redirect: "docs/docs-documentation-redirect.html",
     static_doc_properties: "docs/source.properties",
 }
@@ -269,22 +278,28 @@
         "android.hasSamples true",
     ],
     proofread_file: "ds-docs-proofread.txt",
-    args: framework_docs_only_args +
-        " -toroot / -yamlV2 -samplegroup Admin " +
-        " -samplegroup Background " +
-        " -samplegroup Connectivity " +
-        " -samplegroup Content " +
-        " -samplegroup Input " +
-        " -samplegroup Media " +
-        " -samplegroup Notification " +
-        " -samplegroup RenderScript " +
-        " -samplegroup Security " +
-        " -samplegroup Sensors " +
-        " -samplegroup System " +
-        " -samplegroup Testing " +
-        " -samplegroup UI " +
-        " -samplegroup Views " +
-        " -samplegroup Wearable -devsite -samplesdir development/samples/browseable ",
+    flags: [
+        " -toroot /",
+        "-yamlV2",
+        "-samplegroup Admin",
+        "-samplegroup Background",
+        "-samplegroup Connectivity",
+        "-samplegroup Content",
+        "-samplegroup Input",
+        "-samplegroup Media",
+        "-samplegroup Notification",
+        "-samplegroup RenderScript",
+        "-samplegroup Security",
+        "-samplegroup Sensors",
+        "-samplegroup System",
+        "-samplegroup Testing",
+        "-samplegroup UI",
+        "-samplegroup Views",
+        "-samplegroup Wearable",
+        "-devsite",
+        "-samplesdir",
+        "development/samples/browseable",
+    ],
 }
 
 droiddoc {
@@ -292,8 +307,11 @@
     srcs: [
         ":framework-doc-stubs",
     ],
-    args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
+    flags: [
+        "-noJdkLink",
+        "-links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list",
         "-noStdlibLink",
+    ],
     proofread_file: "ds-dokka-proofread.txt",
     dokka_enabled: true,
 }
@@ -346,11 +364,12 @@
     hdf: [
         "android.whichdoc online",
     ],
-    args: framework_docs_only_args +
-        " -staticonly " +
-        " -toroot / " +
-        " -devsite " +
-        " -ignoreJdLinks ",
+    flags: [
+        "-staticonly",
+        "-toroot /",
+        "-devsite",
+        "-ignoreJdLinks",
+    ],
 }
 
 droiddoc {
@@ -362,8 +381,9 @@
     hdf: [
         "android.whichdoc online",
     ],
-    args: framework_docs_only_args +
-        " -toroot / " +
-        " -atLinksNavtree " +
-        " -navtreeonly ",
+    flags: [
+        "-toroot /",
+        "-atLinksNavtree",
+        "-navtreeonly",
+    ],
 }
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 7e78185..d566552 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -36,7 +36,6 @@
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args,
     check_api: {
         current: {
             api_file: ":non-updatable-current.txt",
@@ -70,19 +69,25 @@
     api_surface: "public",
 }
 
-priv_apps = " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
-    "\\)"
+priv_apps = [
+    "--show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+        "\\)",
+]
 
-priv_apps_in_stubs = " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
-    "\\)"
+priv_apps_in_stubs = [
+    "--show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+        "\\)",
+]
 
-test = " --show-annotation android.annotation.TestApi"
+test = ["--show-annotation android.annotation.TestApi"]
 
-module_libs = " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
-    "\\)"
+module_libs = [
+    "--show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+        "\\)",
+]
 
 droidstubs {
     name: "system-api-stubs-docs-non-updatable",
@@ -93,7 +98,7 @@
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args + priv_apps,
+    flags: priv_apps,
     check_api: {
         current: {
             api_file: ":non-updatable-system-current.txt",
@@ -136,7 +141,7 @@
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args + test + priv_apps_in_stubs,
+    flags: test + priv_apps_in_stubs,
     check_api: {
         current: {
             api_file: ":non-updatable-test-current.txt",
@@ -186,7 +191,7 @@
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
+    flags: priv_apps_in_stubs + module_libs,
     check_api: {
         current: {
             api_file: ":non-updatable-module-lib-current.txt",
@@ -972,7 +977,7 @@
     merge_annotations_dirs: [
         "metalava-manual",
     ],
-    args: priv_apps,
+    flags: priv_apps,
 }
 
 java_library {
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index 71f366a..e0153f7 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@
 
 # Convert each removed.txt to the "dex format" equivalent, and print all output.
 for f in "$@"; do
-    "$metalava_path" "$f" --dex-api "${tmp_dir}/tmp"
+    "$metalava_path" signature-to-dex "$f" "${tmp_dir}/tmp"
     cat "${tmp_dir}/tmp"
 done
diff --git a/core/api/current.txt b/core/api/current.txt
index 4a03d02..18001e8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -232,6 +232,7 @@
     field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
     field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
     field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
+    field @FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission") public static final String READ_DROPBOX_DATA = "android.permission.READ_DROPBOX_DATA";
     field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
     field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
     field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
@@ -11101,7 +11102,7 @@
     field public static final String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
     field @Deprecated public static final String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
     field public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
-    field public static final String EXTRA_ARCHIVAL = "android.intent.extra.ARCHIVAL";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_ARCHIVAL = "android.intent.extra.ARCHIVAL";
     field public static final String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT";
     field public static final String EXTRA_ASSIST_INPUT_DEVICE_ID = "android.intent.extra.ASSIST_INPUT_DEVICE_ID";
     field public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
@@ -11988,6 +11989,40 @@
     method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
   }
 
+  @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivity {
+    ctor public ArchivedActivity(@NonNull CharSequence, @NonNull android.content.ComponentName);
+    method @NonNull public android.content.ComponentName getComponentName();
+    method @Nullable public android.graphics.drawable.Drawable getIcon();
+    method @NonNull public CharSequence getLabel();
+    method @Nullable public android.graphics.drawable.Drawable getMonochromeIcon();
+    method @NonNull public android.content.pm.ArchivedActivity setComponentName(@NonNull android.content.ComponentName);
+    method @NonNull public android.content.pm.ArchivedActivity setIcon(@NonNull android.graphics.drawable.Drawable);
+    method @NonNull public android.content.pm.ArchivedActivity setLabel(@NonNull CharSequence);
+    method @NonNull public android.content.pm.ArchivedActivity setMonochromeIcon(@NonNull android.graphics.drawable.Drawable);
+  }
+
+  @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackage {
+    ctor public ArchivedPackage(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivity>);
+    method @Nullable public String getDefaultToDeviceProtectedStorage();
+    method @NonNull public java.util.List<android.content.pm.ArchivedActivity> getLauncherActivities();
+    method @NonNull public String getPackageName();
+    method @Nullable public String getRequestLegacyExternalStorage();
+    method @NonNull public android.content.pm.SigningInfo getSigningInfo();
+    method public int getTargetSdkVersion();
+    method @Nullable public String getUserDataFragile();
+    method public int getVersionCode();
+    method public int getVersionCodeMajor();
+    method @NonNull public android.content.pm.ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackage setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivity>);
+    method @NonNull public android.content.pm.ArchivedPackage setPackageName(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackage setRequestLegacyExternalStorage(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackage setSigningInfo(@NonNull android.content.pm.SigningInfo);
+    method @NonNull public android.content.pm.ArchivedPackage setTargetSdkVersion(int);
+    method @NonNull public android.content.pm.ArchivedPackage setUserDataFragile(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackage setVersionCode(int);
+    method @NonNull public android.content.pm.ArchivedPackage setVersionCodeMajor(int);
+  }
+
   public final class Attribution implements android.os.Parcelable {
     method public int describeContents();
     method @IdRes public int getLabel();
@@ -12320,6 +12355,7 @@
     method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
     method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender);
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackage, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender);
     method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
@@ -12601,6 +12637,7 @@
     method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackage getArchivedPackage(@NonNull String);
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
@@ -12852,7 +12889,7 @@
     field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access";
     field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
     field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
-    field public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
+    field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
     field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
     field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
     field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -13285,9 +13322,12 @@
 
   public final class SigningInfo implements android.os.Parcelable {
     ctor public SigningInfo();
+    ctor @FlaggedApi("android.content.pm.archiving") public SigningInfo(@IntRange(from=0) int, @Nullable java.util.Collection<android.content.pm.Signature>, @Nullable java.util.Collection<java.security.PublicKey>, @Nullable java.util.Collection<android.content.pm.Signature>);
     ctor public SigningInfo(android.content.pm.SigningInfo);
     method public int describeContents();
     method public android.content.pm.Signature[] getApkContentsSigners();
+    method @FlaggedApi("android.content.pm.archiving") @NonNull public java.util.Collection<java.security.PublicKey> getPublicKeys();
+    method @FlaggedApi("android.content.pm.archiving") @IntRange(from=0) public int getSchemeVersion();
     method public android.content.pm.Signature[] getSigningCertificateHistory();
     method public boolean hasMultipleSigners();
     method public boolean hasPastSigningCertificates();
@@ -17676,12 +17716,14 @@
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_DISABLED = 0; // 0x0
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_ENABLED = 1; // 0x1
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
+    field @FlaggedApi("com.android.text.flags.word_style_auto") public static final int LINE_BREAK_STYLE_AUTO = 5; // 0x5
     field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
     field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
     field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_NO_BREAK = 4; // 0x4
     field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff
+    field @FlaggedApi("com.android.text.flags.word_style_auto") public static final int LINE_BREAK_WORD_STYLE_AUTO = 2; // 0x2
     field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0
     field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff
@@ -32404,7 +32446,7 @@
     method public void addData(@NonNull String, @Nullable byte[], int);
     method public void addFile(@NonNull String, @NonNull java.io.File, int) throws java.io.IOException;
     method public void addText(@NonNull String, @NonNull String);
-    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_LOGS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.DropBoxManager.Entry getNextEntry(String, long);
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_DROPBOX_DATA, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.DropBoxManager.Entry getNextEntry(String, long);
     method public boolean isTagEnabled(String);
     field public static final String ACTION_DROPBOX_ENTRY_ADDED = "android.intent.action.DROPBOX_ENTRY_ADDED";
     field public static final String EXTRA_DROPPED_COUNT = "android.os.extra.DROPPED_COUNT";
@@ -33011,7 +33053,7 @@
   public static class PerformanceHintManager.Session implements java.io.Closeable {
     method public void close();
     method public void reportActualWorkDuration(long);
-    method public void setPreferPowerEfficiency(boolean);
+    method @FlaggedApi("android.os.adpf_prefer_power_efficiency") public void setPreferPowerEfficiency(boolean);
     method public void setThreads(@NonNull int[]);
     method public void updateTargetWorkDuration(long);
   }
@@ -33413,7 +33455,9 @@
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(String);
     method public boolean isAdminUser();
+    method @FlaggedApi("android.multiuser.support_communal_profile") public boolean isCommunalProfile();
     method public boolean isDemoUser();
+    method @FlaggedApi("android.multiuser.support_communal_profile_nextgen") public boolean isForegroundUserAdmin();
     method public static boolean isHeadlessSystemUserMode();
     method public boolean isManagedProfile();
     method public boolean isProfile();
@@ -43127,6 +43171,7 @@
     field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
     field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
     field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.hide_roaming_icon") public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
     field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool";
     field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool";
     field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
@@ -44464,7 +44509,7 @@
     method public static final boolean isStartsPostDial(char);
     method public static boolean isVoiceMailNumber(String);
     method public static boolean isWellFormedSmsAddress(String);
-    method public static boolean isWpsCallNumber(@Nullable String);
+    method @FlaggedApi("com.android.internal.telephony.flags.enable_wps_check_api_flag") public static boolean isWpsCallNumber(@NonNull String);
     method public static byte[] networkPortionToCalledPartyBCD(String);
     method public static byte[] networkPortionToCalledPartyBCDWithLength(String);
     method public static String normalizeNumber(String);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 2af3c34..5bbff0b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -120,7 +120,6 @@
     method @NonNull public android.os.IBinder getProcessToken();
     method @NonNull public android.os.UserHandle getUser();
     field public static final String PAC_PROXY_SERVICE = "pac_proxy";
-    field public static final String REMOTE_AUTH_SERVICE = "remote_auth";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1a7810a..6062f79 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -315,6 +315,7 @@
     field @FlaggedApi("android.app.usage.report_usage_stats_permission") public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
     field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
+    field @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final String RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT = "android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT";
     field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
     field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
     field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
@@ -2451,6 +2452,7 @@
     method public int getFeatureType();
     method @Nullable public android.app.smartspace.SmartspaceAction getHeaderAction();
     method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getIconGrid();
+    method @FlaggedApi("android.app.smartspace.flags.remote_views") @Nullable public android.widget.RemoteViews getRemoteViews();
     method public float getScore();
     method @Nullable public android.net.Uri getSliceUri();
     method @NonNull public String getSmartspaceTargetId();
@@ -2525,6 +2527,7 @@
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setFeatureType(int);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setHeaderAction(@NonNull android.app.smartspace.SmartspaceAction);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setIconGrid(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+    method @FlaggedApi("android.app.smartspace.flags.remote_views") @NonNull public android.app.smartspace.SmartspaceTarget.Builder setRemoteViews(@NonNull android.widget.RemoteViews);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setScore(float);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSensitive(boolean);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setShouldShowExpanded(boolean);
@@ -3486,7 +3489,7 @@
     field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final String TETHERING_SERVICE = "tethering";
-    field public static final String THREAD_NETWORK_SERVICE = "thread_network";
+    field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_SERVICE = "thread_network";
     field public static final String TIME_MANAGER_SERVICE = "time_manager";
     field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
     field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -3872,6 +3875,7 @@
     method public void setInstallAsInstantApp(boolean);
     method public void setInstallAsVirtualPreload();
     method public void setRequestDowngrade(boolean);
+    method @FlaggedApi("android.content.pm.rollback_lifetime") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackLifetimeMillis(long);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
   }
 
@@ -5583,7 +5587,8 @@
     method public void addOnCompleteListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.radio.ProgramList.OnCompleteListener);
     method public void addOnCompleteListener(@NonNull android.hardware.radio.ProgramList.OnCompleteListener);
     method public void close();
-    method @Nullable public android.hardware.radio.RadioManager.ProgramInfo get(@NonNull android.hardware.radio.ProgramSelector.Identifier);
+    method @Deprecated @Nullable public android.hardware.radio.RadioManager.ProgramInfo get(@NonNull android.hardware.radio.ProgramSelector.Identifier);
+    method @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull public java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramInfos(@NonNull android.hardware.radio.ProgramSelector.Identifier);
     method public void registerListCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.radio.ProgramList.ListCallback);
     method public void registerListCallback(@NonNull android.hardware.radio.ProgramList.ListCallback);
     method public void removeOnCompleteListener(@NonNull android.hardware.radio.ProgramList.OnCompleteListener);
@@ -5637,12 +5642,13 @@
     field @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
     field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
     field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int IDENTIFIER_TYPE_HD_STATION_LOCATION = 15; // 0xf
     field public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; // 0x2714
     field @Deprecated public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
     field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0
     field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
-    field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
-    field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
+    field @Deprecated public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
+    field @Deprecated public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
     field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf
     field @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
     field @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
@@ -5657,6 +5663,14 @@
     field @Deprecated public static final int PROGRAM_TYPE_SXM = 7; // 0x7
     field @Deprecated public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
     field @Deprecated public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_1 = 1; // 0x1
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_2 = 2; // 0x2
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_3 = 4; // 0x4
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_4 = 8; // 0x8
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_5 = 16; // 0x10
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_6 = 32; // 0x20
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_7 = 64; // 0x40
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int SUB_CHANNEL_HD_8 = 128; // 0x80
   }
 
   public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -5669,7 +5683,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector.Identifier> CREATOR;
   }
 
-  @IntDef(prefix={"IDENTIFIER_TYPE_"}, value={android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_INVALID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_RDS_PI, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_NAME, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SCID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_MODULATION, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT}) @IntRange(from=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.IdentifierType {
+  @IntDef(prefix={"IDENTIFIER_TYPE_"}, value={android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_INVALID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_RDS_PI, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_NAME, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SCID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_MODULATION, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_LOCATION}) @IntRange(from=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.IdentifierType {
   }
 
   @Deprecated @IntDef(prefix={"PROGRAM_TYPE_"}, value={android.hardware.radio.ProgramSelector.PROGRAM_TYPE_INVALID, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DAB, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DRMO, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_SXM}) @IntRange(from=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.ProgramType {
@@ -5693,7 +5707,9 @@
     field public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8; // 0x8
     field public static final int CONFIG_DAB_FM_LINKING = 7; // 0x7
     field public static final int CONFIG_DAB_FM_SOFT_LINKING = 9; // 0x9
-    field public static final int CONFIG_FORCE_ANALOG = 2; // 0x2
+    field @Deprecated public static final int CONFIG_FORCE_ANALOG = 2; // 0x2
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int CONFIG_FORCE_ANALOG_AM = 11; // 0xb
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int CONFIG_FORCE_ANALOG_FM = 10; // 0xa
     field public static final int CONFIG_FORCE_DIGITAL = 3; // 0x3
     field public static final int CONFIG_FORCE_MONO = 1; // 0x1
     field public static final int CONFIG_RDS_AF = 4; // 0x4
@@ -5821,8 +5837,11 @@
     method @Deprecated public int getSubChannel();
     method @NonNull public java.util.Map<java.lang.String,java.lang.String> getVendorInfo();
     method @Deprecated public boolean isDigital();
+    method @FlaggedApi("android.hardware.radio.hd_radio_improved") public boolean isHdAudioAvailable();
+    method @FlaggedApi("android.hardware.radio.hd_radio_improved") public boolean isHdSisAvailable();
     method public boolean isLive();
     method public boolean isMuted();
+    method @FlaggedApi("android.hardware.radio.hd_radio_improved") public boolean isSignalAcquired();
     method public boolean isStereo();
     method public boolean isTrafficAnnouncementActive();
     method public boolean isTrafficProgram();
@@ -5838,6 +5857,7 @@
     method public android.hardware.radio.RadioMetadata.Clock getClock(String);
     method public int getInt(String);
     method public String getString(String);
+    method @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull public String[] getStringArray(@NonNull String);
     method public java.util.Set<java.lang.String> keySet();
     method public int size();
     method public void writeToParcel(android.os.Parcel, int);
@@ -5846,6 +5866,9 @@
     field public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
     field public static final String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST";
     field public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_COMMENT_ACTUAL_TEXT = "android.hardware.radio.metadata.COMMENT_ACTUAL_TEXT";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_COMMENT_SHORT_DESCRIPTION = "android.hardware.radio.metadata.COMMENT_SHORT_DESCRIPTION";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_COMMERCIAL = "android.hardware.radio.metadata.COMMERCIAL";
     field public static final String METADATA_KEY_DAB_COMPONENT_NAME = "android.hardware.radio.metadata.DAB_COMPONENT_NAME";
     field public static final String METADATA_KEY_DAB_COMPONENT_NAME_SHORT = "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT";
     field public static final String METADATA_KEY_DAB_ENSEMBLE_NAME = "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME";
@@ -5853,6 +5876,9 @@
     field public static final String METADATA_KEY_DAB_SERVICE_NAME = "android.hardware.radio.metadata.DAB_SERVICE_NAME";
     field public static final String METADATA_KEY_DAB_SERVICE_NAME_SHORT = "android.hardware.radio.metadata.DAB_SERVICE_NAME_SHORT";
     field public static final String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_HD_STATION_NAME_LONG = "android.hardware.radio.metadata.HD_STATION_NAME_LONG";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_HD_STATION_NAME_SHORT = "android.hardware.radio.metadata.HD_STATION_NAME_SHORT";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_HD_SUBCHANNELS_AVAILABLE = "android.hardware.radio.metadata.HD_SUBCHANNELS_AVAILABLE";
     field public static final String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON";
     field public static final String METADATA_KEY_PROGRAM_NAME = "android.hardware.radio.metadata.PROGRAM_NAME";
     field public static final String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY";
@@ -5861,6 +5887,7 @@
     field public static final String METADATA_KEY_RDS_PTY = "android.hardware.radio.metadata.RDS_PTY";
     field public static final String METADATA_KEY_RDS_RT = "android.hardware.radio.metadata.RDS_RT";
     field public static final String METADATA_KEY_TITLE = "android.hardware.radio.metadata.TITLE";
+    field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final String METADATA_KEY_UFIDS = "android.hardware.radio.metadata.UFIDS";
   }
 
   public static final class RadioMetadata.Builder {
@@ -5871,6 +5898,7 @@
     method public android.hardware.radio.RadioMetadata.Builder putClock(String, long, int);
     method public android.hardware.radio.RadioMetadata.Builder putInt(String, int);
     method public android.hardware.radio.RadioMetadata.Builder putString(String, String);
+    method @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull public android.hardware.radio.RadioMetadata.Builder putStringArray(@NonNull String, @NonNull String[]);
   }
 
   public static final class RadioMetadata.Clock implements android.os.Parcelable {
@@ -12688,6 +12716,7 @@
   public static final class HotwordDetectionService.Callback {
     method public void onDetected(@NonNull android.service.voice.HotwordDetectedResult);
     method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
+    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public void onTrainingData(@NonNull android.service.voice.HotwordTrainingData);
   }
 
   public final class HotwordDetectionServiceFailure implements android.os.Parcelable {
@@ -12703,6 +12732,8 @@
     field public static final int ERROR_CODE_DETECT_TIMEOUT = 4; // 0x4
     field public static final int ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION = 5; // 0x5
     field public static final int ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE = 6; // 0x6
+    field @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final int ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED = 8; // 0x8
+    field @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final int ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION = 9; // 0x9
     field public static final int ERROR_CODE_REMOTE_EXCEPTION = 7; // 0x7
     field public static final int ERROR_CODE_UNKNOWN = 0; // 0x0
   }
@@ -12724,6 +12755,7 @@
     method public void onRecognitionPaused();
     method public void onRecognitionResumed();
     method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
+    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public default void onTrainingData(@NonNull android.service.voice.HotwordTrainingData);
     method public default void onUnknownFailure(@NonNull String);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 779777c..83b6880 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -872,7 +872,7 @@
     ctor public AttributionSource(int, @Nullable String, @Nullable String);
     ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
     ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
-    ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
+    ctor @FlaggedApi("android.permission.flags.attribution_source_constructor") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
     ctor @FlaggedApi("android.permission.flags.device_aware_permission_apis") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource);
     method public void enforceCallingPid();
   }
@@ -2148,7 +2148,9 @@
   }
 
   public final class BugreportParams {
+    field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 4; // 0x4
     field @FlaggedApi("android.os.bugreport_mode_max_value") public static final int BUGREPORT_MODE_MAX_VALUE = 7; // 0x7
+    field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
   }
 
   public class Build {
@@ -2287,7 +2289,7 @@
   public final class PowerManager {
     method public boolean areAutoPowerSaveModesEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
-    method public boolean isBatterySaverSupported();
+    method @FlaggedApi("android.os.battery_saver_supported_check_api") public boolean isBatterySaverSupported();
     field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
     field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000
   }
@@ -2355,6 +2357,7 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getAliveUsers();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getBootUser();
+    method @FlaggedApi("android.multiuser.support_communal_profile") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getCommunalProfile();
     method public int getMainDisplayIdAssignedToUser();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
@@ -3047,6 +3050,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
     method @NonNull public final java.util.List<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> listModuleProperties();
+    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT) public final void resetHotwordTrainingDataEgressCountForTest();
     method public final void setTestModuleForAlwaysOnHotwordDetectorEnabled(boolean);
   }
 
@@ -3490,10 +3494,6 @@
     method public boolean isSystemGroup();
   }
 
-  public abstract class LayoutInflater {
-    method public void setPrecompiledLayoutsEnabledForTesting(boolean);
-  }
-
   public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
     method public int getDisplayId();
     method public void setActionButton(int);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ecbc9b1..9a19d8e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8638,8 +8638,8 @@
                 }
             }
 
-            SyncNotedAppOp syncOp = mService.noteProxyOperation(op, attributionSource.asState(),
-                    collectionMode == COLLECT_ASYNC, message,
+            SyncNotedAppOp syncOp = mService.noteProxyOperationWithState(op,
+                    attributionSource.asState(), collectionMode == COLLECT_ASYNC, message,
                     shouldCollectMessage, skipProxyOperation);
 
             if (syncOp.getOpMode() == MODE_ALLOWED) {
@@ -9110,7 +9110,7 @@
                 }
             }
 
-            SyncNotedAppOp syncOp = mService.startProxyOperation(clientId, op,
+            SyncNotedAppOp syncOp = mService.startProxyOperationWithState(clientId, op,
                     attributionSource.asState(), false, collectionMode == COLLECT_ASYNC, message,
                     shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
                     proxiedAttributionFlags, attributionChainId);
@@ -9229,8 +9229,8 @@
     public void finishProxyOp(@NonNull IBinder clientId, @NonNull String op,
             @NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
         try {
-            mService.finishProxyOperation(clientId, strOpToOp(op), attributionSource.asState(),
-                    skipProxyOperation);
+            mService.finishProxyOperationWithState(
+                    clientId, strOpToOp(op), attributionSource.asState(), skipProxyOperation);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 21ed098..fd308ce 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -48,6 +48,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApkChecksum;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackage;
 import android.content.pm.ChangedPackages;
 import android.content.pm.Checksum;
 import android.content.pm.ComponentInfo;
@@ -3936,6 +3937,19 @@
     }
 
     @Override
+    public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) {
+        try {
+            var parcel = mPM.getArchivedPackage(packageName, mContext.getUserId());
+            if (parcel == null) {
+                return null;
+            }
+            return new ArchivedPackage(parcel);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public boolean canUserUninstall(String packageName, UserHandle user) {
         try {
             return mPM.getBlockUninstallForUser(packageName, user.getIdentifier());
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 37111e9..c8317c8 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -27,7 +27,15 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -608,6 +616,103 @@
                 }
             };
 
+    /**
+     * Write to a protocol buffer output stream. Protocol buffer message definition at {@link
+     * android.app.ApplicationStartInfoProto}
+     *
+     * @param proto Stream to write the ApplicationStartInfo object to.
+     * @param fieldId Field Id of the ApplicationStartInfo as defined in the parent message
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) throws IOException {
+        final long token = proto.start(fieldId);
+        proto.write(ApplicationStartInfoProto.PID, mPid);
+        proto.write(ApplicationStartInfoProto.REAL_UID, mRealUid);
+        proto.write(ApplicationStartInfoProto.PACKAGE_UID, mPackageUid);
+        proto.write(ApplicationStartInfoProto.DEFINING_UID, mDefiningUid);
+        proto.write(ApplicationStartInfoProto.PROCESS_NAME, mProcessName);
+        proto.write(ApplicationStartInfoProto.STARTUP_STATE, mStartupState);
+        proto.write(ApplicationStartInfoProto.REASON, mReason);
+        if (mStartupTimestampsNs != null && mStartupTimestampsNs.size() > 0) {
+            ByteArrayOutputStream timestampsBytes = new ByteArrayOutputStream();
+            ObjectOutputStream timestampsOut = new ObjectOutputStream(timestampsBytes);
+            timestampsOut.writeObject(mStartupTimestampsNs);
+            proto.write(ApplicationStartInfoProto.STARTUP_TIMESTAMPS,
+                    timestampsBytes.toByteArray());
+        }
+        proto.write(ApplicationStartInfoProto.START_TYPE, mStartType);
+        if (mStartIntent != null) {
+            Parcel parcel = Parcel.obtain();
+            mStartIntent.writeToParcel(parcel, 0);
+            proto.write(ApplicationStartInfoProto.START_INTENT, parcel.marshall());
+            parcel.recycle();
+        }
+        proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode);
+        proto.end(token);
+    }
+
+    /**
+     * Read from a protocol buffer input stream. Protocol buffer message definition at {@link
+     * android.app.ApplicationStartInfoProto}
+     *
+     * @param proto Stream to read the ApplicationStartInfo object from.
+     * @param fieldId Field Id of the ApplicationStartInfo as defined in the parent message
+     * @hide
+     */
+    public void readFromProto(ProtoInputStream proto, long fieldId)
+            throws IOException, WireTypeMismatchException, ClassNotFoundException {
+        final long token = proto.start(fieldId);
+        while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (proto.getFieldNumber()) {
+                case (int) ApplicationStartInfoProto.PID:
+                    mPid = proto.readInt(ApplicationStartInfoProto.PID);
+                    break;
+                case (int) ApplicationStartInfoProto.REAL_UID:
+                    mRealUid = proto.readInt(ApplicationStartInfoProto.REAL_UID);
+                    break;
+                case (int) ApplicationStartInfoProto.PACKAGE_UID:
+                    mPackageUid = proto.readInt(ApplicationStartInfoProto.PACKAGE_UID);
+                    break;
+                case (int) ApplicationStartInfoProto.DEFINING_UID:
+                    mDefiningUid = proto.readInt(ApplicationStartInfoProto.DEFINING_UID);
+                    break;
+                case (int) ApplicationStartInfoProto.PROCESS_NAME:
+                    mProcessName = intern(proto.readString(ApplicationStartInfoProto.PROCESS_NAME));
+                    break;
+                case (int) ApplicationStartInfoProto.STARTUP_STATE:
+                    mStartupState = proto.readInt(ApplicationStartInfoProto.STARTUP_STATE);
+                    break;
+                case (int) ApplicationStartInfoProto.REASON:
+                    mReason = proto.readInt(ApplicationStartInfoProto.REASON);
+                    break;
+                case (int) ApplicationStartInfoProto.STARTUP_TIMESTAMPS:
+                    ByteArrayInputStream timestampsBytes = new ByteArrayInputStream(proto.readBytes(
+                            ApplicationStartInfoProto.STARTUP_TIMESTAMPS));
+                    ObjectInputStream timestampsIn = new ObjectInputStream(timestampsBytes);
+                    mStartupTimestampsNs = (ArrayMap<Integer, Long>) timestampsIn.readObject();
+                    break;
+                case (int) ApplicationStartInfoProto.START_TYPE:
+                    mStartType = proto.readInt(ApplicationStartInfoProto.START_TYPE);
+                    break;
+                case (int) ApplicationStartInfoProto.START_INTENT:
+                    byte[] startIntentBytes = proto.readBytes(
+                        ApplicationStartInfoProto.START_INTENT);
+                    if (startIntentBytes.length > 0) {
+                        Parcel parcel = Parcel.obtain();
+                        parcel.unmarshall(startIntentBytes, 0, startIntentBytes.length);
+                        parcel.setDataPosition(0);
+                        mStartIntent = Intent.CREATOR.createFromParcel(parcel);
+                        parcel.recycle();
+                    }
+                    break;
+                case (int) ApplicationStartInfoProto.LAUNCH_MODE:
+                    mLaunchMode = proto.readInt(ApplicationStartInfoProto.LAUNCH_MODE);
+                    break;
+            }
+        }
+        proto.end(token);
+    }
+
     /** @hide */
     public void dump(@NonNull PrintWriter pw, @Nullable String prefix, @Nullable String seqSuffix,
             @NonNull SimpleDateFormat sdf) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index e2082f7..180725e 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -706,8 +706,12 @@
     }
 
     private boolean allowOverlappingTransitions() {
-        return mIsReturning ? getWindow().getAllowReturnTransitionOverlap()
-                : getWindow().getAllowEnterTransitionOverlap();
+        final Window window = getWindow();
+        if (window == null) {
+            return false;
+        }
+        return mIsReturning ? window.getAllowReturnTransitionOverlap()
+                : window.getAllowEnterTransitionOverlap();
     }
 
     private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 2d55403..545ba8e 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -26,6 +26,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.PasswordMetrics;
@@ -736,7 +737,7 @@
      * @see #isKeyguardLocked()
      */
     public boolean isDeviceLocked() {
-        return isDeviceLocked(mContext.getUserId());
+        return isDeviceLocked(mContext.getUserId(), mContext.getDeviceId());
     }
 
     /**
@@ -746,8 +747,17 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isDeviceLocked(int userId) {
+        return isDeviceLocked(userId, mContext.getDeviceId());
+    }
+
+    /**
+     * Per-user per-device version of {@link #isDeviceLocked()}.
+     *
+     * @hide
+     */
+    public boolean isDeviceLocked(@UserIdInt int userId, int deviceId) {
         try {
-            return mTrustManager.isDeviceLocked(userId, mContext.getAssociatedDisplayId());
+            return mTrustManager.isDeviceLocked(userId, deviceId);
         } catch (RemoteException e) {
             return false;
         }
@@ -769,7 +779,7 @@
      * @see #isKeyguardSecure()
      */
     public boolean isDeviceSecure() {
-        return isDeviceSecure(mContext.getUserId());
+        return isDeviceSecure(mContext.getUserId(), mContext.getDeviceId());
     }
 
     /**
@@ -779,8 +789,17 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isDeviceSecure(int userId) {
+        return isDeviceSecure(userId, mContext.getDeviceId());
+    }
+
+    /**
+     * Per-user per-device version of {@link #isDeviceSecure()}.
+     *
+     * @hide
+     */
+    public boolean isDeviceSecure(@UserIdInt int userId, int deviceId) {
         try {
-            return mTrustManager.isDeviceSecure(userId, mContext.getAssociatedDisplayId());
+            return mTrustManager.isDeviceSecure(userId, deviceId);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 94e1292..bbc843b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -374,7 +374,7 @@
      * that you take care of task management as described in the
      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
      * Stack</a> document.  In particular, make sure to read the
-     * <a href="{@docRoot}/training/notify-user/navigation">Start
+     * <a href="{@docRoot}training/notify-user/navigation">Start
      * an Activity from a Notification</a> page for the correct ways to launch an application from a
      * notification.
      */
@@ -5606,8 +5606,10 @@
             contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
             // Use different highlighted colors for conversations' unread count
             if (p.mHighlightExpander) {
-                pillColor = Colors.flattenAlpha(getColors(p).getTertiaryAccentColor(), bgColor);
-                textColor = Colors.flattenAlpha(getColors(p).getOnAccentTextColor(), pillColor);
+                pillColor = Colors.flattenAlpha(
+                        getColors(p).getTertiaryFixedDimAccentColor(), bgColor);
+                textColor = Colors.flattenAlpha(
+                        getColors(p).getOnTertiaryAccentTextColor(), pillColor);
             }
             contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
@@ -12833,7 +12835,10 @@
         private int mPrimaryAccentColor = COLOR_INVALID;
         private int mSecondaryAccentColor = COLOR_INVALID;
         private int mTertiaryAccentColor = COLOR_INVALID;
-        private int mOnAccentTextColor = COLOR_INVALID;
+        private int mOnTertiaryAccentTextColor = COLOR_INVALID;
+        private int mTertiaryFixedDimAccentColor = COLOR_INVALID;
+        private int mOnTertiaryFixedAccentTextColor = COLOR_INVALID;
+
         private int mErrorColor = COLOR_INVALID;
         private int mContrastColor = COLOR_INVALID;
         private int mRippleAlpha = 0x33;
@@ -12891,7 +12896,7 @@
 
             if (isColorized) {
                 if (rawColor == COLOR_DEFAULT) {
-                    int[] attrs = {R.attr.colorAccentSecondary};
+                    int[] attrs = {R.attr.materialColorSecondary};
                     try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
                         mBackgroundColor = getColor(ta, 0, Color.WHITE);
                     }
@@ -12908,18 +12913,22 @@
                 mPrimaryAccentColor = mPrimaryTextColor;
                 mSecondaryAccentColor = mSecondaryTextColor;
                 mTertiaryAccentColor = flattenAlpha(mPrimaryTextColor, mBackgroundColor);
-                mOnAccentTextColor = mBackgroundColor;
+                mOnTertiaryAccentTextColor = mBackgroundColor;
+                mTertiaryFixedDimAccentColor = mTertiaryAccentColor;
+                mOnTertiaryFixedAccentTextColor = mOnTertiaryAccentTextColor;
                 mErrorColor = mPrimaryTextColor;
                 mRippleAlpha = 0x33;
             } else {
                 int[] attrs = {
-                        R.attr.colorSurface,
-                        R.attr.textColorPrimary,
-                        R.attr.textColorSecondary,
-                        R.attr.colorAccent,
-                        R.attr.colorAccentSecondary,
-                        R.attr.colorAccentTertiary,
-                        R.attr.textColorOnAccent,
+                        R.attr.materialColorSurfaceContainerHigh,
+                        R.attr.materialColorOnSurface,
+                        R.attr.materialColorOnSurfaceVariant,
+                        R.attr.materialColorPrimary,
+                        R.attr.materialColorSecondary,
+                        R.attr.materialColorTertiary,
+                        R.attr.materialColorOnTertiary,
+                        R.attr.materialColorTertiaryFixedDim,
+                        R.attr.materialColorOnTertiaryFixed,
                         R.attr.colorError,
                         R.attr.colorControlHighlight
                 };
@@ -12930,9 +12939,11 @@
                     mPrimaryAccentColor = getColor(ta, 3, COLOR_INVALID);
                     mSecondaryAccentColor = getColor(ta, 4, COLOR_INVALID);
                     mTertiaryAccentColor = getColor(ta, 5, COLOR_INVALID);
-                    mOnAccentTextColor = getColor(ta, 6, COLOR_INVALID);
-                    mErrorColor = getColor(ta, 7, COLOR_INVALID);
-                    mRippleAlpha = Color.alpha(getColor(ta, 8, 0x33ffffff));
+                    mOnTertiaryAccentTextColor = getColor(ta, 6, COLOR_INVALID);
+                    mTertiaryFixedDimAccentColor = getColor(ta, 7, COLOR_INVALID);
+                    mOnTertiaryFixedAccentTextColor = getColor(ta, 8, COLOR_INVALID);
+                    mErrorColor = getColor(ta, 9, COLOR_INVALID);
+                    mRippleAlpha = Color.alpha(getColor(ta, 10, 0x33ffffff));
                 }
                 mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor,
                         mBackgroundColor, nightMode);
@@ -12955,11 +12966,19 @@
                 if (mTertiaryAccentColor == COLOR_INVALID) {
                     mTertiaryAccentColor = mContrastColor;
                 }
-                if (mOnAccentTextColor == COLOR_INVALID) {
-                    mOnAccentTextColor = ColorUtils.setAlphaComponent(
+                if (mOnTertiaryAccentTextColor == COLOR_INVALID) {
+                    mOnTertiaryAccentTextColor = ColorUtils.setAlphaComponent(
                             ContrastColorUtil.resolvePrimaryColor(
                                     ctx, mTertiaryAccentColor, nightMode), 0xFF);
                 }
+                if (mTertiaryFixedDimAccentColor == COLOR_INVALID) {
+                    mTertiaryFixedDimAccentColor = mContrastColor;
+                }
+                if (mOnTertiaryFixedAccentTextColor == COLOR_INVALID) {
+                    mOnTertiaryFixedAccentTextColor = ColorUtils.setAlphaComponent(
+                            ContrastColorUtil.resolvePrimaryColor(
+                                    ctx, mTertiaryFixedDimAccentColor, nightMode), 0xFF);
+                }
                 if (mErrorColor == COLOR_INVALID) {
                     mErrorColor = mPrimaryTextColor;
                 }
@@ -13029,8 +13048,18 @@
         }
 
         /** @return the theme's text color to be used on the tertiary accent color. */
-        public @ColorInt int getOnAccentTextColor() {
-            return mOnAccentTextColor;
+        public @ColorInt int getOnTertiaryAccentTextColor() {
+            return mOnTertiaryAccentTextColor;
+        }
+
+        /** @return the theme's tertiary fixed dim accent color for colored UI elements. */
+        public @ColorInt int getTertiaryFixedDimAccentColor() {
+            return mTertiaryFixedDimAccentColor;
+        }
+
+        /** @return the theme's text color to be used on the tertiary fixed accent color. */
+        public @ColorInt int getOnTertiaryFixedAccentTextColor() {
+            return mOnTertiaryFixedAccentTextColor;
         }
 
         /**
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index e2aee83..7418c06 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -20,46 +20,31 @@
 
 import static java.util.Objects.requireNonNull;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.Process;
-import android.util.ArrayMap;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.function.IntConsumer;
-
 /**
  * Singleton controller to manage listeners to individual {@link ClientTransaction}.
  *
- * TODO(b/260873529) make as TestApi to allow CTS.
  * @hide
  */
 public class ClientTransactionListenerController {
 
     private static ClientTransactionListenerController sController;
 
-    private final Object mLock = new Object();
-
-    /**
-     * Mapping from client registered listener for display change to the corresponding
-     * {@link Executor} to invoke the listener on.
-     * @see #registerDisplayChangeListener(IntConsumer, Executor)
-     */
-    @GuardedBy("mLock")
-    private final ArrayMap<IntConsumer, Executor> mDisplayChangeListeners = new ArrayMap<>();
-
-    private final ArrayList<IntConsumer> mTmpDisplayChangeListeners = new ArrayList<>();
+    private final DisplayManagerGlobal mDisplayManager;
 
     /** Gets the singleton controller. */
     @NonNull
     public static ClientTransactionListenerController getInstance() {
         synchronized (ClientTransactionListenerController.class) {
             if (sController == null) {
-                sController = new ClientTransactionListenerController();
+                sController = new ClientTransactionListenerController(
+                        DisplayManagerGlobal.getInstance());
             }
             return sController;
         }
@@ -68,45 +53,13 @@
     /** Creates a new instance for test only. */
     @VisibleForTesting
     @NonNull
-    public static ClientTransactionListenerController createInstanceForTesting() {
-        return new ClientTransactionListenerController();
+    public static ClientTransactionListenerController createInstanceForTesting(
+            @NonNull DisplayManagerGlobal displayManager) {
+        return new ClientTransactionListenerController(displayManager);
     }
 
-    private ClientTransactionListenerController() {}
-
-    /**
-     * Registers a new listener for display change. It will be invoked when receives a
-     * {@link ClientTransaction} that is updating display-related window configuration, such as
-     * bounds and rotation.
-     *
-     * WHen triggered, the listener will be invoked with the logical display id that was changed.
-     *
-     * @param listener the listener to invoke when receives a transaction with Display change.
-     * @param executor the executor on which callback method will be invoked.
-     */
-    public void registerDisplayChangeListener(@NonNull IntConsumer listener,
-            @NonNull @CallbackExecutor Executor executor) {
-        if (!isSyncWindowConfigUpdateFlagEnabled()) {
-            return;
-        }
-        requireNonNull(listener);
-        requireNonNull(executor);
-        synchronized (mLock) {
-            mDisplayChangeListeners.put(listener, executor);
-        }
-    }
-
-    /**
-     * Unregisters the listener for display change that was previously registered through
-     * {@link #registerDisplayChangeListener}.
-     */
-    public void unregisterDisplayChangeListener(@NonNull IntConsumer listener) {
-        if (!isSyncWindowConfigUpdateFlagEnabled()) {
-            return;
-        }
-        synchronized (mLock) {
-            mDisplayChangeListeners.remove(listener);
-        }
+    private ClientTransactionListenerController(@NonNull DisplayManagerGlobal displayManager) {
+        mDisplayManager = requireNonNull(displayManager);
     }
 
     /**
@@ -117,24 +70,14 @@
         if (!isSyncWindowConfigUpdateFlagEnabled()) {
             return;
         }
-        synchronized (mLock) {
-            // Make a copy of the list to avoid listener removal during callback.
-            mTmpDisplayChangeListeners.addAll(mDisplayChangeListeners.keySet());
-            final int num = mTmpDisplayChangeListeners.size();
-            try {
-                for (int i = 0; i < num; i++) {
-                    final IntConsumer listener = mTmpDisplayChangeListeners.get(i);
-                    final Executor executor = mDisplayChangeListeners.get(listener);
-                    executor.execute(() -> listener.accept(displayId));
-                }
-            } finally {
-                mTmpDisplayChangeListeners.clear();
-            }
+        if (ActivityThread.isSystem()) {
+            // Not enable for system server.
+            return;
         }
+        mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
     }
 
     /** Whether {@link #syncWindowConfigUpdateFlag} feature flag is enabled. */
-    @VisibleForTesting
     public boolean isSyncWindowConfigUpdateFlagEnabled() {
         // Can't read flag from isolated process.
         return !Process.isIsolated() && syncWindowConfigUpdateFlag();
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
index 3c66a15..f6f65c7 100644
--- a/core/java/android/app/smartspace/SmartspaceTarget.java
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -16,10 +16,12 @@
 package android.app.smartspace;
 
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.smartspace.flags.Flags;
 import android.app.smartspace.uitemplatedata.BaseTemplateData;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -27,6 +29,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.widget.RemoteViews;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -41,7 +44,8 @@
  * {@link SmartspaceAction} as their type because they can have associated actions.
  *
  * <p><b>NOTE: </b>
- * If {@link mWidget} is set, it should be preferred over all other properties.
+ * If either {@link mRemoteViews} or {@link mWidget} is set, it should be preferred over all
+ * other properties. (An exception is thrown if both are set.)
  * Else, if {@link mSliceUri} is set, it should be preferred over all other data properties.
  * Otherwise, the instance should be treated as a data object.
  *
@@ -133,6 +137,9 @@
     private final AppWidgetProviderInfo mWidget;
 
     @Nullable
+    private final RemoteViews mRemoteViews;
+
+    @Nullable
     private final BaseTemplateData mTemplateData;
 
     public static final int FEATURE_UNDEFINED = 0;
@@ -288,6 +295,7 @@
         this.mSliceUri = in.readTypedObject(Uri.CREATOR);
         this.mWidget = in.readTypedObject(AppWidgetProviderInfo.CREATOR);
         this.mTemplateData = in.readParcelable(/* loader= */null, BaseTemplateData.class);
+        this.mRemoteViews = in.readTypedObject(RemoteViews.CREATOR);
     }
 
     private SmartspaceTarget(String smartspaceTargetId,
@@ -298,7 +306,7 @@
             boolean shouldShowExpanded, String sourceNotificationKey,
             ComponentName componentName, UserHandle userHandle,
             String associatedSmartspaceTargetId, Uri sliceUri,
-            AppWidgetProviderInfo widget, BaseTemplateData templateData) {
+            AppWidgetProviderInfo widget, BaseTemplateData templateData, RemoteViews remoteViews) {
         mSmartspaceTargetId = smartspaceTargetId;
         mHeaderAction = headerAction;
         mBaseAction = baseAction;
@@ -317,6 +325,7 @@
         mSliceUri = sliceUri;
         mWidget = widget;
         mTemplateData = templateData;
+        mRemoteViews = remoteViews;
     }
 
     /**
@@ -461,6 +470,15 @@
     }
 
     /**
+     * Returns the {@link RemoteViews} to show over the target.
+     */
+    @FlaggedApi(Flags.FLAG_REMOTE_VIEWS)
+    @Nullable
+    public RemoteViews getRemoteViews() {
+        return mRemoteViews;
+    }
+
+    /**
      * @see Parcelable.Creator
      */
     @NonNull
@@ -496,6 +514,7 @@
         dest.writeTypedObject(this.mSliceUri, flags);
         dest.writeTypedObject(this.mWidget, flags);
         dest.writeParcelable(this.mTemplateData, flags);
+        dest.writeTypedObject(this.mRemoteViews, flags);
     }
 
     @Override
@@ -524,6 +543,7 @@
                 + ", mSliceUri=" + mSliceUri
                 + ", mWidget=" + mWidget
                 + ", mTemplateData=" + mTemplateData
+                + ", mRemoteViews=" + mRemoteViews
                 + '}';
     }
 
@@ -550,7 +570,8 @@
                 that.mAssociatedSmartspaceTargetId)
                 && Objects.equals(mSliceUri, that.mSliceUri)
                 && Objects.equals(mWidget, that.mWidget)
-                && Objects.equals(mTemplateData, that.mTemplateData);
+                && Objects.equals(mTemplateData, that.mTemplateData)
+                && Objects.equals(mRemoteViews, that.mRemoteViews);
     }
 
     @Override
@@ -558,7 +579,7 @@
         return Objects.hash(mSmartspaceTargetId, mHeaderAction, mBaseAction, mCreationTimeMillis,
                 mExpiryTimeMillis, mScore, mActionChips, mIconGrid, mFeatureType, mSensitive,
                 mShouldShowExpanded, mSourceNotificationKey, mComponentName, mUserHandle,
-                mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData);
+                mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData, mRemoteViews);
     }
 
     /**
@@ -588,6 +609,8 @@
         private AppWidgetProviderInfo mWidget;
         private BaseTemplateData mTemplateData;
 
+        private RemoteViews mRemoteViews;
+
         /**
          * A builder for {@link SmartspaceTarget}.
          *
@@ -727,9 +750,15 @@
          *
          * <p><b>NOTE: </b> If {@link mWidget} is set, all other @Nullable params should be
          * ignored.
+         *
+         * @throws An {@link IllegalStateException} is thrown if {@link mRemoteViews} is set.
          */
         @NonNull
         public Builder setWidget(@NonNull AppWidgetProviderInfo widget) {
+            if (mRemoteViews != null) {
+                throw new IllegalStateException(
+                        "Widget providers and RemoteViews cannot be used at the same time.");
+            }
             this.mWidget = widget;
             return this;
         }
@@ -745,6 +774,25 @@
         }
 
         /**
+         * Sets the {@link RemoteViews}.
+         *
+         * <p><b>NOTE: </b> If {@link RemoteViews} is set, all other @Nullable params should be
+         * ignored.
+         *
+         * @throws An {@link IllegalStateException} is thrown if {@link mWidget} is set.
+         */
+        @FlaggedApi(Flags.FLAG_REMOTE_VIEWS)
+        @NonNull
+        public Builder setRemoteViews(@NonNull RemoteViews remoteViews) {
+            if (mWidget != null) {
+                throw new IllegalStateException(
+                        "Widget providers and RemoteViews cannot be used at the same time.");
+            }
+            mRemoteViews = remoteViews;
+            return this;
+        }
+
+        /**
          * Builds a new {@link SmartspaceTarget}.
          *
          * @throws IllegalStateException when non null fields are set as null.
@@ -760,7 +808,7 @@
                     mHeaderAction, mBaseAction, mCreationTimeMillis, mExpiryTimeMillis, mScore,
                     mActionChips, mIconGrid, mFeatureType, mSensitive, mShouldShowExpanded,
                     mSourceNotificationKey, mComponentName, mUserHandle,
-                    mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData);
+                    mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData, mRemoteViews);
         }
     }
 }
diff --git a/core/java/android/app/smartspace/flags.aconfig b/core/java/android/app/smartspace/flags.aconfig
new file mode 100644
index 0000000..12af888
--- /dev/null
+++ b/core/java/android/app/smartspace/flags.aconfig
@@ -0,0 +1,15 @@
+package: "android.app.smartspace.flags"
+
+flag {
+  name: "remote_views"
+  namespace: "sysui_integrations"
+  description: "Flag to enable the FlaggedApi to include RemoteViews in SmartspaceTarget"
+  bug: "300157758"
+}
+
+flag {
+  name: "access_smartspace"
+  namespace: "sysui_integrations"
+  description: "Flag to enable the ACCESS_SMARTSPACE check in SmartspaceManagerService"
+  bug: "297207196"
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index a5e5135..70c25ea 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -34,8 +34,8 @@
     void unregisterTrustListener(in ITrustListener trustListener);
     void reportKeyguardShowingChanged();
     void setDeviceLockedForUser(int userId, boolean locked);
-    boolean isDeviceLocked(int userId, int displayId);
-    boolean isDeviceSecure(int userId, int displayId);
+    boolean isDeviceLocked(int userId, int deviceId);
+    boolean isDeviceSecure(int userId, int deviceId);
     @EnforcePermission("TRUST_LISTENER")
     boolean isTrustUsuallyManaged(int userId);
     void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/core/java/android/app/usage/ParcelableUsageEventList.aidl
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
rename to core/java/android/app/usage/ParcelableUsageEventList.aidl
index 6d7c576..1652996 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/core/java/android/app/usage/ParcelableUsageEventList.aidl
@@ -5,7 +5,7 @@
  * 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,
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package android.app.usage;
 
-enum class QSTileLifecycle {
-    ALIVE,
-    DEAD,
-}
+parcelable ParcelableUsageEventList;
diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java
new file mode 100644
index 0000000..016d97f
--- /dev/null
+++ b/core/java/android/app/usage/ParcelableUsageEventList.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2023 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.app.usage;
+
+import android.annotation.NonNull;
+import android.app.usage.UsageEvents.Event;
+import android.content.res.Configuration;
+import android.os.BadParcelableException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is a copied version of BaseParceledListSlice with specific
+ * {@link UsageEvents.Event} instance that used to transfer the large
+ * list of {@link UsageEvents.Event} objects across an IPC. Splits
+ * into multiple transactions if needed.
+ *
+ * @see BasedParceledListSlice
+ *
+ * @hide
+ */
+public final class ParcelableUsageEventList implements Parcelable {
+    private static final String TAG = "ParcelableUsageEventList";
+    private static final boolean DEBUG = false;
+    private static final boolean DEBUG_ALL = false;
+
+    private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
+
+    private List<Event> mList;
+
+    public ParcelableUsageEventList(List<Event> list) {
+        mList = list;
+    }
+
+    private ParcelableUsageEventList(Parcel in) {
+        final int N = in.readInt();
+        mList = new ArrayList<>();
+        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
+        if (N <= 0) {
+            return;
+        }
+
+        int i = 0;
+        while (i < N) {
+            if (in.readInt() == 0) {
+                break;
+            }
+            mList.add(readEventFromParcel(in));
+            if (DEBUG_ALL) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size() - 1));
+            i++;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Read " + mList.size() + " inline UsageEvents"
+                    + ", total N=" + N + " UsageEvents");
+        }
+        if (i >= N) {
+            return;
+        }
+        final IBinder retriever = in.readStrongBinder();
+        while (i < N) {
+            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
+            Parcel data = Parcel.obtain();
+            Parcel reply = Parcel.obtain();
+            data.writeInt(i);
+            try {
+                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+                reply.readException();
+                int count = 0;
+                while (i < N && reply.readInt() != 0) {
+                    mList.add(readEventFromParcel(reply));
+                    if (DEBUG_ALL) {
+                        Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size() - 1));
+                    }
+                    i++;
+                    count++;
+                }
+                if (DEBUG) Log.d(TAG, "Read extra @" + count + " of " + N);
+            } catch (RemoteException e) {
+                throw new BadParcelableException(
+                    "Failure retrieving array; only received " + i + " of " + N, e);
+            } finally {
+                reply.recycle();
+                data.recycle();
+            }
+        }
+        if (DEBUG) Log.d(TAG, "Finish reading total " + i + " UsageEvents");
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        final int N = mList.size();
+        final int callFlags = flags;
+        dest.writeInt(N);
+        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
+        if (N > 0) {
+            int i = 0;
+            while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
+                dest.writeInt(1);
+
+                final Event event = mList.get(i);
+                writeEventToParcel(event, dest, callFlags);
+
+                if (DEBUG_ALL) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
+                i++;
+            }
+            if (i < N) {
+                dest.writeInt(0);
+                Binder retriever = new Binder() {
+                    @Override
+                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                            throws RemoteException {
+                        if (code != FIRST_CALL_TRANSACTION) {
+                            return super.onTransact(code, data, reply, flags);
+                        } else if (mList == null) {
+                            throw new IllegalArgumentException("Attempt to transfer null list, "
+                                + "did transfer finish?");
+                        }
+                        int i = data.readInt();
+
+                        if (DEBUG) {
+                            Log.d(TAG, "Writing more @" + i + " of " + N + " to "
+                                    + Binder.getCallingPid() + ", sender=" + this);
+                        }
+
+                        try {
+                            reply.writeNoException();
+                            int count = 0;
+                            while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+                                reply.writeInt(1);
+
+                                final Event event = mList.get(i);
+                                writeEventToParcel(event, reply, callFlags);
+
+                                if (DEBUG_ALL) {
+                                    Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+                                }
+                                i++;
+                                count++;
+                            }
+                            if (i < N) {
+                                if (DEBUG) {
+                                    Log.d(TAG, "Breaking @" + i + " of " + N
+                                            + "(count = " + count + ")");
+                                }
+                                reply.writeInt(0);
+                            } else {
+                                if (DEBUG) Log.d(TAG, "Transfer done, clearing mList reference");
+                                mList = null;
+                            }
+                        } catch (RuntimeException e) {
+                            if (DEBUG) Log.d(TAG, "Transfer failed, clearing mList reference");
+                            mList = null;
+                            throw e;
+                        }
+                        return true;
+                    }
+                };
+                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
+                dest.writeStrongBinder(retriever);
+            }
+        }
+    }
+
+    public List<Event> getList() {
+        return mList;
+    }
+
+    public static final Parcelable.Creator<ParcelableUsageEventList> CREATOR =
+            new Parcelable.Creator<ParcelableUsageEventList>() {
+                public ParcelableUsageEventList createFromParcel(Parcel in) {
+                    return new ParcelableUsageEventList(in);
+                }
+
+                @Override
+                public ParcelableUsageEventList[] newArray(int size) {
+                    return new ParcelableUsageEventList[size];
+                }
+            };
+
+    private Event readEventFromParcel(Parcel in) {
+        final Event event = new Event();
+        event.mPackage = in.readString();
+        event.mClass = in.readString();
+        event.mInstanceId = in.readInt();
+        event.mTaskRootPackage = in.readString();
+        event.mTaskRootClass = in.readString();
+        event.mEventType = in.readInt();
+        event.mTimeStamp = in.readLong();
+
+        // Fill out the event-dependant fields.
+        event.mConfiguration = null;
+        event.mShortcutId = null;
+        event.mAction = null;
+        event.mContentType = null;
+        event.mContentAnnotations = null;
+        event.mNotificationChannelId = null;
+        event.mLocusId = null;
+
+        switch (event.mEventType) {
+            case Event.CONFIGURATION_CHANGE -> {
+                event.mConfiguration = Configuration.CREATOR.createFromParcel(in);
+            }
+            case Event.SHORTCUT_INVOCATION -> event.mShortcutId = in.readString();
+            case Event.CHOOSER_ACTION -> {
+                event.mAction = in.readString();
+                event.mContentType = in.readString();
+                event.mContentAnnotations = in.readStringArray();
+            }
+            case Event.STANDBY_BUCKET_CHANGED -> event.mBucketAndReason = in.readInt();
+            case Event.NOTIFICATION_INTERRUPTION -> event.mNotificationChannelId = in.readString();
+            case Event.LOCUS_ID_SET -> event.mLocusId = in.readString();
+        }
+        event.mFlags = in.readInt();
+
+        return event;
+    }
+
+    private void writeEventToParcel(@NonNull Event event, @NonNull Parcel dest, int flags) {
+        dest.writeString(event.mPackage);
+        dest.writeString(event.mClass);
+        dest.writeInt(event.mInstanceId);
+        dest.writeString(event.mTaskRootPackage);
+        dest.writeString(event.mTaskRootClass);
+        dest.writeInt(event.mEventType);
+        dest.writeLong(event.mTimeStamp);
+
+        switch (event.mEventType) {
+            case Event.CONFIGURATION_CHANGE -> event.mConfiguration.writeToParcel(dest, flags);
+            case Event.SHORTCUT_INVOCATION -> dest.writeString(event.mShortcutId);
+            case Event.CHOOSER_ACTION -> {
+                dest.writeString(event.mAction);
+                dest.writeString(event.mContentType);
+                dest.writeStringArray(event.mContentAnnotations);
+            }
+            case Event.STANDBY_BUCKET_CHANGED -> dest.writeInt(event.mBucketAndReason);
+            case Event.NOTIFICATION_INTERRUPTION -> dest.writeString(event.mNotificationChannelId);
+            case Event.LOCUS_ID_SET -> dest.writeString(event.mLocusId);
+        }
+        dest.writeInt(event.mFlags);
+    }
+}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 3c256ad..c188686 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -714,7 +714,7 @@
     @UnsupportedAppUsage
     private Parcel mParcel = null;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private final int mEventCount;
+    private int mEventCount;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mIndex = 0;
@@ -735,6 +735,23 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public UsageEvents(Parcel in) {
+        if (Flags.useParceledList()) {
+            readUsageEventsFromParcelWithParceledList(in);
+        } else {
+            readUsageEventsFromParcelWithBlob(in);
+        }
+
+        mIncludeTaskRoots = true;
+    }
+
+    private void readUsageEventsFromParcelWithParceledList(Parcel in) {
+        mIndex = in.readInt();
+        mEventsToWrite = in.readParcelable(UsageEvents.class.getClassLoader(),
+            ParcelableUsageEventList.class).getList();
+        mEventCount = mEventsToWrite.size();
+    }
+
+    private void readUsageEventsFromParcelWithBlob(Parcel in) {
         byte[] bytes = in.readBlob();
         Parcel data = Parcel.obtain();
         data.unmarshall(bytes, 0, bytes.length);
@@ -752,7 +769,6 @@
             mParcel.setDataSize(mParcel.dataPosition());
             mParcel.setDataPosition(positionInParcel);
         }
-        mIncludeTaskRoots = true;
     }
 
     /**
@@ -810,6 +826,10 @@
             return false;
         }
 
+        if (Flags.useParceledList()) {
+            return getNextEventFromParceledList(eventOut);
+        }
+
         if (mParcel != null) {
             readEventFromParcel(mParcel, eventOut);
         } else {
@@ -824,6 +844,12 @@
         return true;
     }
 
+    private boolean getNextEventFromParceledList(Event eventOut) {
+        eventOut.copyFrom(mEventsToWrite.get(mIndex));
+        mIndex++;
+        return true;
+    }
+
     /**
      * Resets the collection so that it can be iterated over from the beginning.
      *
@@ -968,7 +994,7 @@
             case Event.CHOOSER_ACTION:
                 eventOut.mAction = p.readString();
                 eventOut.mContentType = p.readString();
-                eventOut.mContentAnnotations = p.createStringArray();
+                eventOut.mContentAnnotations = p.readStringArray();
                 break;
             case Event.STANDBY_BUCKET_CHANGED:
                 eventOut.mBucketAndReason = p.readInt();
@@ -990,6 +1016,19 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        if (Flags.useParceledList()) {
+            writeUsageEventsToParcelWithParceledList(dest, flags);
+        } else {
+            writeUsageEventsToParcelWithBlob(dest, flags);
+        }
+    }
+
+    private void writeUsageEventsToParcelWithParceledList(Parcel dest, int flags) {
+        dest.writeInt(mIndex);
+        dest.writeParcelable(new ParcelableUsageEventList(mEventsToWrite), flags);
+    }
+
+    private void writeUsageEventsToParcelWithBlob(Parcel dest, int flags) {
         Parcel data = Parcel.obtain();
         data.writeInt(mEventCount);
         data.writeInt(mIndex);
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index 4f1c65b..0b8e29f 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -21,3 +21,10 @@
     is_fixed_read_only: true
     bug: "299336442"
 }
+
+flag {
+    name: "use_parceled_list"
+    namespace: "backstage_power"
+    description: "Flag for parcelable usage event list"
+    bug: "301254110"
+}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 62fbcaf..4b2cee6 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -155,6 +155,7 @@
 
     /** @hide */
     @TestApi
+    @FlaggedApi(Flags.FLAG_ATTRIBUTION_SOURCE_CONSTRUCTOR)
     public AttributionSource(int uid, int pid, @Nullable String packageName,
             @Nullable String attributionTag, @NonNull IBinder token,
             @Nullable String[] renouncedPermissions,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 59bb73b..1c917ee 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -23,6 +23,7 @@
 import android.annotation.ColorRes;
 import android.annotation.DisplayContext;
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
@@ -860,12 +861,23 @@
      * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
      * appropriate in the future; this will not be removed for you.
      * <p>
-     * After {@link Build.VERSION_CODES#S}, Registering the ComponentCallbacks to Context created
+     * After {@link Build.VERSION_CODES#S}, registering the ComponentCallbacks to Context created
      * via {@link #createWindowContext(int, Bundle)} or
      * {@link #createWindowContext(Display, int, Bundle)} will receive
      * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from Window Context rather
      * than its base application. It is helpful if you want to handle UI components that
      * associated with the Window Context when the Window Context has configuration changes.</p>
+     * <p>
+     * After {@link Build.VERSION_CODES#TIRAMISU}, registering the ComponentCallbacks to
+     * {@link Activity} context will receive
+     * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from
+     * {@link Activity#onConfigurationChanged(Configuration)} rather than its base application.</p>
+     * <p>
+     * After {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, registering the ComponentCallbacks to
+     * {@link android.inputmethodservice.InputMethodService} will receive
+     * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from InputmethodService
+     * rather than its base application. It is helpful if you want to handle UI components when the
+     * IME has configuration changes.</p>
      *
      * @param callback The interface to call.  This can be either a
      * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface.
@@ -4773,6 +4785,7 @@
      * @see android.net.thread.ThreadNetworkManager
      * @hide
      */
+    @FlaggedApi("com.android.net.thread.flags.thread_enabled")
     @SystemApi
     public static final String THREAD_NETWORK_SERVICE = "thread_network";
 
@@ -6370,7 +6383,6 @@
      * @see android.remoteauth.RemoteAuthManager
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String REMOTE_AUTH_SERVICE = "remote_auth";
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7b6bad3..ffc4805 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6340,6 +6340,7 @@
      * the package is being archived. Either by removing the existing APK, or by installing
      * a package without an APK.
      */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
     public static final String EXTRA_ARCHIVAL = "android.intent.extra.ARCHIVAL";
 
     /**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index aefa55f..323592c 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1204,12 +1204,15 @@
     /**
      * This change id is the gatekeeper for all treatments that force a given min aspect ratio.
      * Enabling this change will allow the following min aspect ratio treatments to be applied:
-     * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
-     * OVERRIDE_MIN_ASPECT_RATIO_LARGE
+     * <ul>
+     *  <li>OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
+     *  <li>OVERRIDE_MIN_ASPECT_RATIO_LARGE
+     * </ul>
      *
      * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's manifest
      * will be overridden to the largest enabled aspect ratio treatment unless the app's manifest
-     * value is higher.
+     * value is higher. By default, this will only apply to activities with fixed portrait
+     * orientation if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY is not explicitly disabled.
      * @hide
      */
     @ChangeId
diff --git a/core/java/android/content/pm/ArchivedActivity.java b/core/java/android/content/pm/ArchivedActivity.java
new file mode 100644
index 0000000..9e49c9e
--- /dev/null
+++ b/core/java/android/content/pm/ArchivedActivity.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2023 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.content.pm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.util.DataClass;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+
+@DataClass(genBuilder = false, genConstructor = false, genSetters = true)
+@FlaggedApi(Flags.FLAG_ARCHIVING)
+public final class ArchivedActivity {
+    /** The label for the activity. */
+    private @NonNull CharSequence mLabel;
+    /** The component name of this activity. */
+    private @NonNull ComponentName mComponentName;
+    /**
+     * Icon of the activity in the app's locale. if null then the default icon would be shown in the
+     * launcher.
+     */
+    private @Nullable Drawable mIcon;
+    /** Monochrome icon, if defined, of the activity. */
+    private @Nullable Drawable mMonochromeIcon;
+
+    public ArchivedActivity(@NonNull CharSequence label, @NonNull ComponentName componentName) {
+        Objects.requireNonNull(label);
+        Objects.requireNonNull(componentName);
+        mLabel = label;
+        mComponentName = componentName;
+    }
+
+    /* @hide */
+    ArchivedActivity(@NonNull ArchivedActivityParcel parcel) {
+        mLabel = parcel.title;
+        mComponentName = parcel.originalComponentName;
+        mIcon = drawableFromCompressedBitmap(parcel.iconBitmap);
+        mMonochromeIcon = drawableFromCompressedBitmap(parcel.monochromeIconBitmap);
+    }
+
+    /* @hide */
+    @NonNull ArchivedActivityParcel getParcel() {
+        var parcel = new ArchivedActivityParcel();
+        parcel.title = mLabel.toString();
+        parcel.originalComponentName = mComponentName;
+        parcel.iconBitmap = mIcon == null ? null :
+                bytesFromBitmap(drawableToBitmap(mIcon));
+        parcel.monochromeIconBitmap = mMonochromeIcon == null ? null :
+                bytesFromBitmap(drawableToBitmap(mMonochromeIcon));
+        return parcel;
+    }
+
+    /**
+     * Convert a generic drawable into a bitmap.
+     * @hide
+     */
+    public static Bitmap drawableToBitmap(Drawable drawable) {
+        return drawableToBitmap(drawable, /* iconSize= */ 0);
+    }
+
+    /**
+     * Same as above, but scale the resulting image to fit iconSize.
+     * @hide
+     */
+    public static Bitmap drawableToBitmap(Drawable drawable, int iconSize) {
+        if (drawable instanceof BitmapDrawable) {
+            return ((BitmapDrawable) drawable).getBitmap();
+
+        }
+
+        Bitmap bitmap;
+        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+            // Needed for drawables that are just a single color.
+            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        } else {
+            bitmap =
+                    Bitmap.createBitmap(
+                            drawable.getIntrinsicWidth(),
+                            drawable.getIntrinsicHeight(),
+                            Bitmap.Config.ARGB_8888);
+        }
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        if (iconSize > 0 && bitmap.getWidth() > iconSize * 2 || bitmap.getHeight() > iconSize * 2) {
+            var scaledBitmap = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
+            if (scaledBitmap != bitmap) {
+                bitmap.recycle();
+            }
+            return scaledBitmap;
+        }
+        return bitmap;
+    }
+
+    /**
+     * Compress bitmap to PNG format.
+     * @hide
+     */
+    public static byte[] bytesFromBitmap(Bitmap bitmap) {
+        if (bitmap == null) {
+            return null;
+        }
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(
+                bitmap.getByteCount())) {
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+            return baos.toByteArray();
+        } catch (IOException ignored) {
+            return null;
+        }
+    }
+
+    private static Drawable drawableFromCompressedBitmap(byte[] bytes) {
+        if (bytes == null) {
+            return null;
+        }
+        return new BitmapDrawable(null /*res*/, new ByteArrayInputStream(bytes));
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivity.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * The label for the activity.
+     */
+    @DataClass.Generated.Member
+    public @NonNull CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * The component name of this activity.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * Icon of the activity in the app's locale. if null then the default icon would be shown in the
+     * launcher.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Monochrome icon, if defined, of the activity.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Drawable getMonochromeIcon() {
+        return mMonochromeIcon;
+    }
+
+    /**
+     * The label for the activity.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedActivity setLabel(@NonNull CharSequence value) {
+        mLabel = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mLabel);
+        return this;
+    }
+
+    /**
+     * The component name of this activity.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedActivity setComponentName(@NonNull ComponentName value) {
+        mComponentName = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mComponentName);
+        return this;
+    }
+
+    /**
+     * Icon of the activity in the app's locale. if null then the default icon would be shown in the
+     * launcher.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedActivity setIcon(@NonNull Drawable value) {
+        mIcon = value;
+        return this;
+    }
+
+    /**
+     * Monochrome icon, if defined, of the activity.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedActivity setMonochromeIcon(@NonNull Drawable value) {
+        mMonochromeIcon = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1698173429911L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivity.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static  byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static  android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivity extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/ArchivedPackage.java b/core/java/android/content/pm/ArchivedPackage.java
new file mode 100644
index 0000000..42795db
--- /dev/null
+++ b/core/java/android/content/pm/ArchivedPackage.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2023 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.content.pm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@DataClass(genBuilder = false, genConstructor = false, genSetters = true)
+@FlaggedApi(Flags.FLAG_ARCHIVING)
+public final class ArchivedPackage {
+    /** Name of the package as used to identify it in the system */
+    private @NonNull String mPackageName;
+    /** Signing certificates used to sign the package. */
+    private @NonNull SigningInfo mSigningInfo;
+    /**
+     * The version number of the package, as specified by the &lt;manifest&gt;tag's
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute.
+     */
+    private int mVersionCode = 0;
+    /**
+     * The major version number of the package, as specified by the &lt;manifest&gt;tag's
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
+     */
+    private int mVersionCodeMajor = 0;
+    /**
+     * This is the SDK version number that the application is targeting, as specified by the
+     * &lt;manifest&gt; tag's {@link android.R.styleable#AndroidManifestUsesSdk_targetSdkVersion}
+     * attribute.
+     */
+    private int mTargetSdkVersion = 0;
+    /**
+     * Package data will default to device protected storage. Specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage}
+     * attribute.
+     */
+    private @Nullable String mDefaultToDeviceProtectedStorage;
+    /**
+     * If {@code true} this app would like to run under the legacy storage model. Specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage}
+     * attribute.
+     */
+    private @Nullable String mRequestLegacyExternalStorage;
+    /**
+     * If {@code true} the user is prompted to keep the app's data on uninstall. Specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute.
+     */
+    private @Nullable String mUserDataFragile;
+    /**
+     * List of the package's activities that specify {@link Intent#ACTION_MAIN} and
+     * {@link Intent#CATEGORY_LAUNCHER}.
+     * @see LauncherApps#getActivityList
+     */
+    private @NonNull List<ArchivedActivity> mLauncherActivities;
+
+    public ArchivedPackage(@NonNull String packageName, @NonNull SigningInfo signingInfo,
+            @NonNull List<ArchivedActivity> launcherActivities) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(signingInfo);
+        Objects.requireNonNull(launcherActivities);
+        this.mPackageName = packageName;
+        this.mSigningInfo = signingInfo;
+        this.mLauncherActivities = launcherActivities;
+    }
+
+    /**
+     * Constructs the archived package from parcel.
+     * @hide
+     */
+    public ArchivedPackage(@NonNull ArchivedPackageParcel parcel) {
+        mPackageName = parcel.packageName;
+        mSigningInfo = new SigningInfo(parcel.signingDetails);
+        mVersionCode = parcel.versionCode;
+        mVersionCodeMajor = parcel.versionCodeMajor;
+        mTargetSdkVersion = parcel.targetSdkVersion;
+        mDefaultToDeviceProtectedStorage = parcel.defaultToDeviceProtectedStorage;
+        mRequestLegacyExternalStorage = parcel.requestLegacyExternalStorage;
+        mUserDataFragile = parcel.userDataFragile;
+        mLauncherActivities = new ArrayList<>();
+        if (parcel.archivedActivities != null) {
+            for (var activityParcel : parcel.archivedActivities) {
+                mLauncherActivities.add(new ArchivedActivity(activityParcel));
+            }
+        }
+    }
+
+    /* @hide */
+    ArchivedPackageParcel getParcel() {
+        var parcel = new ArchivedPackageParcel();
+        parcel.packageName = mPackageName;
+        parcel.signingDetails = mSigningInfo.getSigningDetails();
+        parcel.versionCode = mVersionCode;
+        parcel.versionCodeMajor = mVersionCodeMajor;
+        parcel.targetSdkVersion = mTargetSdkVersion;
+        parcel.defaultToDeviceProtectedStorage = mDefaultToDeviceProtectedStorage;
+        parcel.requestLegacyExternalStorage = mRequestLegacyExternalStorage;
+        parcel.userDataFragile = mUserDataFragile;
+
+        parcel.archivedActivities = new ArchivedActivityParcel[mLauncherActivities.size()];
+        for (int i = 0, size = parcel.archivedActivities.length; i < size; ++i) {
+            parcel.archivedActivities[i] = mLauncherActivities.get(i).getParcel();
+        }
+
+        return parcel;
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackage.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Name of the package as used to identify it in the system
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Signing certificates used to sign the package.
+     */
+    @DataClass.Generated.Member
+    public @NonNull SigningInfo getSigningInfo() {
+        return mSigningInfo;
+    }
+
+    /**
+     * The version number of the package, as specified by the &lt;manifest&gt;tag's
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute.
+     */
+    @DataClass.Generated.Member
+    public int getVersionCode() {
+        return mVersionCode;
+    }
+
+    /**
+     * The major version number of the package, as specified by the &lt;manifest&gt;tag's
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
+     */
+    @DataClass.Generated.Member
+    public int getVersionCodeMajor() {
+        return mVersionCodeMajor;
+    }
+
+    /**
+     * This is the SDK version number that the application is targeting, as specified by the
+     * &lt;manifest&gt; tag's {@link android.R.styleable#AndroidManifestUsesSdk_targetSdkVersion}
+     * attribute.
+     */
+    @DataClass.Generated.Member
+    public int getTargetSdkVersion() {
+        return mTargetSdkVersion;
+    }
+
+    /**
+     * Package data will default to device protected storage. Specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage}
+     * attribute.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getDefaultToDeviceProtectedStorage() {
+        return mDefaultToDeviceProtectedStorage;
+    }
+
+    /**
+     * If {@code true} this app would like to run under the legacy storage model. Specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage}
+     * attribute.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getRequestLegacyExternalStorage() {
+        return mRequestLegacyExternalStorage;
+    }
+
+    /**
+     * If {@code true} the user is prompted to keep the app's data on uninstall. Specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getUserDataFragile() {
+        return mUserDataFragile;
+    }
+
+    /**
+     * List of the package's activities that specify {@link Intent#ACTION_MAIN} and
+     * {@link Intent#CATEGORY_LAUNCHER}.
+     *
+     * @see LauncherApps#getActivityList
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<ArchivedActivity> getLauncherActivities() {
+        return mLauncherActivities;
+    }
+
+    /**
+     * Name of the package as used to identify it in the system
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setPackageName(@NonNull String value) {
+        mPackageName = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        return this;
+    }
+
+    /**
+     * Signing certificates used to sign the package.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setSigningInfo(@NonNull SigningInfo value) {
+        mSigningInfo = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSigningInfo);
+        return this;
+    }
+
+    /**
+     * The version number of the package, as specified by the &lt;manifest&gt;tag's
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setVersionCode( int value) {
+        mVersionCode = value;
+        return this;
+    }
+
+    /**
+     * The major version number of the package, as specified by the &lt;manifest&gt;tag's
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setVersionCodeMajor( int value) {
+        mVersionCodeMajor = value;
+        return this;
+    }
+
+    /**
+     * This is the SDK version number that the application is targeting, as specified by the
+     * &lt;manifest&gt; tag's {@link android.R.styleable#AndroidManifestUsesSdk_targetSdkVersion}
+     * attribute.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setTargetSdkVersion( int value) {
+        mTargetSdkVersion = value;
+        return this;
+    }
+
+    /**
+     * Package data will default to device protected storage. Specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage}
+     * attribute.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String value) {
+        mDefaultToDeviceProtectedStorage = value;
+        return this;
+    }
+
+    /**
+     * If {@code true} this app would like to run under the legacy storage model. Specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage}
+     * attribute.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setRequestLegacyExternalStorage(@NonNull String value) {
+        mRequestLegacyExternalStorage = value;
+        return this;
+    }
+
+    /**
+     * If {@code true} the user is prompted to keep the app's data on uninstall. Specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setUserDataFragile(@NonNull String value) {
+        mUserDataFragile = value;
+        return this;
+    }
+
+    /**
+     * List of the package's activities that specify {@link Intent#ACTION_MAIN} and
+     * {@link Intent#CATEGORY_LAUNCHER}.
+     *
+     * @see LauncherApps#getActivityList
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArchivedPackage setLauncherActivities(@NonNull List<ArchivedActivity> value) {
+        mLauncherActivities = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mLauncherActivities);
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1697824890503L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackage.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate  int mVersionCode\nprivate  int mVersionCodeMajor\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivity> mLauncherActivities\n  android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackage extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index edb07ce..59ed045 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.IPackageDeleteObserver2;
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
@@ -82,4 +83,11 @@
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
     void requestUnarchive(String packageName, String callerPackageName, in UserHandle userHandle);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)")
+    void installPackageArchived(in ArchivedPackageParcel archivedPackageParcel,
+            in PackageInstaller.SessionParams params,
+            in IntentSender statusReceiver,
+            String installerPackageName, in UserHandle userHandle);
+
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d837aae..cbb20e0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1000,6 +1000,37 @@
         }
     }
 
+    /**
+     * Install package in an archived state.
+     *
+     * @param archivedPackage archived package data such as package name, signature etc.
+     * @param sessionParams used to create an underlying installation session
+     * @param statusReceiver Called when the state of the session changes. Intents
+     *                       sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
+     *                       individual status codes on how to handle them.
+     * @see #createSession
+     * @see PackageInstaller.Session#commit
+     */
+    @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+    @FlaggedApi(Flags.FLAG_ARCHIVING)
+    public void installPackageArchived(@NonNull ArchivedPackage archivedPackage,
+            @NonNull SessionParams sessionParams,
+            @NonNull IntentSender statusReceiver) {
+        Objects.requireNonNull(archivedPackage, "archivedPackage cannot be null");
+        Objects.requireNonNull(sessionParams, "sessionParams cannot be null");
+        Objects.requireNonNull(statusReceiver, "statusReceiver cannot be null");
+        try {
+            mInstaller.installPackageArchived(
+                    archivedPackage.getParcel(),
+                    sessionParams,
+                    statusReceiver,
+                    mInstallerPackageName,
+                    new UserHandle(mUserId));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** {@hide} */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
@@ -2504,6 +2535,8 @@
         public DataLoaderParams dataLoaderParams;
         /** {@hide} */
         public int rollbackDataPolicy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE;
+        /** @hide */
+        public long rollbackLifetimeMillis = 0;
         /** {@hide} */
         public boolean forceQueryableOverride;
         /** {@hide} */
@@ -2558,6 +2591,7 @@
                 dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel);
             }
             rollbackDataPolicy = source.readInt();
+            rollbackLifetimeMillis = source.readLong();
             requireUserAction = source.readInt();
             packageSource = source.readInt();
             applicationEnabledSettingPersistent = source.readBoolean();
@@ -2590,6 +2624,7 @@
             ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
             ret.dataLoaderParams = dataLoaderParams;
             ret.rollbackDataPolicy = rollbackDataPolicy;
+            ret.rollbackLifetimeMillis = rollbackLifetimeMillis;
             ret.requireUserAction = requireUserAction;
             ret.packageSource = packageSource;
             ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
@@ -2871,12 +2906,7 @@
          */
         @SystemApi
         public void setEnableRollback(boolean enable) {
-            if (enable) {
-                installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
-            } else {
-                installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
-            }
-            rollbackDataPolicy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE;
+            setEnableRollback(enable, PackageManager.ROLLBACK_DATA_POLICY_RESTORE);
         }
 
         /**
@@ -2900,10 +2930,36 @@
                 installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
             } else {
                 installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+                rollbackLifetimeMillis = 0;
             }
             rollbackDataPolicy = dataPolicy;
         }
 
+        /**
+         * If rollback enabled for this session (via {@link #setEnableRollback}, set time
+         * after which rollback will no longer be possible
+         *
+         * <p>For multi-package installs, this value must be set on the parent session.
+         * Child session rollback lifetime will be ignored.
+         *
+         * @param lifetimeMillis time after which rollback expires
+         * @throws IllegalArgumentException if lifetimeMillis is negative or rollback is not
+         * enabled via setEnableRollback.
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+        @FlaggedApi(Flags.FLAG_ROLLBACK_LIFETIME)
+        public void setRollbackLifetimeMillis(@DurationMillisLong long lifetimeMillis) {
+            if (lifetimeMillis < 0) {
+                throw new IllegalArgumentException("rollbackLifetimeMillis can't be negative.");
+            }
+            if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
+                throw new IllegalArgumentException(
+                        "Can't set rollbackLifetimeMillis when rollback is not enabled");
+            }
+            rollbackLifetimeMillis = lifetimeMillis;
+        }
 
         /**
          * @deprecated use {@link #setRequestDowngrade(boolean)}.
@@ -3264,6 +3320,7 @@
             pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
             pw.printPair("dataLoaderParams", dataLoaderParams);
             pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
+            pw.printPair("rollbackLifetimeMillis", rollbackLifetimeMillis);
             pw.printPair("applicationEnabledSettingPersistent",
                     applicationEnabledSettingPersistent);
             pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
@@ -3305,6 +3362,7 @@
                 dest.writeParcelable(null, flags);
             }
             dest.writeInt(rollbackDataPolicy);
+            dest.writeLong(rollbackLifetimeMillis);
             dest.writeInt(requireUserAction);
             dest.writeInt(packageSource);
             dest.writeBoolean(applicationEnabledSettingPersistent);
@@ -3498,6 +3556,9 @@
         /** {@hide} */
         public int rollbackDataPolicy;
 
+        /** @hide */
+        public long rollbackLifetimeMillis;
+
         /** {@hide} */
         public int requireUserAction;
 
@@ -3565,6 +3626,7 @@
             isCommitted = source.readBoolean();
             isPreapprovalRequested = source.readBoolean();
             rollbackDataPolicy = source.readInt();
+            rollbackLifetimeMillis = source.readLong();
             createdMillis = source.readLong();
             requireUserAction = source.readInt();
             installerUid = source.readInt();
@@ -4189,6 +4251,7 @@
             dest.writeBoolean(isCommitted);
             dest.writeBoolean(isPreapprovalRequested);
             dest.writeInt(rollbackDataPolicy);
+            dest.writeLong(rollbackLifetimeMillis);
             dest.writeLong(createdMillis);
             dest.writeInt(requireUserAction);
             dest.writeInt(installerUid);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b15c9e4..ad7dd51 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3778,6 +3778,7 @@
      * The device is capable of communicating with other devices via
      * <a href="https://www.threadgroup.org">Thread</a> networking protocol.
      */
+    @FlaggedApi("com.android.net.thread.flags.thread_enabled")
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
 
@@ -11026,6 +11027,16 @@
                 "makeUidVisible not implemented in subclass");
     }
 
+    /**
+     * Return archived package info for the package or null if the package is not installed.
+     * @see PackageInstaller#installPackageArchived
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
+    public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) {
+        throw new UnsupportedOperationException(
+                "getArchivedPackage not implemented in subclass");
+    }
+
     // Some of the flags don't affect the query result, but let's be conservative and cache
     // each combination of flags separately.
 
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index ee9aaca3..543703e 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,9 +16,16 @@
 
 package android.content.pm;
 
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.security.PublicKey;
+import java.util.Collection;
 
 /**
  * Information pertaining to the signing certificates used to sign a package.
@@ -33,6 +40,43 @@
     }
 
     /**
+     * Creates a new instance of information used to sign the APK.
+     *
+     * @param schemeVersion version of signing schema.
+     * @param apkContentsSigners signing certificates.
+     * @param publicKeys for the signing certificates.
+     * @param signingCertificateHistory All signing certificates the package has proven it is
+     *                                  authorized to use.
+     *
+     * @see
+     * <a href="https://source.android.com/docs/security/features/apksigning#schemes">APK signing
+     * schemas</a>
+     */
+    @FlaggedApi(Flags.FLAG_ARCHIVING)
+    public SigningInfo(@IntRange(from = 0) int schemeVersion,
+            @Nullable Collection<Signature> apkContentsSigners,
+            @Nullable Collection<PublicKey> publicKeys,
+            @Nullable Collection<Signature> signingCertificateHistory) {
+        if (schemeVersion <= 0 || apkContentsSigners == null) {
+            mSigningDetails = SigningDetails.UNKNOWN;
+            return;
+        }
+        Signature[] signatures = apkContentsSigners != null && !apkContentsSigners.isEmpty()
+                ? apkContentsSigners.toArray(new Signature[apkContentsSigners.size()])
+                : null;
+        Signature[] pastSignatures =
+                signingCertificateHistory != null && !signingCertificateHistory.isEmpty()
+                ? signingCertificateHistory.toArray(new Signature[signingCertificateHistory.size()])
+                : null;
+        if (Signature.areExactArraysMatch(signatures, pastSignatures)) {
+            pastSignatures = null;
+        }
+        ArraySet<PublicKey> keys =
+                publicKeys != null && !publicKeys.isEmpty() ? new ArraySet<>(publicKeys) : null;
+        mSigningDetails = new SigningDetails(signatures, schemeVersion, keys, pastSignatures);
+    }
+
+    /**
      * @hide only packagemanager should be populating this
      */
     public SigningInfo(SigningDetails signingDetails) {
@@ -116,6 +160,26 @@
         return mSigningDetails.getSignatures();
     }
 
+    /**
+     * Returns the version of signing schema used to sign the APK.
+     *
+     * @see
+     * <a href="https://source.android.com/docs/security/features/apksigning#schemes">APK signing
+     * schemas</a>
+     */
+    @FlaggedApi(Flags.FLAG_ARCHIVING)
+    public @IntRange(from = 0) int getSchemeVersion() {
+        return mSigningDetails.getSignatureSchemeVersion();
+    }
+
+    /**
+     * Returns the public keys for the signing certificates.
+     */
+    @FlaggedApi(Flags.FLAG_ARCHIVING)
+    public @NonNull Collection<PublicKey> getPublicKeys() {
+        return mSigningDetails.getPublicKeys();
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -126,6 +190,12 @@
         mSigningDetails.writeToParcel(dest, parcelableFlags);
     }
 
+    /* @hide */
+    @NonNull
+    SigningDetails getSigningDetails() {
+        return mSigningDetails;
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<SigningInfo> CREATOR =
             new Parcelable.Creator<SigningInfo>() {
         @Override
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 96609ad..9ad66ce 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -58,3 +58,11 @@
     bug: "295827951"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "rollback_lifetime"
+    namespace: "package_manager_service"
+    description: "Feature flag to enable custom rollback lifetime during install."
+    bug: "299670324"
+    is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3ec239c..43cf97f 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -28,3 +28,10 @@
     description: "Framework support for communal profile."
     bug: "285426179"
 }
+
+flag {
+    name: "support_communal_profile_nextgen"
+    namespace: "multiuser"
+    description: "Further framework support for communal profile, beyond the basics, for later releases."
+    bug: "285426179"
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 9c05dfc..82694ee 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -537,24 +537,6 @@
     }
 
     /**
-     * Listens for biometric prompt status, i.e., if it is being shown or idle.
-     * @hide
-     */
-    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void registerBiometricPromptStatusListener(
-            IBiometricPromptStatusListener callback) {
-        if (mService != null) {
-            try {
-                mService.registerBiometricPromptStatusListener(callback);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            Slog.w(TAG, "registerBiometricPromptOnKeyguardCallback(): Service not connected");
-        }
-    }
-
-    /**
      * Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their
      * authenticatorId invalidated for the specified user. This happens when enrollments have been
      * added on devices with multiple biometric sensors.
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 8eede47..c2e5c0b 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -17,7 +17,6 @@
 package android.hardware.biometrics;
 
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptStatusListener;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 import android.hardware.biometrics.IInvalidationCallback;
 import android.hardware.biometrics.ITestSession;
@@ -64,9 +63,6 @@
     // Register callback for when keyguard biometric eligibility changes.
     void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
 
-    // Register callback to check biometric prompt status.
-    void registerBiometricPromptStatusListener(IBiometricPromptStatusListener callback);
-
     // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
     // specified user. This happens when enrollments have been added on devices with multiple
     // biometric sensors.
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 36606a1..18c8d1b 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,7 +17,6 @@
 package android.hardware.biometrics;
 
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptStatusListener;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IInvalidationCallback;
@@ -69,10 +68,6 @@
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
 
-    // Register a callback for biometric prompt status on keyguard.
-    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
-    void registerBiometricPromptStatusListener(IBiometricPromptStatusListener callback);
-
     // Notify BiometricService when <Biometric>Service is ready to start the prepared client.
     // Client lifecycle is still managed in <Biometric>Service.
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index bf77681..db7055b 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -357,7 +357,7 @@
             mCameraRepeatingSurface = mRepeatingRequestImageReader.getSurface();
         }
         mRepeatingRequestImageCallback = new CameraOutputImageCallback(
-                mRepeatingRequestImageReader);
+                mRepeatingRequestImageReader, true /*pruneOlderBuffers*/);
         mRepeatingRequestImageReader
                 .setOnImageAvailableListener(mRepeatingRequestImageCallback, mHandler);
     }
@@ -398,7 +398,8 @@
                         CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT);
             }
 
-            mBurstCaptureImageCallback = new CameraOutputImageCallback(mBurstCaptureImageReader);
+            mBurstCaptureImageCallback = new CameraOutputImageCallback(mBurstCaptureImageReader,
+                    false /*pruneOlderBuffers*/);
             mBurstCaptureImageReader.setOnImageAvailableListener(mBurstCaptureImageCallback,
                     mHandler);
             mCameraBurstSurface = mBurstCaptureImageReader.getSurface();
@@ -1106,7 +1107,9 @@
                 }
 
                 for (Pair<Image, TotalCaptureResult> captureStage : mCaptureStageMap.values()) {
-                    captureStage.first.close();
+                    if (captureStage.first != null) {
+                        captureStage.first.close();
+                    }
                 }
                 mCaptureStageMap.clear();
             }
@@ -1207,6 +1210,7 @@
                 if (mImageProcessor != null) {
                     if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
                         Image img = mCapturePendingMap.get(timestamp).first;
+                        mCapturePendingMap.remove(timestamp);
                         mCaptureStageMap.put(stageId, new Pair<>(img, result));
                         checkAndFireBurstProcessing();
                     } else {
@@ -1303,6 +1307,7 @@
                 reader.detachImage(img);
                 if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
                     Integer stageId = mCapturePendingMap.get(timestamp).second;
+                    mCapturePendingMap.remove(timestamp);
                     Pair<Image, TotalCaptureResult> captureStage =
                             mCaptureStageMap.get(stageId);
                     if (captureStage != null) {
@@ -1402,9 +1407,11 @@
         private HashMap<Long, Pair<Image, OnImageAvailableListener>> mImageListenerMap =
                 new HashMap<>();
         private boolean mOutOfBuffers = false;
+        private final boolean mPruneOlderBuffers;
 
-        CameraOutputImageCallback(ImageReader imageReader) {
+        CameraOutputImageCallback(ImageReader imageReader, boolean pruneOlderBuffers) {
             mImageReader = imageReader;
+            mPruneOlderBuffers = pruneOlderBuffers;
         }
 
         @Override
@@ -1447,6 +1454,10 @@
                 ArrayList<Long> removedTs = new ArrayList<>();
                 for (long ts : timestamps) {
                     if (ts < timestamp) {
+                        if (!mPruneOlderBuffers) {
+                            Log.w(TAG, "Unexpected older image with ts: " + ts);
+                            continue;
+                        }
                         Log.e(TAG, "Dropped image with ts: " + ts);
                         Pair<Image, OnImageAvailableListener> entry = mImageListenerMap.get(ts);
                         if (entry.second != null) {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 5e53373..95526a8 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1403,6 +1403,7 @@
                 if (mSurfaces.get(i) != other.mSurfaces.get(i))
                     return false;
             }
+            if (!mIsDeferredConfig && mSurfaces.size() != other.mSurfaces.size()) return false;
             if (mDynamicRangeProfile != other.mDynamicRangeProfile) {
                 return false;
             }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index aeddd0c..8e49c4c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -46,6 +46,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -71,7 +72,11 @@
 @SystemService(Context.DISPLAY_SERVICE)
 public final class DisplayManager {
     private static final String TAG = "DisplayManager";
-    private static final boolean DEBUG = false;
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayManager DEBUG && adb reboot'
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+            || Log.isLoggable("DisplayManager_All", Log.DEBUG);
     private static final boolean ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE = true;
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 2b5f5ee..75f0ceb 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -42,7 +42,6 @@
 import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.Trace;
@@ -82,7 +81,9 @@
     private static String sCurrentPackageName = ActivityThread.currentPackageName();
     private static boolean sExtraDisplayListenerLogging = initExtraLogging();
 
-    private static final boolean DEBUG = false || sExtraDisplayListenerLogging;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayManager DEBUG && adb reboot'
+    private static final boolean DEBUG = DisplayManager.DEBUG || sExtraDisplayListenerLogging;
 
     // True if display info and display ids should be cached.
     //
@@ -412,6 +413,18 @@
         }
     }
 
+    /**
+     * Called when there is a display-related window configuration change. Reroutes the event from
+     * WindowManager to make sure the {@link Display} fields are up-to-date in the last callback.
+     * @param displayId the logical display that was changed.
+     */
+    public void handleDisplayChangeFromWindowManager(int displayId) {
+        // There can be racing condition between DMS and WMS callbacks, so force triggering the
+        // listener to make sure the client can get the onDisplayChanged callback even if
+        // DisplayInfo is not changed (Display read from both DisplayInfo and WindowConfiguration).
+        handleDisplayEvent(displayId, EVENT_DISPLAY_CHANGED, true /* forceUpdate */);
+    }
+
     private static Looper getLooperForHandler(@Nullable Handler handler) {
         Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
         if (looper == null) {
@@ -470,7 +483,7 @@
         }
     }
 
-    private void handleDisplayEvent(int displayId, @DisplayEvent int event) {
+    private void handleDisplayEvent(int displayId, @DisplayEvent int event, boolean forceUpdate) {
         final DisplayInfo info;
         synchronized (mLock) {
             if (USE_CACHE) {
@@ -501,7 +514,7 @@
         // Accepting an Executor means the listener may be synchronously invoked, so we must
         // not be holding mLock when we do so
         for (DisplayListenerDelegate listener : mDisplayListeners) {
-            listener.sendDisplayEvent(displayId, event, info);
+            listener.sendDisplayEvent(displayId, event, info, forceUpdate);
         }
     }
 
@@ -1176,7 +1189,7 @@
                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + eventToString(
                         event));
             }
-            handleDisplayEvent(displayId, event);
+            handleDisplayEvent(displayId, event, false /* forceUpdate */);
         }
     }
 
@@ -1197,87 +1210,86 @@
             mPackageName = packageName;
         }
 
-        public void sendDisplayEvent(int displayId, @DisplayEvent int event, DisplayInfo info) {
+        void sendDisplayEvent(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info,
+                boolean forceUpdate) {
             if (extraLogging()) {
                 Slog.i(TAG, "Sending Display Event: " + eventToString(event));
             }
             long generationId = mGenerationId.get();
-            Message msg = Message.obtain(null, event, displayId, 0, info);
             mExecutor.execute(() -> {
-                // If the generation id's don't match we were canceled but still need to recycle()
+                // If the generation id's don't match we were canceled
                 if (generationId == mGenerationId.get()) {
-                    handleMessage(msg);
+                    handleDisplayEventInner(displayId, event, info, forceUpdate);
                 }
-                msg.recycle();
             });
         }
 
-        public void clearEvents() {
+        void clearEvents() {
             mGenerationId.incrementAndGet();
         }
 
-        public void setEventsMask(@EventsMask long newEventsMask) {
+        void setEventsMask(@EventsMask long newEventsMask) {
             mEventsMask = newEventsMask;
         }
 
-        private void handleMessage(Message msg) {
+        private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
+                @Nullable DisplayInfo info, boolean forceUpdate) {
             if (extraLogging()) {
-                Slog.i(TAG, "DLD(" + eventToString(msg.what)
-                        + ", display=" + msg.arg1
+                Slog.i(TAG, "DLD(" + eventToString(event)
+                        + ", display=" + displayId
                         + ", mEventsMask=" + Long.toBinaryString(mEventsMask)
                         + ", mPackageName=" + mPackageName
-                        + ", msg.obj=" + msg.obj
+                        + ", displayInfo=" + info
                         + ", listener=" + mListener.getClass() + ")");
             }
             if (DEBUG) {
                 Trace.beginSection(
                         TextUtils.trimToSize(
-                                "DLD(" + eventToString(msg.what)
-                                + ", display=" + msg.arg1
+                                "DLD(" + eventToString(event)
+                                + ", display=" + displayId
                                 + ", listener=" + mListener.getClass() + ")", 127));
             }
-            switch (msg.what) {
+            switch (event) {
                 case EVENT_DISPLAY_ADDED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
-                        mListener.onDisplayAdded(msg.arg1);
+                        mListener.onDisplayAdded(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_CHANGED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
-                        DisplayInfo newInfo = (DisplayInfo) msg.obj;
-                        if (newInfo != null && !newInfo.equals(mDisplayInfo)) {
+                        if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
                             if (extraLogging()) {
                                 Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
-                                        + newInfo);
+                                        + info);
                             }
-                            mDisplayInfo.copyFrom(newInfo);
-                            mListener.onDisplayChanged(msg.arg1);
+                            mDisplayInfo.copyFrom(info);
+                            mListener.onDisplayChanged(displayId);
                         }
                     }
                     break;
                 case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
-                        mListener.onDisplayChanged(msg.arg1);
+                        mListener.onDisplayChanged(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_REMOVED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
-                        mListener.onDisplayRemoved(msg.arg1);
+                        mListener.onDisplayRemoved(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
-                        mListener.onDisplayChanged(msg.arg1);
+                        mListener.onDisplayChanged(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_CONNECTED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
-                        mListener.onDisplayConnected(msg.arg1);
+                        mListener.onDisplayConnected(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_DISCONNECTED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
-                        mListener.onDisplayDisconnected(msg.arg1);
+                        mListener.onDisplayDisconnected(displayId);
                     }
                     break;
             }
@@ -1423,12 +1435,10 @@
             sExtraDisplayListenerLogging = !TextUtils.isEmpty(EXTRA_LOGGING_PACKAGE_NAME)
                     && EXTRA_LOGGING_PACKAGE_NAME.equals(sCurrentPackageName);
         }
-        // TODO: b/306170135 - return sExtraDisplayListenerLogging instead
-        return true;
+        return sExtraDisplayListenerLogging;
     }
 
     private static boolean extraLogging() {
-        // TODO: b/306170135 - return sExtraDisplayListenerLogging & package name check instead
-        return true;
+        return sExtraDisplayListenerLogging;
     }
 }
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index 4f07acf..c5167db 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -17,6 +17,7 @@
 package android.hardware.radio;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -45,7 +46,7 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private final Map<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
+    private final ArrayMap<ProgramSelector.Identifier, ArrayMap<UniqueProgramIdentifier,
             RadioManager.ProgramInfo>> mPrograms = new ArrayMap<>();
 
     @GuardedBy("mLock")
@@ -203,11 +204,11 @@
             listCallbacksCopied = new ArrayList<>(mListCallbacks);
 
             if (chunk.isPurge()) {
-                Iterator<Map.Entry<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
-                        RadioManager.ProgramInfo>>> programsIterator =
-                        mPrograms.entrySet().iterator();
+                Iterator<Map.Entry<ProgramSelector.Identifier,
+                        ArrayMap<UniqueProgramIdentifier, RadioManager.ProgramInfo>>>
+                        programsIterator = mPrograms.entrySet().iterator();
                 while (programsIterator.hasNext()) {
-                    Map.Entry<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
+                    Map.Entry<ProgramSelector.Identifier, ArrayMap<UniqueProgramIdentifier,
                             RadioManager.ProgramInfo>> removed = programsIterator.next();
                     if (removed.getValue() != null) {
                         removedList.add(removed.getKey());
@@ -270,8 +271,7 @@
         if (!mPrograms.containsKey(primaryKey)) {
             return;
         }
-        Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries = mPrograms
-                .get(primaryKey);
+        Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries = mPrograms.get(primaryKey);
         RadioManager.ProgramInfo removed = entries.remove(Objects.requireNonNull(key));
         if (removed == null) return;
         if (entries.size() == 0) {
@@ -287,15 +287,10 @@
     public @NonNull List<RadioManager.ProgramInfo> toList() {
         List<RadioManager.ProgramInfo> list = new ArrayList<>();
         synchronized (mLock) {
-            Iterator<Map.Entry<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
-                    RadioManager.ProgramInfo>>> listIterator = mPrograms.entrySet().iterator();
-            while (listIterator.hasNext()) {
-                Iterator<Map.Entry<UniqueProgramIdentifier,
-                        RadioManager.ProgramInfo>> prorgramsIterator = listIterator.next()
-                        .getValue().entrySet().iterator();
-                while (prorgramsIterator.hasNext()) {
-                    list.add(prorgramsIterator.next().getValue());
-                }
+            for (int index = 0; index < mPrograms.size(); index++) {
+                ArrayMap<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries =
+                        mPrograms.valueAt(index);
+                list.addAll(entries.values());
             }
         }
         return list;
@@ -304,9 +299,16 @@
     /**
      * Returns the program with a specified primary identifier.
      *
+     * <p>This method only returns the first program from the list return from
+     * {@link #getProgramInfos}
+     *
      * @param id primary identifier of a program to fetch
      * @return the program info, or null if there is no such program on the list
+     *
+     * @deprecated Use {@link #getProgramInfos(ProgramSelector.Identifier)} to get all programs
+     * with the given primary identifier
      */
+    @Deprecated
     public @Nullable RadioManager.ProgramInfo get(@NonNull ProgramSelector.Identifier id) {
         Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries;
         synchronized (mLock) {
@@ -320,6 +322,29 @@
     }
 
     /**
+     * Returns the program list with a specified primary identifier.
+     *
+     * @param id primary identifier of a program to fetch
+     * @return the program info list with the primary identifier, or empty list if there is no such
+     * program identifier on the list
+     * @throws NullPointerException if primary identifier is {@code null}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public @NonNull List<RadioManager.ProgramInfo> getProgramInfos(
+            @NonNull ProgramSelector.Identifier id) {
+        Objects.requireNonNull(id, "Primary identifier can not be null");
+        ArrayMap<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries;
+        synchronized (mLock) {
+            entries = mPrograms.get(id);
+        }
+
+        if (entries == null) {
+            return new ArrayList<>();
+        }
+        return new ArrayList<>(entries.values());
+    }
+
+    /**
      * Filter for the program list.
      */
     public static final class Filter implements Parcelable {
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 12442ba..c7ec052 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -16,6 +16,7 @@
 
 package android.hardware.radio;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -124,6 +125,86 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProgramType {}
 
+    /**
+     * Bitmask for HD radio subchannel 1
+     *
+     * <p>There are at most 8 HD radio subchannels of 1-based om HD radio standard. It is
+     * converted to 0-based index. 0 is the index of main program service (MPS). 1 to 7 are
+     * indexes of additional supplemental program services (SPS).
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_1 = 1 << 0;
+
+    /**
+     * Bitmask for HD radio subchannel 2
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_2 = 1 << 1;
+
+    /**
+     * Bitmask for HD radio subchannel 3
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_3 = 1 << 2;
+
+    /**
+     * Bitmask for HD radio subchannel 4
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_4 = 1 << 3;
+
+    /**
+     * Bitmask for HD radio subchannel 5
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_5 = 1 << 4;
+
+    /**
+     * Bitmask for HD radio subchannel 6
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_6 = 1 << 5;
+
+    /**
+     * Bitmask for HD radio subchannel 7
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_7 = 1 << 6;
+
+    /**
+     * Bitmask for HD radio subchannel 8
+     *
+     * <p>For further reference, see {@link #SUB_CHANNEL_HD_1}
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int SUB_CHANNEL_HD_8 = 1 << 7;
+
+    /** @hide */
+    @IntDef(prefix = { "SUB_CHANNEL_HD_" }, value = {
+            SUB_CHANNEL_HD_1,
+            SUB_CHANNEL_HD_2,
+            SUB_CHANNEL_HD_3,
+            SUB_CHANNEL_HD_4,
+            SUB_CHANNEL_HD_5,
+            SUB_CHANNEL_HD_6,
+            SUB_CHANNEL_HD_7,
+            SUB_CHANNEL_HD_8,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HdSubChannel {}
+
     public static final int IDENTIFIER_TYPE_INVALID = 0;
     /**
      * Primary identifier for analog (without RDS) AM/FM stations:
@@ -147,19 +228,15 @@
      *
      * <p>Consists of (from the LSB):
      * <li>
-     *     <ul>32bit: Station ID number.
-     *     <ul>4bit: HD_SUBCHANNEL.
-     *     <ul>18bit: AMFM_FREQUENCY.
+     *     <ul>32bit: Station ID number.</ul>
+     *     <ul>4bit: HD subchannel, see {@link #SUB_CHANNEL_HD_1}.</ul>
+     *     <ul>18bit: AMFM_FREQUENCY.</ul>
      * </li>
      *
      * <p>While station ID number should be unique globally, it sometimes gets
      * abused by broadcasters (i.e. not being set at all). To ensure local
      * uniqueness, AMFM_FREQUENCY_KHZ was added here. Global uniqueness is
-     * a best-effort - see {@link IDENTIFIER_TYPE_HD_STATION_NAME}.
-     *
-     * <p>HD Radio subchannel is a value in range of 0-7.
-     * This index is 0-based (where 0 is MPS and 1..7 are SPS),
-     * as opposed to HD Radio standard (where it's 1-based).
+     * a best-effort - see {@link #IDENTIFIER_TYPE_HD_STATION_NAME}.
      *
      * <p>The remaining bits should be set to zeros when writing on the chip side
      * and ignored when read.
@@ -202,9 +279,9 @@
      *
      * <p>Consists of (from the LSB):
      * <li>
-     *     <ul>16bit: SId.
-     *     <ul>8bit: ECC code.
-     *     <ul>4bit: SCIdS.
+     *     <ul>16bit: SId.</ul>
+     *     <ul>8bit: ECC code.</ul>
+     *     <ul>4bit: SCIdS.</ul>
      * </li>
      *
      * <p>SCIdS (Service Component Identifier within the Service) value
@@ -238,18 +315,26 @@
     public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
     /**
      * 32bit primary identifier for SiriusXM Satellite Radio.
+     *
+     * @deprecated SiriusXM Satellite Radio is not supported
      */
     public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
-    /** 0-999 range */
+    /**
+     * 0-999 range
+     *
+     * @deprecated SiriusXM Satellite Radio is not supported
+     */
     public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13;
     /**
      * 44bit compound primary identifier for Digital Audio Broadcasting and
      * Digital Multimedia Broadcasting.
      *
      * <p>Consists of (from the LSB):
-     * - 32bit: SId;
-     * - 8bit: ECC code;
-     * - 4bit: SCIdS.
+     * <li>
+     *     <ul>32bit: SId;</ul>
+     *     <ul>8bit: ECC code;</ul>
+     *     <ul>4bit: SCIdS.</ul>
+     * </li>
      *
      * <p>SCIdS (Service Component Identifier within the Service) value
      * of 0 represents the main service, while 1 and above represents
@@ -260,6 +345,36 @@
      */
     public static final int IDENTIFIER_TYPE_DAB_DMB_SID_EXT = 14;
     /**
+     * 64bit additional identifier for HD Radio representing station location.
+     *
+     * <p>Consists of (from the LSB):
+     * <li>
+     *     <ul>4 bit: Bits 0:3 of altitude</ul>
+     *     <ul>13 bit: Fractional bits of longitude</ul>
+     *     <ul>8 bit: Integer bits of longitude</ul>
+     *     <ul>1 bit: 0 for east and 1 for west for longitude</ul>
+     *     <ul>1 bit: 0, representing longitude</ul>
+     *     <ul>5 bit: pad of zeros separating longitude and latitude</ul>
+     *     <ul>4 bit: Bits 4:7 of altitude</ul>
+     *     <ul>13 bit: Fractional bits of latitude</ul>
+     *     <ul>8 bit: Integer bits of latitude</ul>
+     *     <ul>1 bit: 0 for north and 1 for south for latitude</ul>
+     *     <ul>1 bit: 1, representing latitude</ul>
+     *     <ul>5 bit: pad of zeros</ul>
+     * </li>
+     *
+     * <p>This format is defined in NRSC-5-C document: SY_IDD_1020s.
+     *
+     * <p>Due to Station ID abuse, some
+     * {@link #IDENTIFIER_TYPE_HD_STATION_ID_EXT} identifiers may be not
+     * globally unique. To provide a best-effort solution, the station’s
+     * broadcast antenna containing the latitude and longitude may be
+     * carried as additional identifier and may be used by the tuner hardware
+     * to double-check tuning.
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int IDENTIFIER_TYPE_HD_STATION_LOCATION = 15;
+    /**
      * Primary identifier for vendor-specific radio technology.
      * The value format is determined by a vendor.
      *
@@ -300,6 +415,7 @@
         IDENTIFIER_TYPE_SXM_SERVICE_ID,
         IDENTIFIER_TYPE_SXM_CHANNEL,
         IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
+        IDENTIFIER_TYPE_HD_STATION_LOCATION,
     })
     @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END)
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 8c6083c..237ec01 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -159,12 +160,17 @@
     /**
      * Forces the analog playback for the supporting radio technology.
      *
-     * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
-     * this option. This is purely user choice, ie. does not reflect digital-
+     * <p>User may disable digital playback for FM HD Radio or hybrid FM/DAB with
+     * this option. This is purely user choice, i.e. does not reflect digital-
      * analog handover state managed from the HAL implementation side.
      *
-     * Some radio technologies may not support this, ie. DAB.
+     * <p>Some radio technologies may not support this, i.e. DAB.
+     *
+     * @deprecated Use {@link #CONFIG_FORCE_ANALOG_FM} instead. If {@link #CONFIG_FORCE_ANALOG_FM}
+     * is supported in HAL, {@link RadioTuner#setConfigFlag} and {@link RadioTuner#isConfigFlagSet}
+     * with CONFIG_FORCE_ANALOG will set/get the value of {@link #CONFIG_FORCE_ANALOG_FM}.
      */
+    @Deprecated
     public static final int CONFIG_FORCE_ANALOG = 2;
     /**
      * Forces the digital playback for the supporting radio technology.
@@ -199,6 +205,30 @@
     /** Enables DAB-FM soft-linking (related content). */
     public static final int CONFIG_DAB_FM_SOFT_LINKING = 9;
 
+    /**
+     * Forces the FM analog playback for the supporting radio technology.
+     *
+     * <p>User may disable FM digital playback for FM HD Radio or hybrid FM/DAB
+     * with this option. This is purely user choice, i.e. does not reflect
+     * digital-analog handover state managed from the HAL implementation side.
+     *
+     * <p>Some radio technologies may not support this, i.e. DAB.
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int CONFIG_FORCE_ANALOG_FM = 10;
+
+    /**
+     * Forces the AM analog playback for the supporting radio technology.
+     *
+     * <p>User may disable FM digital playback for AM HD Radio or hybrid AM/DAB
+     * with this option. This is purely user choice, i.e. does not reflect
+     * digital-analog handover state managed from the HAL implementation side.
+     *
+     * <p>Some radio technologies may not support this, i.e. DAB.
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final int CONFIG_FORCE_ANALOG_AM = 11;
+
     /** @hide */
     @IntDef(prefix = { "CONFIG_" }, value = {
         CONFIG_FORCE_MONO,
@@ -210,6 +240,8 @@
         CONFIG_DAB_FM_LINKING,
         CONFIG_DAB_DAB_SOFT_LINKING,
         CONFIG_DAB_FM_SOFT_LINKING,
+        CONFIG_FORCE_ANALOG_FM,
+        CONFIG_FORCE_ANALOG_AM,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ConfigFlag {}
@@ -1441,6 +1473,9 @@
         private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
         private static final int FLAG_TUNED = 1 << 4;
         private static final int FLAG_STEREO = 1 << 5;
+        private static final int FLAG_SIGNAL_ACQUIRED = 1 << 6;
+        private static final int FLAG_HD_SIS_ACQUIRED = 1 << 7;
+        private static final int FLAG_HD_AUDIO_ACQUIRED = 1 << 8;
 
         @NonNull private final ProgramSelector mSelector;
         @Nullable private final ProgramSelector.Identifier mLogicallyTunedTo;
@@ -1595,7 +1630,7 @@
         }
 
         /**
-         * {@code true} if radio stream is not playing, ie. due to bad reception
+         * {@code true} if radio stream is not playing, i.e. due to bad reception
          * conditions or buffering. In this state volume knob MAY be disabled to
          * prevent user increasing volume too much.
          * It does NOT mean the user has muted audio.
@@ -1621,6 +1656,28 @@
         }
 
         /**
+         * @return {@code true} if the signal has been acquired.
+         */
+        @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+        public boolean isSignalAcquired() {
+            return (mInfoFlags & FLAG_SIGNAL_ACQUIRED) != 0;
+        }
+        /**
+         * @return {@code true} if HD Station Information Service (SIS) information is available.
+         */
+        @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+        public boolean isHdSisAvailable() {
+            return (mInfoFlags & FLAG_HD_SIS_ACQUIRED) != 0;
+        }
+        /**
+         * @return {@code true} if HD audio is available.
+         */
+        @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+        public boolean isHdAudioAvailable() {
+            return (mInfoFlags & FLAG_HD_AUDIO_ACQUIRED) != 0;
+        }
+
+        /**
          * Signal quality (as opposed to the name) indication from 0 (no signal)
          * to 100 (excellent)
          * @return the signal quality indication.
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index b7bf783..db14c08 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.radio;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -30,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -142,12 +144,84 @@
     public static final String METADATA_KEY_DAB_COMPONENT_NAME_SHORT =
             "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT";
 
+    /**
+     * Short context description of comment
+     *
+     * <p>Comment could relate to the current audio program content, or it might
+     * be unrelated information that the station chooses to send. It is composed
+     * of short content description and actual text (see NRSC-G200-A and id3v2.3.0
+     * for more info).
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_COMMENT_SHORT_DESCRIPTION =
+            "android.hardware.radio.metadata.COMMENT_SHORT_DESCRIPTION";
+
+    /**
+     * Actual text of comment
+     *
+     * @see #METADATA_KEY_COMMENT_SHORT_DESCRIPTION
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_COMMENT_ACTUAL_TEXT =
+            "android.hardware.radio.metadata.COMMENT_ACTUAL_TEXT";
+
+    /**
+     * Commercial
+     *
+     * <p>Commercial is application specific and generally used to facilitate the
+     * sale of products and services (see NRSC-G200-A and id3v2.3.0 for more info).
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_COMMERCIAL =
+            "android.hardware.radio.metadata.COMMERCIAL";
+
+    /**
+     * Array of Unique File Identifiers
+     *
+     * <p>Unique File Identifier (UFID) can be used to transmit an alphanumeric
+     * identifier of the current content, or of an advertised product or
+     * service (see NRSC-G200-A and id3v2.3.0 for more info).
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_UFIDS = "android.hardware.radio.metadata.UFIDS";
+
+    /**
+     * HD short station name or HD universal short station name
+     *
+     * <p>It can be up to 12 characters (see SY_IDD_1020s for more info).
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_HD_STATION_NAME_SHORT =
+            "android.hardware.radio.metadata.HD_STATION_NAME_SHORT";
+
+    /**
+     * HD long station name, HD station slogan or HD station message
+     *
+     * <p>(see SY_IDD_1020s for more info)
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_HD_STATION_NAME_LONG =
+            "android.hardware.radio.metadata.HD_STATION_NAME_LONG";
+
+    /**
+     * Bit mask of all HD Radio subchannels available
+     *
+     * <p>Bit {@link ProgramSelector#SUB_CHANNEL_HD_1} from LSB represents the
+     * availability of HD-1 subchannel (main program service, MPS). Bits
+     * {@link ProgramSelector#SUB_CHANNEL_HD_2} to {@link ProgramSelector#SUB_CHANNEL_HD_8}
+     * from LSB represent HD-2 to HD-8 subchannel (supplemental program services, SPS)
+     * respectively.
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    public static final String METADATA_KEY_HD_SUBCHANNELS_AVAILABLE =
+            "android.hardware.radio.metadata.HD_SUBCHANNELS_AVAILABLE";
 
     private static final int METADATA_TYPE_INVALID = -1;
     private static final int METADATA_TYPE_INT = 0;
     private static final int METADATA_TYPE_TEXT = 1;
     private static final int METADATA_TYPE_BITMAP = 2;
     private static final int METADATA_TYPE_CLOCK = 3;
+    private static final int METADATA_TYPE_TEXT_ARRAY = 4;
 
     private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
 
@@ -172,6 +246,13 @@
         METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME_SHORT, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME_SHORT, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMMENT_SHORT_DESCRIPTION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMMENT_ACTUAL_TEXT, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMMERCIAL, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_UFIDS, METADATA_TYPE_TEXT_ARRAY);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_HD_STATION_NAME_SHORT, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_HD_STATION_NAME_LONG, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_HD_SUBCHANNELS_AVAILABLE, METADATA_TYPE_INT);
     }
 
     // keep in sync with: system/media/radio/include/system/radio_metadata.h
@@ -288,9 +369,12 @@
             return false;
         }
         for (String key : mBundle.keySet()) {
-            // This logic will return a false negative if we ever put Bundles into mBundle. As of
-            // 2019-04-09, we only put ints, Strings, and Parcelables in, so it's fine for now.
-            if (!mBundle.get(key).equals(otherBundle.get(key))) {
+            if (Flags.hdRadioImproved() && Objects.equals(METADATA_KEYS_TYPE.get(key),
+                    METADATA_TYPE_TEXT_ARRAY)) {
+                if (!Arrays.equals(mBundle.getStringArray(key), otherBundle.getStringArray(key))) {
+                    return false;
+                }
+            } else if (!Objects.equals(mBundle.get(key), otherBundle.get(key))) {
                 return false;
             }
         }
@@ -326,7 +410,21 @@
 
             sb.append(keyDisp);
             sb.append('=');
-            sb.append(mBundle.get(key));
+            if (Flags.hdRadioImproved() && Objects.equals(METADATA_KEYS_TYPE.get(key),
+                    METADATA_TYPE_TEXT_ARRAY)) {
+                String[] stringArrayValue = mBundle.getStringArray(key);
+                sb.append('[');
+                for (int i = 0; i < stringArrayValue.length; i++) {
+                    if (i != 0) {
+                        sb.append(',');
+                    }
+                    sb.append(stringArrayValue[i]);
+                }
+                sb.append(']');
+            } else {
+                sb.append(mBundle.get(key));
+            }
+
         }
 
         sb.append("]");
@@ -427,6 +525,36 @@
         return clock;
     }
 
+    /**
+     * Gets the string array value associated with the given key as a string
+     * array.
+     *
+     * <p>Only string array keys may be used with this method:
+     * <ul>
+     * <li>{@link #METADATA_KEY_UFIDS}</li>
+     * </ul>
+     *
+     * @param key The key the value is stored under
+     * @return String array of the given string-array-type key
+     * @throws NullPointerException if metadata key is {@code null}
+     * @throws IllegalArgumentException if the metadata with the key is not found in
+     * metadata or the key is not of string-array type
+     */
+    @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+    @NonNull
+    public String[] getStringArray(@NonNull String key) {
+        Objects.requireNonNull(key, "Metadata key can not be null");
+        if (!Objects.equals(METADATA_KEYS_TYPE.get(key), METADATA_TYPE_TEXT_ARRAY)) {
+            throw new IllegalArgumentException("Failed to retrieve key " + key
+                    + " as string array");
+        }
+        String[] stringArrayValue = mBundle.getStringArray(key);
+        if (stringArrayValue == null) {
+            throw new IllegalArgumentException("Key " + key + " is not found in metadata");
+        }
+        return stringArrayValue;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -539,6 +667,11 @@
          * <li>{@link #METADATA_KEY_ARTIST}</li>
          * <li>{@link #METADATA_KEY_ALBUM}</li>
          * <li>{@link #METADATA_KEY_GENRE}</li>
+         * <li>{@link #METADATA_KEY_COMMENT_SHORT_DESCRIPTION}</li>
+         * <li>{@link #METADATA_KEY_COMMENT_ACTUAL_TEXT}</li>
+         * <li>{@link #METADATA_KEY_COMMERCIAL}</li>
+         * <li>{@link #METADATA_KEY_HD_STATION_NAME_SHORT}</li>
+         * <li>{@link #METADATA_KEY_HD_STATION_NAME_LONG}</li>
          * </ul>
          *
          * @param key The key for referencing this value
@@ -563,6 +696,7 @@
          * <li>{@link #METADATA_KEY_RDS_PI}</li>
          * <li>{@link #METADATA_KEY_RDS_PTY}</li>
          * <li>{@link #METADATA_KEY_RBDS_PTY}</li>
+         * <li>{@link #METADATA_KEY_HD_SUBCHANNELS_AVAILABLE}</li>
          * </ul>
          * or any bitmap represented by its identifier.
          *
@@ -621,6 +755,35 @@
         }
 
         /**
+         * Put a String array into the meta data. Custom keys may be used, but if
+         * the METADATA_KEYs defined in this class are used they may only be one
+         * of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_UFIDS}</li>
+         * </ul>
+         *
+         * @param key The key for referencing this value
+         * @param value The String value to store
+         * @return the same Builder instance
+         * @throws NullPointerException if key or value is null
+         * @throws IllegalArgumentException if the key is not string-array-type key
+         */
+        @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
+        @NonNull
+        public Builder putStringArray(@NonNull String key, @NonNull String[] value) {
+            Objects.requireNonNull(key, "Key can not be null");
+            Objects.requireNonNull(value, "Value can not be null");
+            if (!METADATA_KEYS_TYPE.containsKey(key)
+                    || !Objects.equals(METADATA_KEYS_TYPE.get(key), METADATA_TYPE_TEXT_ARRAY)) {
+                throw new IllegalArgumentException("The " + key
+                        + " key cannot be used to put a RadioMetadata String Array.");
+            }
+            mBundle.putStringArray(key, value);
+            return this;
+        }
+
+
+        /**
          * Creates a {@link RadioMetadata} instance with the specified fields.
          *
          * @return a new {@link RadioMetadata} object
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index bdbca91..ba31ca3 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -363,7 +363,7 @@
     @Override
     public boolean isConfigFlagSet(@RadioManager.ConfigFlag int flag) {
         try {
-            return mTuner.isConfigFlagSet(flag);
+            return mTuner.isConfigFlagSet(convertForceAnalogConfigFlag(flag));
         } catch (RemoteException e) {
             throw new RuntimeException("Service died", e);
         }
@@ -372,7 +372,7 @@
     @Override
     public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) {
         try {
-            mTuner.setConfigFlag(flag, value);
+            mTuner.setConfigFlag(convertForceAnalogConfigFlag(flag), value);
         } catch (RemoteException e) {
             throw new RuntimeException("Service died", e);
         }
@@ -411,4 +411,13 @@
             return false;
         }
     }
+
+    private @RadioManager.ConfigFlag int convertForceAnalogConfigFlag(
+            @RadioManager.ConfigFlag int flag) throws RemoteException {
+        if (Flags.hdRadioImproved() && flag == RadioManager.CONFIG_FORCE_ANALOG
+                && mTuner.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM)) {
+            flag = RadioManager.CONFIG_FORCE_ANALOG_FM;
+        }
+        return flag;
+    }
 }
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index a40fb15..66e3c28 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,10 +16,12 @@
 package android.net.vcn;
 
 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.Flags.FLAG_SAFE_MODE_CONFIG;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -235,6 +237,9 @@
             "mMinUdpPort4500NatTimeoutSeconds";
     private final int mMinUdpPort4500NatTimeoutSeconds;
 
+    private static final String IS_SAFE_MODE_DISABLED_KEY = "mIsSafeModeDisabled";
+    private final boolean mIsSafeModeDisabled;
+
     private static final String GATEWAY_OPTIONS_KEY = "mGatewayOptions";
     @NonNull private final Set<Integer> mGatewayOptions;
 
@@ -247,6 +252,7 @@
             @NonNull long[] retryIntervalsMs,
             @IntRange(from = MIN_MTU_V6) int maxMtu,
             @NonNull int minUdpPort4500NatTimeoutSeconds,
+            boolean isSafeModeDisabled,
             @NonNull Set<Integer> gatewayOptions) {
         mGatewayConnectionName = gatewayConnectionName;
         mTunnelConnectionParams = tunnelConnectionParams;
@@ -255,6 +261,7 @@
         mMaxMtu = maxMtu;
         mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
         mGatewayOptions = Collections.unmodifiableSet(new ArraySet(gatewayOptions));
+        mIsSafeModeDisabled = isSafeModeDisabled;
 
         mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
         if (mUnderlyingNetworkTemplates.isEmpty()) {
@@ -317,6 +324,7 @@
                 in.getInt(
                         MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY,
                         MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
+        mIsSafeModeDisabled = in.getBoolean(IS_SAFE_MODE_DISABLED_KEY);
 
         validate();
     }
@@ -483,6 +491,17 @@
     }
 
     /**
+     * Check whether safe mode is enabled
+     *
+     * @see Builder#enableSafeMode(boolean)
+     * @hide
+     */
+    @FlaggedApi(FLAG_SAFE_MODE_CONFIG)
+    public boolean isSafeModeEnabled() {
+        return !mIsSafeModeDisabled;
+    }
+
+    /**
      * Checks if the given VCN gateway option is enabled.
      *
      * @param option the option to check.
@@ -528,6 +547,7 @@
         result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
         result.putInt(MAX_MTU_KEY, mMaxMtu);
         result.putInt(MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY, mMinUdpPort4500NatTimeoutSeconds);
+        result.putBoolean(IS_SAFE_MODE_DISABLED_KEY, mIsSafeModeDisabled);
 
         return result;
     }
@@ -542,6 +562,7 @@
                 Arrays.hashCode(mRetryIntervalsMs),
                 mMaxMtu,
                 mMinUdpPort4500NatTimeoutSeconds,
+                mIsSafeModeDisabled,
                 mGatewayOptions);
     }
 
@@ -559,6 +580,7 @@
                 && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
                 && mMaxMtu == rhs.mMaxMtu
                 && mMinUdpPort4500NatTimeoutSeconds == rhs.mMinUdpPort4500NatTimeoutSeconds
+                && mIsSafeModeDisabled == rhs.mIsSafeModeDisabled
                 && mGatewayOptions.equals(rhs.mGatewayOptions);
     }
 
@@ -577,6 +599,7 @@
         @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
         private int mMaxMtu = DEFAULT_MAX_MTU;
         private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
+        private boolean mIsSafeModeDisabled = false;
 
         @NonNull private final Set<Integer> mGatewayOptions = new ArraySet<>();
 
@@ -789,6 +812,28 @@
         }
 
         /**
+         * Enable/disable safe mode
+         *
+         * <p>If a VCN fails to provide connectivity within a system-provided timeout, it will enter
+         * safe mode. In safe mode, the VCN Network will be torn down and the system will restore
+         * connectivity by allowing underlying cellular networks to be used as default. At the same
+         * time, VCN will continue to retry until it succeeds.
+         *
+         * <p>When safe mode is disabled and VCN connection fails to provide connectivity, end users
+         * might not have connectivity, and may not have access to carrier-owned underlying
+         * networks.
+         *
+         * @param enabled whether safe mode should be enabled. Defaults to {@code true}
+         * @hide
+         */
+        @FlaggedApi(FLAG_SAFE_MODE_CONFIG)
+        @NonNull
+        public Builder enableSafeMode(boolean enabled) {
+            mIsSafeModeDisabled = !enabled;
+            return this;
+        }
+
+        /**
          * Builds and validates the VcnGatewayConnectionConfig.
          *
          * @return an immutable VcnGatewayConnectionConfig instance
@@ -803,6 +848,7 @@
                     mRetryIntervalsMs,
                     mMaxMtu,
                     mMinUdpPort4500NatTimeoutSeconds,
+                    mIsSafeModeDisabled,
                     mGatewayOptions);
         }
     }
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
new file mode 100644
index 0000000..6956916
--- /dev/null
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.net.vcn"
+
+flag {
+    name: "safe_mode_config"
+    namespace: "vcn"
+    description: "Feature flag for safe mode configurability"
+    bug: "276358140"
+}
\ No newline at end of file
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 58def6e..960e84d 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -26,6 +26,7 @@
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -280,8 +281,8 @@
      *
      * <p>{@link BugreportManager} takes ownership of {@code bugreportFd}.
      *
-     * <p>The caller may only request to retrieve a given bugreport once. Subsequent calls will fail
-     * with error code {@link BugreportCallback#BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE}.
+     * <p>The caller can reattempt to retrieve the bugreport multiple times if the user has not
+     * consented on previous attempts.
      *
      * @param bugreportFile the identifier for a bugreport that was previously generated for this
      *      caller using {@code startBugreport}.
@@ -294,6 +295,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.DUMP)
     @WorkerThread
+    @UserHandleAware
     public void retrieveBugreport(
             @NonNull String bugreportFile,
             @NonNull ParcelFileDescriptor bugreportFd,
@@ -307,8 +309,10 @@
             Preconditions.checkNotNull(callback);
             DumpstateListener dsListener = new DumpstateListener(executor, callback, false, false);
             mBinder.retrieveBugreport(Binder.getCallingUid(), mContext.getOpPackageName(),
+                    mContext.getUserId(),
                     bugreportFd.getFileDescriptor(),
                     bugreportFile,
+                    /* keepBugreportOnRetrieval = */ false,
                     dsListener);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index e8ad303..8510084 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -20,6 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -128,6 +129,8 @@
      *
      * @hide
      */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED)
     public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
 
     /**
@@ -145,7 +148,8 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "BUGREPORT_FLAG_" }, value = {
             BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA,
-            BUGREPORT_FLAG_DEFER_CONSENT
+            BUGREPORT_FLAG_DEFER_CONSENT,
+            BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL
     })
     public @interface BugreportFlag {}
 
@@ -165,4 +169,20 @@
      * String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
      */
     public static final int BUGREPORT_FLAG_DEFER_CONSENT = IDumpstate.BUGREPORT_FLAG_DEFER_CONSENT;
+
+    /**
+     * Flag for keeping a bugreport stored even after it has been retrieved via
+     * {@link BugreportManager#retrieveBugreport}.
+     *
+     * <p>This flag can only be used when {@link #BUGREPORT_FLAG_DEFER_CONSENT} is set.
+     * The bugreport may be retrieved multiple times using
+     * {@link BugreportManager#retrieveBugreport(
+     * String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
+     *
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED)
+    public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL =
+            IDumpstate.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL;
 }
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index cf35460..a1d2dcc 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -17,7 +17,7 @@
 package android.os;
 
 import static android.Manifest.permission.PACKAGE_USAGE_STATS;
-import static android.Manifest.permission.READ_LOGS;
+import static android.Manifest.permission.READ_DROPBOX_DATA;
 
 import android.annotation.BytesLong;
 import android.annotation.CurrentTimeMillisLong;
@@ -81,9 +81,12 @@
 
     /**
      * Broadcast Action: This is broadcast when a new entry is added in the dropbox.
-     * You must hold the {@link android.Manifest.permission#READ_LOGS} permission
-     * in order to receive this broadcast. This broadcast can be rate limited for low priority
-     * entries
+     * For apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and later, you
+     * must hold the {@link android.Manifest.permission#READ_DROPBOX_DATA} permission
+     * in order to receive this broadcast. For apps targeting Android versions lower
+     * than {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, you must hold
+     * {@link android.Manifest.permission#READ_LOGS}.
+     * This broadcast can be rate limited for low priority entries
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
@@ -382,12 +385,17 @@
     /**
      * Gets the next entry from the drop box <em>after</em> the specified time.
      * You must always call {@link Entry#close()} on the return value!
+     * {@link android.Manifest.permission#READ_DROPBOX_DATA} permission is
+     * required for apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+     * and later. {@link android.Manifest.permission#READ_LOGS} permission is
+     * required for apps targeting Android versions lower than
+     * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}.
      *
      * @param tag of entry to look for, null for all tags
      * @param msec time of the last entry seen
      * @return the next entry, or null if there are no more entries
      */
-    @RequiresPermission(allOf = { READ_LOGS, PACKAGE_USAGE_STATS })
+    @RequiresPermission(allOf = { READ_DROPBOX_DATA, PACKAGE_USAGE_STATS })
     public @Nullable Entry getNextEntry(String tag, long msec) {
         try {
             return mService.getNextEntryWithAttribution(tag, msec, mContext.getOpPackageName(),
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 839c56f..330b992 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -137,6 +137,7 @@
     boolean isUserVisible(int userId);
     int[] getVisibleUsers();
     int getMainDisplayIdAssignedToUser();
+    boolean isForegroundUserAdmin();
     boolean isUserNameSet(int userId);
     boolean hasRestrictedProfiles(int userId);
     boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index cbc9213..11084b8 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -231,6 +232,7 @@
          *
          * @param enabled The flag that sets whether this session uses power-efficient scheduling.
          */
+        @FlaggedApi(Flags.FLAG_ADPF_PREFER_POWER_EFFICIENCY)
         public void setPreferPowerEfficiency(boolean enabled) {
             nativeSetPreferPowerEfficiency(mNativeSessionPtr, enabled);
         }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index fce715a..d2c1755 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.FlaggedApi;
 import android.Manifest.permission;
 import android.annotation.CallbackExecutor;
 import android.annotation.CurrentTimeMillisLong;
@@ -1940,6 +1941,7 @@
      *
      * @hide
      */
+    @FlaggedApi(android.os.Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API)
     @TestApi
     public boolean isBatterySaverSupported() {
         try {
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 81d4e3a..47b6d8d 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2367,14 +2367,14 @@
     }
 
     /** Assume locked until we hear otherwise */
-    private static volatile boolean sUserKeyUnlocked = false;
+    private static volatile boolean sCeStorageUnlocked = false;
 
-    private static boolean isUserKeyUnlocked(int userId) {
+    private static boolean isCeStorageUnlocked(int userId) {
         final IStorageManager storage = IStorageManager.Stub
                 .asInterface(ServiceManager.getService("mount"));
         if (storage != null) {
             try {
-                return storage.isUserKeyUnlocked(userId);
+                return storage.isCeStorageUnlocked(userId);
             } catch (RemoteException ignored) {
             }
         }
@@ -2387,13 +2387,13 @@
         // since any relocking of that user will always result in our
         // process being killed to release any CE FDs we're holding onto.
         if (userId == UserHandle.myUserId()) {
-            if (sUserKeyUnlocked) {
+            if (sCeStorageUnlocked) {
                 return;
-            } else if (isUserKeyUnlocked(userId)) {
-                sUserKeyUnlocked = true;
+            } else if (isCeStorageUnlocked(userId)) {
+                sCeStorageUnlocked = true;
                 return;
             }
-        } else if (isUserKeyUnlocked(userId)) {
+        } else if (isCeStorageUnlocked(userId)) {
             return;
         }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 72bc211..cc79ae3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2774,6 +2774,8 @@
      * Returns the designated "communal profile" of the device, or {@code null} if there is none.
      * @hide
      */
+    @FlaggedApi(android.multiuser.Flags.FLAG_SUPPORT_COMMUNAL_PROFILE)
+    @TestApi
     @RequiresPermission(anyOf = {
             Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS,
@@ -2791,16 +2793,33 @@
     }
 
     /**
+     * Checks if the context user is running in a communal profile.
+     *
+     * A communal profile is a {@link #isProfile() profile}, but instead of being associated with a
+     * particular parent user, it is communal to the device.
+     *
+     * @return whether the context user is a communal profile.
+     */
+    @FlaggedApi(android.multiuser.Flags.FLAG_SUPPORT_COMMUNAL_PROFILE)
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public boolean isCommunalProfile() {
+        return isCommunalProfile(mUserId);
+    }
+
+    /**
      * Returns {@code true} if the given user is the designated "communal profile" of the device.
      * @hide
      */
     @RequiresPermission(anyOf = {
-            Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS,
-            Manifest.permission.QUERY_USERS})
-    public boolean isCommunalProfile(@UserIdInt int userId) {
-        final UserInfo user = getUserInfo(userId);
-        return user != null && user.isCommunalProfile();
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    private boolean isCommunalProfile(@UserIdInt int userId) {
+        return isUserTypeCommunalProfile(getProfileType(userId));
     }
 
     /**
@@ -2840,6 +2859,23 @@
     }
 
     /**
+     * Used to check if the user currently running in the <b>foreground</b> is an
+     * {@link #isAdminUser() admin} user.
+     *
+     * @return whether the foreground user is an admin user.
+     * @see #isAdminUser()
+     * @see #isUserForeground()
+     */
+    @FlaggedApi(android.multiuser.Flags.FLAG_SUPPORT_COMMUNAL_PROFILE_NEXTGEN)
+    public boolean isForegroundUserAdmin() {
+        try {
+            return mService.isForegroundUserAdmin();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns whether the context user is of the given user type.
      *
      * @param userType the name of the user's user type, e.g.
@@ -2997,7 +3033,7 @@
     }
 
     /**
-     * Checks if the calling context user can have a restricted profile.
+     * Checks if the context user can have a restricted profile.
      * @return whether the context user can have a restricted profile.
      * @hide
      */
@@ -3101,11 +3137,11 @@
     }
 
     /**
-     * Checks if the calling context user is running in a profile. A profile is a user that
+     * Checks if the context user is running in a profile. A profile is a user that
      * typically has its own separate data but shares its UI with some parent user. For example, a
      * {@link #isManagedProfile() managed profile} is a type of profile.
      *
-     * @return whether the caller is in a profile.
+     * @return whether the context user is in a profile.
      */
     @UserHandleAware(
             requiresAnyOfPermissionsIfNotCallerProfileGroup = {
@@ -5247,7 +5283,7 @@
 
     /**
      * Returns the parent of the profile which this method is called from
-     * or null if called from a user that is not a profile.
+     * or null if it has no parent (e.g. if it is not a profile).
      *
      * @hide
      */
@@ -5269,7 +5305,7 @@
      *
      * @param user the handle of the user profile
      *
-     * @return the parent of the user or {@code null} if the user is not profile
+     * @return the parent of the user or {@code null} if the user has no parent
      *
      * @hide
      */
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index c4521c0..86f03cd 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -34,3 +34,17 @@
     description: "Introduce a constant as maximum value of bugreport mode."
     bug: "305067125"
 }
+
+flag {
+    name: "adpf_prefer_power_efficiency"
+    namespace: "game"
+    description: "Guards the ADPF power efficiency API"
+    bug: "288117936"
+}
+
+flag {
+    name: "battery_saver_supported_check_api"
+    namespace: "backstage_power"
+    description: "Guards a new API in PowerManager to check if battery saver is supported or not."
+    bug: "305067031"
+}
\ No newline at end of file
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 369a193..3ecf74e 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -134,20 +134,20 @@
     @EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS")
     void setDebugFlags(int flags, int mask) = 60;
     @EnforcePermission("STORAGE_INTERNAL")
-    void createUserKey(int userId, int serialNumber, boolean ephemeral) = 61;
+    void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) = 61;
     @EnforcePermission("STORAGE_INTERNAL")
-    void destroyUserKey(int userId) = 62;
+    void destroyUserStorageKeys(int userId) = 62;
     @EnforcePermission("STORAGE_INTERNAL")
-    void unlockUserKey(int userId, int serialNumber, in byte[] secret) = 63;
+    void unlockCeStorage(int userId, int serialNumber, in byte[] secret) = 63;
     @EnforcePermission("STORAGE_INTERNAL")
-    void lockUserKey(int userId) = 64;
-    boolean isUserKeyUnlocked(int userId) = 65;
+    void lockCeStorage(int userId) = 64;
+    boolean isCeStorageUnlocked(int userId) = 65;
     @EnforcePermission("STORAGE_INTERNAL")
     void prepareUserStorage(in String volumeUuid, int userId, int serialNumber, int flags) = 66;
     @EnforcePermission("STORAGE_INTERNAL")
     void destroyUserStorage(in String volumeUuid, int userId, int flags) = 67;
     @EnforcePermission("STORAGE_INTERNAL")
-    void setUserKeyProtection(int userId, in byte[] secret) = 70;
+    void setCeStorageProtection(int userId, in byte[] secret) = 70;
     @EnforcePermission("MOUNT_FORMAT_FILESYSTEMS")
     void fstrim(int flags, IVoldTaskListener listener) = 72;
     AppFuseMount mountProxyFileDescriptorBridge() = 73;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index ee387e7..2d1802a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1589,28 +1589,64 @@
                 DEFAULT_FULL_THRESHOLD_BYTES);
     }
 
-    /** {@hide} */
-    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
+    /**
+     * Creates the keys for a user's credential-encrypted (CE) and device-encrypted (DE) storage.
+     * <p>
+     * This creates the user's CE key and DE key for internal storage, then adds them to the kernel.
+     * Then, if the user is not ephemeral, this stores the DE key (encrypted) on flash.  (The CE key
+     * is not stored until {@link IStorageManager#setCeStorageProtection()}.)
+     * <p>
+     * This does not create the CE and DE directories themselves.  For that, see {@link
+     * #prepareUserStorage()}.
+     * <p>
+     * This is only intended to be called by UserManagerService, as part of creating a user.
+     *
+     * @param userId ID of the user
+     * @param serialNumber serial number of the user
+     * @param ephemeral whether the user is ephemeral
+     * @throws RuntimeException on error.  The user's keys already existing is considered an error.
+     * @hide
+     */
+    public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) {
         try {
-            mStorageManager.createUserKey(userId, serialNumber, ephemeral);
+            mStorageManager.createUserStorageKeys(userId, serialNumber, ephemeral);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /** {@hide} */
-    public void destroyUserKey(int userId) {
+    /**
+     * Destroys the keys for a user's credential-encrypted (CE) and device-encrypted (DE) storage.
+     * <p>
+     * This evicts the keys from the kernel (if present), which "locks" the corresponding
+     * directories.  Then, this deletes the encrypted keys from flash.  This operates on all the
+     * user's CE and DE keys, for both internal and adoptable storage.
+     * <p>
+     * This does not destroy the CE and DE directories themselves.  For that, see {@link
+     * #destroyUserStorage()}.
+     * <p>
+     * This is only intended to be called by UserManagerService, as part of removing a user.
+     *
+     * @param userId ID of the user
+     * @throws RuntimeException on error.  On error, as many things as possible are still destroyed.
+     * @hide
+     */
+    public void destroyUserStorageKeys(int userId) {
         try {
-            mStorageManager.destroyUserKey(userId);
+            mStorageManager.destroyUserStorageKeys(userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /** {@hide} */
-    public void lockUserKey(int userId) {
+    /**
+     * Locks the user's credential-encrypted (CE) storage.
+     *
+     * @hide
+     */
+    public void lockCeStorage(int userId) {
         try {
-            mStorageManager.lockUserKey(userId);
+            mStorageManager.lockCeStorage(userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1637,17 +1673,26 @@
     /** {@hide} */
     @TestApi
     public static boolean isUserKeyUnlocked(int userId) {
+        return isCeStorageUnlocked(userId);
+    }
+
+    /**
+     * Returns true if the user's credential-encrypted (CE) storage is unlocked.
+     *
+     * @hide
+     */
+    public static boolean isCeStorageUnlocked(int userId) {
         if (sStorageManager == null) {
             sStorageManager = IStorageManager.Stub
                     .asInterface(ServiceManager.getService("mount"));
         }
         if (sStorageManager == null) {
-            Slog.w(TAG, "Early during boot, assuming locked");
+            Slog.w(TAG, "Early during boot, assuming CE storage is locked");
             return false;
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            return sStorageManager.isUserKeyUnlocked(userId);
+            return sStorageManager.isCeStorageUnlocked(userId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         } finally {
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 3f06a91..0798f65 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -35,3 +35,10 @@
     description: "enable the shouldRegisterAttributionSource API"
     bug: "305057691"
 }
+
+flag {
+  name: "attribution_source_constructor"
+  namespace: "permissions"
+  description: "enable AttributionSource(int, int, String, String, IBinder, String[], AttributionSource)"
+  bug: "304478648"
+}
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f0906b1..a2f1ce1e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11653,6 +11653,15 @@
                 "accessibility_magnification_joystick_enabled";
 
         /**
+         * Setting that specifies whether the display magnification is enabled via a system-wide
+         * two fingers triple tap gesture.
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED =
+                "accessibility_magnification_two_finger_triple_tap_enabled";
+
+        /**
          * Controls magnification enable gesture. Accessibility magnification can have one or more
          * enable gestures.
          *
@@ -12058,6 +12067,13 @@
         public static final String DND_CONFIGS_MIGRATED = "dnd_settings_migrated";
 
         /**
+         * Controls whether to hide private space entry point in All Apps
+         *
+         * @hide
+         */
+        public static final String HIDE_PRIVATESPACE_ENTRY_POINT = "hide_privatespace_entry_point";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 2931435..52d4d47 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -7,3 +7,11 @@
   bug: "284297289"
 }
 
+flag {
+  name: "notification_lifetime_extension_refactor"
+    namespace: "systemui"
+    description: "Enables moving notification lifetime extension management from SystemUI to "
+        "Notification Manager Service"
+    bug: "299448097"
+}
+
diff --git a/core/java/android/service/voice/AbstractDetector.java b/core/java/android/service/voice/AbstractDetector.java
index db97d4f..dfb1361 100644
--- a/core/java/android/service/voice/AbstractDetector.java
+++ b/core/java/android/service/voice/AbstractDetector.java
@@ -263,5 +263,12 @@
                         result != null ? result : new HotwordRejectedResult.Builder().build());
             }));
         }
+
+        @Override
+        public void onTrainingData(HotwordTrainingData data) {
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
+                mCallback.onTrainingData(data);
+            }));
+        }
     }
 }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 6a82f6d..875031f 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -306,6 +306,7 @@
     private static final int MSG_DETECTION_HOTWORD_DETECTION_SERVICE_FAILURE = 9;
     private static final int MSG_DETECTION_SOUND_TRIGGER_FAILURE = 10;
     private static final int MSG_DETECTION_UNKNOWN_FAILURE = 11;
+    private static final int MSG_HOTWORD_TRAINING_DATA = 12;
 
     private final String mText;
     private final Locale mLocale;
@@ -1653,6 +1654,16 @@
         }
 
         @Override
+        public void onTrainingData(@NonNull HotwordTrainingData data) {
+            if (DBG) {
+                Slog.d(TAG, "onTrainingData(" + data + ")");
+            } else {
+                Slog.i(TAG, "onTrainingData");
+            }
+            Message.obtain(mHandler, MSG_HOTWORD_TRAINING_DATA, data).sendToTarget();
+        }
+
+        @Override
         public void onHotwordDetectionServiceFailure(
                 HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
             Slog.v(TAG, "onHotwordDetectionServiceFailure: " + hotwordDetectionServiceFailure);
@@ -1783,6 +1794,9 @@
                     case MSG_DETECTION_UNKNOWN_FAILURE:
                         mExternalCallback.onUnknownFailure((String) message.obj);
                         break;
+                    case MSG_HOTWORD_TRAINING_DATA:
+                        mExternalCallback.onTrainingData((HotwordTrainingData) message.obj);
+                        break;
                     default:
                         super.handleMessage(message);
                 }
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index ccf8b67..13b6a9a 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -39,6 +40,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SharedMemory;
+import android.service.voice.flags.Flags;
 import android.speech.IRecognitionServiceManager;
 import android.util.Log;
 import android.view.contentcapture.ContentCaptureManager;
@@ -443,5 +445,30 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+
+        /**
+         * Informs the {@link HotwordDetector} when there is training data.
+         *
+         * <p> A daily limit of 20 is enforced on training data events sent. Number events egressed
+         * are tracked across UTC day (24-hour window) and count is reset at midnight
+         * (UTC 00:00:00). To be informed of failures to egress training data due to limit being
+         * reached, the associated hotword detector should listen for
+         * {@link HotwordDetectionServiceFailure#ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED}
+         * events in {@link HotwordDetector.Callback#onFailure(HotwordDetectionServiceFailure)}.
+         *
+         * @param data Training data determined by the service. This is provided to the
+         *               {@link HotwordDetector}.
+         */
+        @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+        public void onTrainingData(@NonNull HotwordTrainingData data) {
+            requireNonNull(data);
+            try {
+                Log.d(TAG, "onTrainingData");
+                mRemoteCallback.onTrainingData(data);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
     }
 }
diff --git a/core/java/android/service/voice/HotwordDetectionServiceFailure.java b/core/java/android/service/voice/HotwordDetectionServiceFailure.java
index 5cf245d..420dac1 100644
--- a/core/java/android/service/voice/HotwordDetectionServiceFailure.java
+++ b/core/java/android/service/voice/HotwordDetectionServiceFailure.java
@@ -16,12 +16,14 @@
 
 package android.service.voice;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.voice.flags.Flags;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
@@ -79,6 +81,14 @@
      */
     public static final int ERROR_CODE_REMOTE_EXCEPTION = 7;
 
+    /** Indicates failure to egress training data due to limit being exceeded. */
+    @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+    public static final int ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED = 8;
+
+    /** Indicates failure to egress training data due to security exception. */
+    @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+    public static final int ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION = 9;
+
     /**
      * @hide
      */
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 32a93ee..16a6dbe 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
 import static android.Manifest.permission.RECORD_AUDIO;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -27,6 +28,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.SharedMemory;
+import android.service.voice.flags.Flags;
 
 import java.io.PrintWriter;
 
@@ -244,6 +246,19 @@
         void onRejected(@NonNull HotwordRejectedResult result);
 
         /**
+         * Called by the {@link HotwordDetectionService} to egress training data to the
+         * {@link HotwordDetector}. This data can be used for improving and analyzing hotword
+         * detection models.
+         *
+         * @param data Training data to be egressed provided by the
+         *               {@link HotwordDetectionService}.
+         */
+        @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+        default void onTrainingData(@NonNull HotwordTrainingData data) {
+            return;
+        }
+
+        /**
          * Called when the {@link HotwordDetectionService} or {@link VisualQueryDetectionService} is
          * created by the system and given a short amount of time to report their initialization
          * state.
diff --git a/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java b/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java
new file mode 100644
index 0000000..7eb5280
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 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.service.voice;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Enforces daily limits on the egress of {@link HotwordTrainingData} from the hotword detection
+ * service.
+ *
+ * <p> Egress is tracked across UTC day (24-hour window) and count is reset at
+ * midnight (UTC 00:00:00).
+ *
+ * @hide
+ */
+public class HotwordTrainingDataLimitEnforcer {
+    private static final String TAG = "HotwordTrainingDataLimitEnforcer";
+
+    /**
+     * Number of hotword training data events that are allowed to be egressed per day.
+     */
+    private static final int TRAINING_DATA_EGRESS_LIMIT = 20;
+
+    /**
+     * Name of hotword training data limit shared preference.
+     */
+    private static final String TRAINING_DATA_LIMIT_SHARED_PREF = "TrainingDataSharedPref";
+
+    /**
+     * Key for date associated with
+     * {@link HotwordTrainingDataLimitEnforcer#TRAINING_DATA_EGRESS_COUNT}.
+     */
+    private static final String TRAINING_DATA_EGRESS_DATE = "TRAINING_DATA_EGRESS_DATE";
+
+    /**
+     * Key for number of hotword training data events egressed on
+     * {@link HotwordTrainingDataLimitEnforcer#TRAINING_DATA_EGRESS_DATE}.
+     */
+    private static final String TRAINING_DATA_EGRESS_COUNT = "TRAINING_DATA_EGRESS_COUNT";
+
+    private SharedPreferences mSharedPreferences;
+
+    private static final Object INSTANCE_LOCK = new Object();
+    private final Object mTrainingDataIncrementLock = new Object();
+
+    private static HotwordTrainingDataLimitEnforcer sInstance;
+
+    /** Get singleton HotwordTrainingDataLimitEnforcer instance. */
+    public static @NonNull HotwordTrainingDataLimitEnforcer getInstance(@NonNull Context context) {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new HotwordTrainingDataLimitEnforcer(context.getApplicationContext());
+            }
+            return sInstance;
+        }
+    }
+
+    private HotwordTrainingDataLimitEnforcer(Context context) {
+        mSharedPreferences = context.getSharedPreferences(
+                new File(Environment.getDataSystemCeDirectory(UserHandle.USER_SYSTEM),
+                        TRAINING_DATA_LIMIT_SHARED_PREF),
+                Context.MODE_PRIVATE);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public void resetTrainingDataEgressCount() {
+        Log.i(TAG, "Resetting training data egress count!");
+        synchronized (mTrainingDataIncrementLock) {
+            // Clear all training data shared preferences.
+            mSharedPreferences.edit().clear().commit();
+        }
+    }
+
+    /**
+     * Increments training data egress count.
+     * <p> If count exceeds daily training data egress limit, returns false. Else, will return true.
+     */
+    public boolean incrementEgressCount() {
+        synchronized (mTrainingDataIncrementLock) {
+            return incrementTrainingDataEgressCountLocked();
+        }
+    }
+
+    private boolean incrementTrainingDataEgressCountLocked() {
+        LocalDate utcDate = LocalDate.now(ZoneOffset.UTC);
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        String currentDate = utcDate.format(formatter);
+
+        String storedDate = mSharedPreferences.getString(TRAINING_DATA_EGRESS_DATE, "");
+        int storedCount = mSharedPreferences.getInt(TRAINING_DATA_EGRESS_COUNT, 0);
+        Log.i(TAG,
+                TextUtils.formatSimple("There are %s hotword training data events egressed for %s",
+                        storedCount, storedDate));
+
+        SharedPreferences.Editor editor = mSharedPreferences.edit();
+
+        // If date has not changed from last training data event, increment counter if within
+        // limit.
+        if (storedDate.equals(currentDate)) {
+            if (storedCount < TRAINING_DATA_EGRESS_LIMIT) {
+                Log.i(TAG, "Within hotword training data egress limit, incrementing...");
+                editor.putInt(TRAINING_DATA_EGRESS_COUNT, storedCount + 1);
+                editor.commit();
+                return true;
+            }
+            Log.i(TAG, "Exceeded hotword training data egress limit.");
+            return false;
+        }
+
+        // If date has changed, reset.
+        Log.i(TAG, TextUtils.formatSimple(
+                "Stored date %s is different from current data %s. Resetting counters...",
+                storedDate, currentDate));
+
+        editor.putString(TRAINING_DATA_EGRESS_DATE, currentDate);
+        editor.putInt(TRAINING_DATA_EGRESS_COUNT, 1);
+        editor.commit();
+        return true;
+    }
+}
diff --git a/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl b/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl
index c6b10ff..a9c6af79 100644
--- a/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl
+++ b/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl
@@ -18,6 +18,7 @@
 
 import android.service.voice.HotwordDetectedResult;
 import android.service.voice.HotwordRejectedResult;
+import android.service.voice.HotwordTrainingData;
 
 /**
  * Callback for returning the detected result from the HotwordDetectionService.
@@ -37,4 +38,10 @@
      * Sends {@code result} to the HotwordDetector.
      */
     void onRejected(in HotwordRejectedResult result);
+
+    /**
+     * Called by {@link HotwordDetectionService} to egress training data to the
+     * {@link HotwordDetector}.
+     */
+     void onTrainingData(in HotwordTrainingData data);
 }
diff --git a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
index fab830a..6226772 100644
--- a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
+++ b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
@@ -20,6 +20,7 @@
 import android.service.voice.HotwordDetectedResult;
 import android.service.voice.HotwordDetectionServiceFailure;
 import android.service.voice.HotwordRejectedResult;
+import android.service.voice.HotwordTrainingData;
 
 /**
  * Callback for returning the detected result from the HotwordDetectionService.
@@ -47,4 +48,10 @@
      */
     void onRejected(
         in HotwordRejectedResult hotwordRejectedResult);
+
+    /**
+     * Called by {@link HotwordDetectionService} to egress training data to the
+     * {@link HotwordDetector}.
+     */
+     void onTrainingData(in HotwordTrainingData data);
 }
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index f1bc792..2c68fae 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.RECORD_AUDIO;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.hardware.soundtrigger.SoundTrigger;
@@ -201,6 +202,13 @@
                         result != null ? result : new HotwordRejectedResult.Builder().build());
             }));
         }
+
+        @Override
+        public void onTrainingData(@NonNull HotwordTrainingData result) {
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
+                mCallback.onTrainingData(result);
+            }));
+        }
     }
 
     private static class InitializationStateListener
@@ -238,6 +246,13 @@
         }
 
         @Override
+        public void onTrainingData(@NonNull HotwordTrainingData data) {
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onTrainingData event");
+            }
+        }
+
+        @Override
         public void onHotwordDetectionServiceFailure(
                 HotwordDetectionServiceFailure hotwordDetectionServiceFailure)
                 throws RemoteException {
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index b5448d4..91de894 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -369,6 +369,12 @@
                 Slog.i(TAG, "Ignored #onRejected event");
             }
         }
+        @Override
+        public void onTrainingData(HotwordTrainingData data) throws RemoteException {
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onTrainingData event");
+            }
+        }
 
         @Override
         public void onRecognitionPaused() throws RemoteException {
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index d280621..42203d4 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -48,6 +49,7 @@
 import android.os.SharedMemory;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.service.voice.flags.Flags;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -443,6 +445,20 @@
         }
     }
 
+    /** Reset hotword training data egressed count.
+     *  @hide */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+    @RequiresPermission(Manifest.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT)
+    public final void resetHotwordTrainingDataEgressCountForTest() {
+        Log.i(TAG, "Resetting hotword training data egress count for test.");
+        try {
+            mSystemService.resetHotwordTrainingDataEgressCountForTest();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
      * This instance must be retained and used by the client.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 517ae4f..5f6a9bd 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -457,12 +457,21 @@
         } else {
             hyphenationMode = MeasuredText.Builder.HYPHENATION_MODE_NONE;
         }
+        LineBreakConfig config = params.getLineBreakConfig();
+        if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO
+                && pct.getParagraphCount() != 1) {
+            // If the text has multiple paragraph, resolve line break word style auto to none.
+            config = new LineBreakConfig.Builder()
+                    .merge(config)
+                    .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+                    .build();
+        }
         ArrayList<ParagraphInfo> result = new ArrayList<>();
         for (int i = 0; i < pct.getParagraphCount(); ++i) {
             final int paraStart = pct.getParagraphStart(i);
             final int paraEnd = pct.getParagraphEnd(i);
             result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
-                    params.getTextPaint(), params.getLineBreakConfig(), pct, paraStart, paraEnd,
+                    params.getTextPaint(), config, pct, paraStart, paraEnd,
                     params.getTextDirection(), hyphenationMode, computeLayout, true,
                     pct.getMeasuredParagraph(i), null /* no recycle */)));
         }
@@ -489,6 +498,7 @@
             hyphenationMode = MeasuredText.Builder.HYPHENATION_MODE_NONE;
         }
 
+        LineBreakConfig config = null;
         int paraEnd = 0;
         for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
             paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
@@ -500,8 +510,21 @@
                 paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
             }
 
+            if (config == null) {
+                config = params.getLineBreakConfig();
+                if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO
+                        && !(paraStart == start && paraEnd == end)) {
+                    // If the text has multiple paragraph, resolve line break word style auto to
+                    // none.
+                    config = new LineBreakConfig.Builder()
+                            .merge(config)
+                            .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+                            .build();
+                }
+            }
+
             result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
-                    params.getTextPaint(), params.getLineBreakConfig(), text, paraStart, paraEnd,
+                    params.getTextPaint(), config, text, paraStart, paraEnd,
                     params.getTextDirection(), hyphenationMode, computeLayout, computeBounds,
                     null /* no hint */,
                     null /* no recycle */)));
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 201f680..43c38f3 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -61,3 +61,17 @@
   description: "Feature flag for preventing horizontal clipping."
   bug: "63938206"
 }
+
+flag {
+  name: "deprecate_ui_fonts"
+  namespace: "text"
+  description: "Feature flag for deprecating UI fonts. By setting true for this feature flag, the elegant text height of will be turned on by default unless explicitly setting it to false by attribute or Java API call."
+  bug: "279646685"
+}
+
+flag {
+  name: "word_style_auto"
+  namespace: "text"
+  description: "A feature flag that implements line break word style auto."
+  bug: "280005585"
+}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 1ec7c41..17a3a12 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -28,6 +28,7 @@
 import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
 import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
 import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
+import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsController.DEBUG;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
@@ -469,8 +470,10 @@
             }
             addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
 
-            final boolean visible = mPendingFraction == 0 && source != null
-                    ? source.isVisible()
+            // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is animated from
+            // the hidden state.
+            final boolean visible = mPendingFraction == 0
+                    ? mAnimationType != ANIMATION_TYPE_SHOW
                     : !mFinished || mShownOnFinish;
 
             if (outState != null && source != null) {
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 99a7fe5..aad3bf2 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -20,11 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.annotation.UiContext;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -42,15 +40,11 @@
 
 import com.android.internal.R;
 
-import dalvik.system.PathClassLoader;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Objects;
 
@@ -82,12 +76,6 @@
     private static final String TAG = LayoutInflater.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";
-    /**
-     * Whether or not we use the precompiled layout.
-     */
-    private static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
-
     /** Empty stack trace used to avoid log spam in re-throw exceptions. */
     private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
 
@@ -116,13 +104,6 @@
     private Factory2 mPrivateFactory;
     private Filter mFilter;
 
-    // Indicates whether we should try to inflate layouts using a precompiled layout instead of
-    // inflating from the XML resource.
-    private boolean mUseCompiledView;
-    // This variable holds the classloader that will be used to look for precompiled layouts. The
-    // The classloader includes the generated compiled_view.dex file.
-    private ClassLoader mPrecompiledClassLoader;
-
     /**
      * This is not a public API. Two APIs are now available to alleviate the need to access
      * this directly: {@link #createView(Context, String, String, AttributeSet)} and
@@ -259,7 +240,6 @@
     protected LayoutInflater(Context context) {
         StrictMode.assertConfigurationContext(context, "LayoutInflater");
         mContext = context;
-        initPrecompiledViews();
     }
 
     /**
@@ -277,7 +257,6 @@
         mFactory2 = original.mFactory2;
         mPrivateFactory = original.mPrivateFactory;
         setFilter(original.mFilter);
-        initPrecompiledViews();
     }
 
     /**
@@ -419,57 +398,6 @@
         }
     }
 
-    private void initPrecompiledViews() {
-        // Precompiled layouts are not supported in this release.
-        boolean enabled = false;
-        initPrecompiledViews(enabled);
-    }
-
-    private void initPrecompiledViews(boolean enablePrecompiledViews) {
-        mUseCompiledView = enablePrecompiledViews;
-
-        if (!mUseCompiledView) {
-            mPrecompiledClassLoader = null;
-            return;
-        }
-
-        // Make sure the application allows code generation
-        ApplicationInfo appInfo = mContext.getApplicationInfo();
-        if (appInfo.isEmbeddedDexUsed() || appInfo.isPrivilegedApp()) {
-            mUseCompiledView = false;
-            return;
-        }
-
-        // Try to load the precompiled layout file.
-        try {
-            mPrecompiledClassLoader = mContext.getClassLoader();
-            String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME;
-            if (new File(dexFile).exists()) {
-                mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader);
-            } else {
-                // If the precompiled layout file doesn't exist, then disable precompiled
-                // layouts.
-                mUseCompiledView = false;
-            }
-        } catch (Throwable e) {
-            if (DEBUG) {
-                Log.e(TAG, "Failed to initialized precompiled views layouts", e);
-            }
-            mUseCompiledView = false;
-        }
-        if (!mUseCompiledView) {
-            mPrecompiledClassLoader = null;
-        }
-    }
-
-    /**
-     * @hide for use by CTS tests
-     */
-    @TestApi
-    public void setPrecompiledLayoutsEnabledForTesting(boolean enablePrecompiledLayouts) {
-        initPrecompiledViews(enablePrecompiledLayouts);
-    }
-
     /**
      * Inflate a new view hierarchy from the specified xml resource. Throws
      * {@link InflateException} if there is an error.
@@ -529,10 +457,6 @@
                   + Integer.toHexString(resource) + ")");
         }
 
-        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
-        if (view != null) {
-            return view;
-        }
         XmlResourceParser parser = res.getLayout(resource);
         try {
             return inflate(parser, root, attachToRoot);
@@ -541,54 +465,6 @@
         }
     }
 
-    private @Nullable
-    View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
-        boolean attachToRoot) {
-        if (!mUseCompiledView) {
-            return null;
-        }
-
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");
-
-        // Try to inflate using a precompiled layout.
-        String pkg = res.getResourcePackageName(resource);
-        String layout = res.getResourceEntryName(resource);
-
-        try {
-            Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader);
-            Method inflater = clazz.getMethod(layout, Context.class, int.class);
-            View view = (View) inflater.invoke(null, mContext, resource);
-
-            if (view != null && root != null) {
-                // We were able to use the precompiled inflater, but now we need to do some work to
-                // attach the view to the root correctly.
-                XmlResourceParser parser = res.getLayout(resource);
-                try {
-                    AttributeSet attrs = Xml.asAttributeSet(parser);
-                    advanceToRootNode(parser);
-                    ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);
-
-                    if (attachToRoot) {
-                        root.addView(view, params);
-                    } else {
-                        view.setLayoutParams(params);
-                    }
-                } finally {
-                    parser.close();
-                }
-            }
-
-            return view;
-        } catch (Throwable e) {
-            if (DEBUG) {
-                Log.e(TAG, "Failed to use precompiled view", e);
-            }
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-        return null;
-    }
-
     /**
      * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is
      * found.
@@ -1050,7 +926,7 @@
      * of the general view creation logic, and thus may return {@code null} for some tags. This
      * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
      *
-     * @hide for use by precompiled layouts.
+     * @hide originally for internal use by precompiled layouts, which have since been removed.
      *
      * @param parent the parent view, used to inflate layout params
      * @param name the name of the XML tag used to define the view
@@ -1217,85 +1093,80 @@
                 + "reference. The layout ID " + value + " is not valid.");
         }
 
-        final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
-            (ViewGroup) parent, /*attachToRoot=*/true);
-        if (precompiled == null) {
-            final XmlResourceParser childParser = context.getResources().getLayout(layout);
+        final XmlResourceParser childParser = context.getResources().getLayout(layout);
+        try {
+            final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
 
-            try {
-                final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
-
-                while ((type = childParser.next()) != XmlPullParser.START_TAG &&
-                    type != XmlPullParser.END_DOCUMENT) {
-                    // Empty.
-                }
-
-                if (type != XmlPullParser.START_TAG) {
-                    throw new InflateException(getParserStateDescription(context, childAttrs)
-                            + ": No start tag found!");
-                }
-
-                final String childName = childParser.getName();
-
-                if (TAG_MERGE.equals(childName)) {
-                    // The <merge> tag doesn't support android:theme, so
-                    // nothing special to do here.
-                    rInflate(childParser, parent, context, childAttrs, false);
-                } else {
-                    final View view = createViewFromTag(parent, childName,
-                        context, childAttrs, hasThemeOverride);
-                    final ViewGroup group = (ViewGroup) parent;
-
-                    final TypedArray a = context.obtainStyledAttributes(
-                        attrs, R.styleable.Include);
-                    final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
-                    final int visibility = a.getInt(R.styleable.Include_visibility, -1);
-                    a.recycle();
-
-                    // We try to load the layout params set in the <include /> tag.
-                    // If the parent can't generate layout params (ex. missing width
-                    // or height for the framework ViewGroups, though this is not
-                    // necessarily true of all ViewGroups) then we expect it to throw
-                    // a runtime exception.
-                    // We catch this exception and set localParams accordingly: true
-                    // means we successfully loaded layout params from the <include>
-                    // tag, false means we need to rely on the included layout params.
-                    ViewGroup.LayoutParams params = null;
-                    try {
-                        params = group.generateLayoutParams(attrs);
-                    } catch (RuntimeException e) {
-                        // Ignore, just fail over to child attrs.
-                    }
-                    if (params == null) {
-                        params = group.generateLayoutParams(childAttrs);
-                    }
-                    view.setLayoutParams(params);
-
-                    // Inflate all children.
-                    rInflateChildren(childParser, view, childAttrs, true);
-
-                    if (id != View.NO_ID) {
-                        view.setId(id);
-                    }
-
-                    switch (visibility) {
-                        case 0:
-                            view.setVisibility(View.VISIBLE);
-                            break;
-                        case 1:
-                            view.setVisibility(View.INVISIBLE);
-                            break;
-                        case 2:
-                            view.setVisibility(View.GONE);
-                            break;
-                    }
-
-                    group.addView(view);
-                }
-            } finally {
-                childParser.close();
+            while ((type = childParser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                // Empty.
             }
+
+            if (type != XmlPullParser.START_TAG) {
+                throw new InflateException(getParserStateDescription(context, childAttrs)
+                        + ": No start tag found!");
+            }
+
+            final String childName = childParser.getName();
+
+            if (TAG_MERGE.equals(childName)) {
+                // The <merge> tag doesn't support android:theme, so
+                // nothing special to do here.
+                rInflate(childParser, parent, context, childAttrs, false);
+            } else {
+                final View view =
+                        createViewFromTag(parent, childName, context, childAttrs, hasThemeOverride);
+                final ViewGroup group = (ViewGroup) parent;
+
+                final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Include);
+                final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
+                final int visibility = a.getInt(R.styleable.Include_visibility, -1);
+                a.recycle();
+
+                // We try to load the layout params set in the <include /> tag.
+                // If the parent can't generate layout params (ex. missing width
+                // or height for the framework ViewGroups, though this is not
+                // necessarily true of all ViewGroups) then we expect it to throw
+                // a runtime exception.
+                // We catch this exception and set localParams accordingly: true
+                // means we successfully loaded layout params from the <include>
+                // tag, false means we need to rely on the included layout params.
+                ViewGroup.LayoutParams params = null;
+                try {
+                    params = group.generateLayoutParams(attrs);
+                } catch (RuntimeException e) {
+                    // Ignore, just fail over to child attrs.
+                }
+                if (params == null) {
+                    params = group.generateLayoutParams(childAttrs);
+                }
+                view.setLayoutParams(params);
+
+                // Inflate all children.
+                rInflateChildren(childParser, view, childAttrs, true);
+
+                if (id != View.NO_ID) {
+                    view.setId(id);
+                }
+
+                switch (visibility) {
+                    case 0:
+                        view.setVisibility(View.VISIBLE);
+                        break;
+                    case 1:
+                        view.setVisibility(View.INVISIBLE);
+                        break;
+                    case 2:
+                        view.setVisibility(View.GONE);
+                        break;
+                }
+
+                group.addView(view);
+            }
+        } finally {
+            childParser.close();
         }
+
         LayoutInflater.consumeChildElements(parser);
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dfada58..4da02f9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1020,8 +1020,7 @@
         mDisplay = display;
         mBasePackageName = context.getBasePackageName();
         final String name = DisplayProperties.debug_vri_package().orElse(null);
-        // TODO: b/306170135 - return to using textutils check on package name.
-        mExtraDisplayListenerLogging = true;
+        mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
         mThread = Thread.currentThread();
         mLocation = new WindowLeaked(null);
         mLocation.fillInStackTrace();
@@ -11613,7 +11612,14 @@
             Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
         }
 
-        surfaceSyncGroup.addTransaction(mPendingTransaction);
+        final Transaction t;
+        if (mHasPendingTransactions) {
+            t = new Transaction();
+            t.merge(mPendingTransaction);
+        } else {
+            t = null;
+        }
+
         mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
             @Override
             public void onFrameDraw(long frame) {
@@ -11626,6 +11632,9 @@
                             "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
                                     + frame + ".");
                 }
+                if (t != null) {
+                    mergeWithNextTransaction(t, frame);
+                }
 
                 // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
                 // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index 841354a..e6eeca4 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -35,17 +35,21 @@
 
     private final boolean mVisible;
 
+    private final boolean mHasDirectActivity;
+
     public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
-            boolean visible) {
+            boolean visible, boolean hasDirectActivity) {
         mConfiguration.setTo(configuration);
         mDisplayId = displayId;
         mVisible = visible;
+        mHasDirectActivity = hasDirectActivity;
     }
 
     public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
         mConfiguration.setTo(info.getConfiguration());
         mDisplayId = info.mDisplayId;
         mVisible = info.mVisible;
+        mHasDirectActivity = info.mHasDirectActivity;
     }
 
     /** The {@link Configuration} of the parent Task */
@@ -68,6 +72,14 @@
     }
 
     /**
+     * Whether the parent Task has any direct child activity, which is not embedded in any
+     * TaskFragment, or not
+     */
+    public boolean hasDirectActivity() {
+        return mHasDirectActivity;
+    }
+
+    /**
      * Returns {@code true} if the parameters which are important for task fragment
      * organizers are equal between this {@link TaskFragmentParentInfo} and {@code that}.
      * Note that this method is usually called with
@@ -80,7 +92,7 @@
             return false;
         }
         return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
-                && mVisible == that.mVisible;
+                && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity;
     }
 
     @WindowConfiguration.WindowingMode
@@ -94,6 +106,7 @@
                 + "config=" + mConfiguration
                 + ", displayId=" + mDisplayId
                 + ", visible=" + mVisible
+                + ", hasDirectActivity=" + mHasDirectActivity
                 + "}";
     }
 
@@ -114,7 +127,8 @@
         final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj;
         return mConfiguration.equals(that.mConfiguration)
                 && mDisplayId == that.mDisplayId
-                && mVisible == that.mVisible;
+                && mVisible == that.mVisible
+                && mHasDirectActivity == that.mHasDirectActivity;
     }
 
     @Override
@@ -122,6 +136,7 @@
         int result = mConfiguration.hashCode();
         result = 31 * result + mDisplayId;
         result = 31 * result + (mVisible ? 1 : 0);
+        result = 31 * result + (mHasDirectActivity ? 1 : 0);
         return result;
     }
 
@@ -130,12 +145,14 @@
         mConfiguration.writeToParcel(dest, flags);
         dest.writeInt(mDisplayId);
         dest.writeBoolean(mVisible);
+        dest.writeBoolean(mHasDirectActivity);
     }
 
     private TaskFragmentParentInfo(Parcel in) {
         mConfiguration.readFromParcel(in);
         mDisplayId = in.readInt();
         mVisible = in.readBoolean();
+        mHasDirectActivity = in.readBoolean();
     }
 
     public static final Creator<TaskFragmentParentInfo> CREATOR =
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index c2b5196..d9b5b2d 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -804,7 +804,7 @@
      * Sets/removes the always on top flag for this {@code windowContainer}. See
      * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
      * Please note that this method is only intended to be used for a
-     * {@link com.android.server.wm.DisplayArea}.
+     * {@link com.android.server.wm.Task} or {@link com.android.server.wm.DisplayArea}.
      *
      * <p>
      *     Setting always on top to {@code True} will also make the {@code windowContainer} to move
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index 0cbfcc5..c81c9ec 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -129,7 +129,6 @@
     @SuppressLint({"OnNameExpected", "ExecutorRegistration"})
     // Suppress lint because this is a legacy named function and doesn't have an optional param
     // for executor.
-    // TODO(b/259347943): Update documentation for U.
     /**
      * Here we override to prevent WindowProviderService from invoking
      * {@link Application.registerComponentCallback}, which will result in callback registered
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 584dd80..492e2ac 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -20,6 +20,7 @@
 import android.app.AsyncNotedAppOp;
 import android.app.SyncNotedAppOp;
 import android.app.RuntimeAppOpAccessMessage;
+import android.content.AttributionSource;
 import android.content.AttributionSourceState;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
@@ -32,10 +33,17 @@
 import com.android.internal.app.IAppOpsStartedCallback;
 import com.android.internal.app.MessageSamplingConfig;
 
+// AppOpsService AIDL interface.
+// PLEASE READ BEFORE MODIFYING THIS FILE.
+// Some methods in this interface or their transaction codes are mentioned in
+// frameworks/base/boot/hiddenapi/hiddenapi-unsupported.txt, meaning that we cannot change their
+// signature or ordering as they may be used by 3p apps.
+// Also, some methods are mentioned in native code, meaning that the numbering in
+// frameworks/native/libs/permission/include/binder/IAppOpsService.h must match the order here.
+// Please be careful to respect both these issues when modifying this file.
 interface IAppOpsService {
-    // These methods are also called by native code, so must
-    // be kept in sync with frameworks/native/libs/permission/include/binder/IAppOpsService.h
-    // and not be reordered
+    // These methods are also called by native code, so please be careful that the number in
+    // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
     int checkOperation(int code, int uid, String packageName);
     SyncNotedAppOp noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
@@ -54,21 +62,21 @@
     void setCameraAudioRestriction(int mode);
     void startWatchingModeWithFlags(int op, String packageName, int flags,
             IAppOpsCallback callback);
-    // End of methods also called by native code.
-    // Any new method exposed to native must be added after the last one, do not reorder
-
-    SyncNotedAppOp noteProxyOperation(int code, in AttributionSourceState attributionSourceState,
+    // End of methods also called by native code (there may be more blocks like this of native
+    // methods later in this file).
+    // Deprecated, use noteProxyOperationWithState instead.
+    SyncNotedAppOp noteProxyOperation(int code, in AttributionSource attributionSource,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             boolean skipProxyOperation);
+    // Deprecated, use startProxyOperationWithState instead.
     SyncNotedAppOp startProxyOperation(IBinder clientId, int code,
-            in AttributionSourceState attributionSourceState, boolean startIfModeDefault,
+            in AttributionSource attributionSource, boolean startIfModeDefault,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
             int attributionChainId);
-    void finishProxyOperation(IBinder clientId, int code,
-            in AttributionSourceState attributionSourceState, boolean skipProxyOperation);
-
-    // Remaining methods are only used in Java.
+    // Deprecated, use finishProxyOperationWithState instead.
+    void finishProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource,
+            boolean skipProxyOperation);
     int checkPackage(int uid, String packageName);
     RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
     MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(String packageName,
@@ -127,8 +135,19 @@
     List<AsyncNotedAppOp> extractAsyncOps(String packageName);
 
     int checkOperationRaw(int code, int uid, String packageName, @nullable String attributionTag);
-
     void reloadNonHistoricalState();
 
     void collectNoteOpCallsForValidation(String stackTrace, int op, String packageName, long version);
+
+    SyncNotedAppOp noteProxyOperationWithState(int code,
+                in AttributionSourceState attributionSourceStateState,
+                boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+                boolean skipProxyOperation);
+    SyncNotedAppOp startProxyOperationWithState(IBinder clientId, int code,
+                in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
+                boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+                boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
+                int attributionChainId);
+    void finishProxyOperationWithState(IBinder clientId, int code,
+                in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
 }
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index ba87caa..a65877c 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -20,6 +20,7 @@
 import android.service.voice.HotwordDetectedResult;
 import android.service.voice.HotwordDetectionServiceFailure;
 import android.service.voice.HotwordRejectedResult;
+import android.service.voice.HotwordTrainingData;
 import android.service.voice.SoundTriggerFailure;
 import android.service.voice.VisualQueryDetectionServiceFailure;
 import com.android.internal.infra.AndroidFuture;
@@ -59,6 +60,12 @@
     void onRejected(in HotwordRejectedResult result);
 
     /**
+     * Called by {@link HotwordDetectionService} to egress training data to the
+     * {@link HotwordDetector}.
+     */
+    void onTrainingData(in HotwordTrainingData data);
+
+    /**
      * Called when the detection fails due to an error occurs in the
      * {@link HotwordDetectionService}.
      *
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 314ed69..68e2b48 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -359,6 +359,12 @@
             in IHotwordRecognitionStatusCallback callback);
 
     /**
+     * Test API to reset training data egress count for test.
+     */
+    @EnforcePermission("RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT")
+    void resetHotwordTrainingDataEgressCountForTest();
+
+    /**
      * Starts to listen the status of visible activity.
      */
     void startListeningVisibleActivityChanged(in IBinder token);
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 0399430..7d78f29 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -603,6 +603,17 @@
     }
 
     /**
+     * Returns the monotonic clock time when the available battery history collection started.
+     */
+    public long getStartTime() {
+        if (!mHistoryFiles.isEmpty()) {
+            return mHistoryFiles.get(0).monotonicTimeMs;
+        } else {
+            return mHistoryBufferStartTime;
+        }
+    }
+
+    /**
      * Start iterating history files and history buffer.
      *
      * @param startTimeMs monotonic time (the HistoryItem.time field) to start iterating from,
diff --git a/core/java/com/android/internal/os/MultiStateStats.java b/core/java/com/android/internal/os/MultiStateStats.java
index dc5055a..ecfed53 100644
--- a/core/java/com/android/internal/os/MultiStateStats.java
+++ b/core/java/com/android/internal/os/MultiStateStats.java
@@ -29,6 +29,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Maintains multidimensional multi-state stats.  States could be something like on-battery (0,1),
@@ -52,13 +54,55 @@
 
         public States(String name, boolean tracked, String... labels) {
             mName = name;
-            this.mTracked = tracked;
-            this.mLabels = labels;
+            mTracked = tracked;
+            mLabels = labels;
         }
 
         public boolean isTracked() {
             return mTracked;
         }
+
+        public String getName() {
+            return mName;
+        }
+
+        public String[] getLabels() {
+            return mLabels;
+        }
+
+        /**
+         * Iterates over all combinations of tracked states and invokes <code>consumer</code>
+         * for each of them.
+         */
+        public static void forEachTrackedStateCombination(States[] states,
+                Consumer<int[]> consumer) {
+            forEachTrackedStateCombination(consumer, states, new int[states.length], 0);
+        }
+
+        /**
+         * Recursive function that does a depth-first traversal of the multi-dimensional
+         * state space. Each time the traversal reaches the end of the <code>states</code> array,
+         * <code>statesValues</code> contains a unique combination of values for all tracked states.
+         * For untracked states, the corresponding values are left as 0.  The end result is
+         * that the <code>consumer</code> is invoked for every unique combination of tracked state
+         * values.  For example, it may be a sequence of calls like screen-on/power-on,
+         * screen-on/power-off, screen-off/power-on, screen-off/power-off.
+         */
+        private static void forEachTrackedStateCombination(Consumer<int[]> consumer,
+                States[] states, int[] statesValues, int stateIndex) {
+            if (stateIndex < statesValues.length) {
+                if (!states[stateIndex].mTracked) {
+                    forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
+                    return;
+                }
+                for (int i = 0; i < states[stateIndex].mLabels.length; i++) {
+                    statesValues[stateIndex] = i;
+                    forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
+                }
+                return;
+            }
+            consumer.accept(statesValues);
+        }
     }
 
     /**
@@ -276,6 +320,13 @@
     }
 
     /**
+     * Updates the stats values for the provided combination of states.
+     */
+    public void setStats(int[] states, long[] values) {
+        mCounter.setValues(mFactory.getSerialState(states), values);
+    }
+
+    /**
      * Resets the counters.
      */
     public void reset() {
@@ -293,24 +344,27 @@
      */
     public void writeXml(TypedXmlSerializer serializer) throws IOException {
         long[] tmpArray = new long[mCounter.getArrayLength()];
-        writeXmlAllStates(serializer, new int[mFactory.mStates.length], 0, tmpArray);
+
+        try {
+            States.forEachTrackedStateCombination(mFactory.mStates,
+                    states -> {
+                        try {
+                            writeXmlForStates(serializer, states, tmpArray);
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    });
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof IOException) {
+                throw (IOException) e.getCause();
+            } else {
+                throw e;
+            }
+        }
     }
 
-    private void writeXmlAllStates(TypedXmlSerializer serializer, int[] states, int stateIndex,
-            long[] values) throws IOException {
-        if (stateIndex < states.length) {
-            if (!mFactory.mStates[stateIndex].mTracked) {
-                writeXmlAllStates(serializer, states, stateIndex + 1, values);
-                return;
-            }
-
-            for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
-                states[stateIndex] = i;
-                writeXmlAllStates(serializer, states, stateIndex + 1, values);
-            }
-            return;
-        }
-
+    private void writeXmlForStates(TypedXmlSerializer serializer, int[] states, long[] values)
+            throws IOException {
         mCounter.getCounts(values, mFactory.getSerialState(states));
         boolean nonZero = false;
         for (long value : values) {
@@ -391,48 +445,33 @@
     /**
      * Prints the accumulated stats, one line of every combination of states that has data.
      */
-    public void dump(PrintWriter pw) {
-        long[] tmpArray = new long[mCounter.getArrayLength()];
-        dumpAllStates(pw, new int[mFactory.mStates.length], 0, tmpArray);
-    }
-
-    private void dumpAllStates(PrintWriter pw, int[] states, int stateIndex, long[] values) {
-        if (stateIndex < states.length) {
-            if (!mFactory.mStates[stateIndex].mTracked) {
-                dumpAllStates(pw, states, stateIndex + 1, values);
+    public void dump(PrintWriter pw, Function<long[], String> statsFormatter) {
+        long[] values = new long[mCounter.getArrayLength()];
+        States.forEachTrackedStateCombination(mFactory.mStates, states -> {
+            mCounter.getCounts(values, mFactory.getSerialState(states));
+            boolean nonZero = false;
+            for (long value : values) {
+                if (value != 0) {
+                    nonZero = true;
+                    break;
+                }
+            }
+            if (!nonZero) {
                 return;
             }
 
-            for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
-                states[stateIndex] = i;
-                dumpAllStates(pw, states, stateIndex + 1, values);
-            }
-            return;
-        }
-
-        mCounter.getCounts(values, mFactory.getSerialState(states));
-        boolean nonZero = false;
-        for (long value : values) {
-            if (value != 0) {
-                nonZero = true;
-                break;
-            }
-        }
-        if (!nonZero) {
-            return;
-        }
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < states.length; i++) {
-            if (mFactory.mStates[i].mTracked) {
-                if (sb.length() != 0) {
-                    sb.append(" ");
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < states.length; i++) {
+                if (mFactory.mStates[i].mTracked) {
+                    if (sb.length() != 0) {
+                        sb.append(" ");
+                    }
+                    sb.append(mFactory.mStates[i].mLabels[states[i]]);
                 }
-                sb.append(mFactory.mStates[i].mLabels[states[i]]);
             }
-        }
-        sb.append(" ");
-        sb.append(Arrays.toString(values));
-        pw.println(sb);
+            sb.append(" ");
+            sb.append(statsFormatter.apply(values));
+            pw.println(sb);
+        });
     }
 }
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 503e689..2298cbd 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -44,7 +44,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Locale;
 
 /**
  * Reports power consumption values for various device activities. Reads values from an XML file.
@@ -295,7 +294,7 @@
 
     private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFF_FFFF;
 
-    private static final int DEFAULT_CPU_POWER_BRACKET_NUMBER = 3;
+    public static final int POWER_BRACKETS_UNSPECIFIED = -1;
 
     /**
      * A map from Power Use Item to its power consumption.
@@ -361,7 +360,7 @@
         }
         initCpuClusters();
         initCpuScalingPolicies();
-        initCpuPowerBrackets(DEFAULT_CPU_POWER_BRACKET_NUMBER);
+        initCpuPowerBrackets();
         initDisplays();
         initModem();
     }
@@ -560,8 +559,7 @@
     /**
      * Parses or computes CPU power brackets: groups of states with similar power requirements.
      */
-    @VisibleForTesting
-    public void initCpuPowerBrackets(int defaultCpuPowerBracketNumber) {
+    private void initCpuPowerBrackets() {
         boolean anyBracketsSpecified = false;
         boolean allBracketsSpecified = true;
         for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
@@ -580,79 +578,32 @@
                     "Power brackets should be specified for all scaling policies or none");
         }
 
+        if (!allBracketsSpecified) {
+            mCpuPowerBracketCount = POWER_BRACKETS_UNSPECIFIED;
+            return;
+        }
+
         mCpuPowerBracketCount = 0;
-        if (allBracketsSpecified) {
-            for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
-                int policy = mCpuScalingPolicies.keyAt(i);
-                CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
-                final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy);
-                if (data.length != cpuScalingPolicyPower.powerBrackets.length) {
-                    throw new RuntimeException(
-                            "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy
-                                    + ", expected: "
-                                    + cpuScalingPolicyPower.powerBrackets.length);
-                }
-
-                for (int j = 0; j < data.length; j++) {
-                    final int bracket = (int) Math.round(data[j]);
-                    cpuScalingPolicyPower.powerBrackets[j] = bracket;
-                    if (bracket > mCpuPowerBracketCount) {
-                        mCpuPowerBracketCount = bracket;
-                    }
-                }
-            }
-            mCpuPowerBracketCount++;
-        } else {
-            double minPower = Double.MAX_VALUE;
-            double maxPower = Double.MIN_VALUE;
-            int stateCount = 0;
-            for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
-                int policy = mCpuScalingPolicies.keyAt(i);
-                CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
-                final int steps = cpuScalingPolicyPower.stepPower.length;
-                for (int step = 0; step < steps; step++) {
-                    final double power = getAveragePowerForCpuScalingStep(policy, step);
-                    if (power < minPower) {
-                        minPower = power;
-                    }
-                    if (power > maxPower) {
-                        maxPower = power;
-                    }
-                }
-                stateCount += steps;
+        for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
+            int policy = mCpuScalingPolicies.keyAt(i);
+            CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+            final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy);
+            if (data.length != cpuScalingPolicyPower.powerBrackets.length) {
+                throw new RuntimeException(
+                        "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy
+                                + ", expected: "
+                                + cpuScalingPolicyPower.powerBrackets.length);
             }
 
-            if (stateCount <= defaultCpuPowerBracketNumber) {
-                mCpuPowerBracketCount = stateCount;
-                int bracket = 0;
-                for (int i = 0; i < mCpuScalingPolicies.size(); i++) {
-                    CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
-                    final int steps = cpuScalingPolicyPower.stepPower.length;
-                    for (int step = 0; step < steps; step++) {
-                        cpuScalingPolicyPower.powerBrackets[step] = bracket++;
-                    }
-                }
-            } else {
-                mCpuPowerBracketCount = defaultCpuPowerBracketNumber;
-                final double minLogPower = Math.log(minPower);
-                final double logBracket = (Math.log(maxPower) - minLogPower)
-                        / defaultCpuPowerBracketNumber;
-
-                for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
-                    int policy = mCpuScalingPolicies.keyAt(i);
-                    CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
-                    final int steps = cpuScalingPolicyPower.stepPower.length;
-                    for (int step = 0; step < steps; step++) {
-                        final double power = getAveragePowerForCpuScalingStep(policy, step);
-                        int bracket = (int) ((Math.log(power) - minLogPower) / logBracket);
-                        if (bracket >= defaultCpuPowerBracketNumber) {
-                            bracket = defaultCpuPowerBracketNumber - 1;
-                        }
-                        cpuScalingPolicyPower.powerBrackets[step] = bracket;
-                    }
+            for (int j = 0; j < data.length; j++) {
+                final int bracket = (int) Math.round(data[j]);
+                cpuScalingPolicyPower.powerBrackets[j] = bracket;
+                if (bracket > mCpuPowerBracketCount) {
+                    mCpuPowerBracketCount = bracket;
                 }
             }
         }
+        mCpuPowerBracketCount++;
     }
 
     private static class CpuScalingPolicyPower {
@@ -771,44 +722,13 @@
 
     /**
      * Returns the number of CPU power brackets: groups of states with similar power requirements.
+     * If power brackets are not specified, returns {@link #POWER_BRACKETS_UNSPECIFIED}
      */
     public int getCpuPowerBracketCount() {
         return mCpuPowerBracketCount;
     }
 
     /**
-     * Description of a CPU power bracket: which cluster/frequency combinations are included.
-     */
-    public String getCpuPowerBracketDescription(CpuScalingPolicies cpuScalingPolicies,
-            int powerBracket) {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < mCpuScalingPolicies.size(); i++) {
-            int policy = mCpuScalingPolicies.keyAt(i);
-            CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
-            int[] brackets = cpuScalingPolicyPower.powerBrackets;
-            int[] freqs = cpuScalingPolicies.getFrequencies(policy);
-            for (int step = 0; step < brackets.length; step++) {
-                if (brackets[step] == powerBracket) {
-                    if (sb.length() != 0) {
-                        sb.append(", ");
-                    }
-                    if (mCpuScalingPolicies.size() > 1) {
-                        sb.append(policy).append('/');
-                    }
-                    if (step < freqs.length) {
-                        sb.append(freqs[step] / 1000);
-                    }
-                    sb.append('(');
-                    sb.append(String.format(Locale.US, "%.1f",
-                            getAveragePowerForCpuScalingStep(policy, step)));
-                    sb.append(')');
-                }
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
      * Returns the CPU power bracket corresponding to the specified scaling policy and frequency
      * step
      */
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 1130a45..1a7efac 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -55,12 +55,12 @@
     private static final int STATS_ARRAY_LENGTH_SHIFT =
             Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK);
     public static final int MAX_STATS_ARRAY_LENGTH =
-            2 ^ Integer.bitCount(STATS_ARRAY_LENGTH_MASK) - 1;
+            (1 << Integer.bitCount(STATS_ARRAY_LENGTH_MASK)) - 1;
     private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
     private static final int UID_STATS_ARRAY_LENGTH_SHIFT =
             Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK);
     public static final int MAX_UID_STATS_ARRAY_LENGTH =
-            (2 ^ Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1;
+            (1 << Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1;
 
     /**
      * Descriptor of the stats collected for a given power component (e.g. CPU, WiFi etc).
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 965277c..1c5f4f0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -868,6 +868,11 @@
                                  args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
                                  args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
 
+            // While `specializeAppProcess` sets the thread name on the process's main thread, this
+            // is distinct from the app process name which appears in stack traces, as the latter is
+            // sourced from the argument buffer of the Process class. Set the app process name here.
+            Zygote.setAppProcessName(args, TAG);
+
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
             return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 993e4e7..5fe086d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -296,7 +296,6 @@
                     } else {
                         // child; result is a Runnable.
                         zygoteServer.setForkChild();
-                        Zygote.setAppProcessName(parsedArgs, TAG);  // ??? Necessary?
                         return result;
                     }
                 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a3e2706..a3e0016 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1105,10 +1105,9 @@
     @UnsupportedAppUsage
     public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
         final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
-        if (isSpecialUserId(userId)) {
-            // For secure password storage (that is required for special users such as FRP), the
-            // underlying storage also enforces the deadline. Since we cannot store settings
-            // for special users, don't.
+        if (userId == USER_FRP) {
+            // For secure password storage (that is required for FRP), the underlying storage also
+            // enforces the deadline. Since we cannot store settings for the FRP user, don't.
             return deadline;
         }
         mLockoutDeadlines.put(userId, deadline);
@@ -1934,15 +1933,21 @@
     }
 
     /**
-     * Unlocks the credential-encrypted storage for the given user if the user is not secured, i.e.
-     * doesn't have an LSKF.
+     * If the user is not secured, ie doesn't have an LSKF, then decrypt the user's synthetic
+     * password and use it to unlock various cryptographic keys associated with the user.  This
+     * primarily includes unlocking the user's credential-encrypted (CE) storage.  It also includes
+     * deriving or decrypting the vendor auth secret and sending it to the AuthSecret HAL.
      * <p>
-     * Whether the storage has been unlocked can be determined by
-     * {@link StorageManager#isUserKeyUnlocked()}.
-     *
+     * These tasks would normally be done when the LSKF is verified.  This method is where these
+     * tasks are done when the user doesn't have an LSKF.  It's called when the user is started.
+     * <p>
+     * Except on permission denied, this method doesn't throw an exception on failure.  However, the
+     * last thing that it does is unlock CE storage, and whether CE storage has been successfully
+     * unlocked can be determined by {@link StorageManager#isCeStorageUnlocked()}.
+     * <p>
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
      *
-     * @param userId the ID of the user whose storage to unlock
+     * @param userId the ID of the user whose keys to unlock
      */
     public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
         try {
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 03e9a6a..1b24efa 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -39,7 +39,7 @@
     explicit VelocityTrackerState(const VelocityTracker::Strategy strategy);
 
     void clear();
-    void addMovement(const MotionEvent* event);
+    void addMovement(const MotionEvent& event);
     // TODO(b/32830165): consider supporting an overload that supports computing velocity only for
     // a subset of the supported axes.
     void computeCurrentVelocity(int32_t units, float maxVelocity);
@@ -57,7 +57,7 @@
     mVelocityTracker.clear();
 }
 
-void VelocityTrackerState::addMovement(const MotionEvent* event) {
+void VelocityTrackerState::addMovement(const MotionEvent& event) {
     mVelocityTracker.addMovement(event);
 }
 
@@ -102,13 +102,13 @@
 static void android_view_VelocityTracker_nativeAddMovement(JNIEnv* env, jclass clazz, jlong ptr,
         jobject eventObj) {
     const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
-    if (!event) {
+    if (event == nullptr) {
         LOG(WARNING) << "nativeAddMovement failed because MotionEvent was finalized.";
         return;
     }
 
     VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
-    state->addMovement(event);
+    state->addMovement(*event);
 }
 
 static void android_view_VelocityTracker_nativeComputeCurrentVelocity(JNIEnv* env, jclass clazz,
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
new file mode 100644
index 0000000..8c33041
--- /dev/null
+++ b/core/proto/android/app/appstartinfo.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.app;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/proto_logging/stats/enums/app/enums.proto";
+
+/**
+ * An android.app.ApplicationStartInfo object.
+ */
+message ApplicationStartInfoProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 pid = 1;
+    optional int32 real_uid = 2;
+    optional int32 package_uid = 3;
+    optional int32 defining_uid = 4;
+    optional string process_name = 5;
+    optional AppStartStartupState startup_state = 6;
+    optional AppStartReasonCode reason = 7;
+    optional bytes startup_timestamps = 8;
+    optional AppStartStartType start_type = 9;
+    optional bytes start_intent = 10;
+    optional AppStartLaunchMode launch_mode = 11;
+}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 4732702..5b0a502 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -99,6 +99,7 @@
         optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_magnification_gesture = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto accessibility_magnification_two_finger_triple_tap_enabled = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Accessibility accessibility = 2;
 
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 025a57d..c5889ba 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -20,6 +20,7 @@
 
 import "frameworks/base/core/proto/android/app/activitymanager.proto";
 import "frameworks/base/core/proto/android/app/appexitinfo.proto";
+import "frameworks/base/core/proto/android/app/appstartinfo.proto";
 import "frameworks/base/core/proto/android/app/notification.proto";
 import "frameworks/base/core/proto/android/app/profilerinfo.proto";
 import "frameworks/base/core/proto/android/content/component_name.proto";
@@ -1041,3 +1042,23 @@
     }
     repeated Package packages = 2;
 }
+
+// sync with com.android.server.am.am.ProcessList.java
+message AppsStartInfoProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int64 last_update_timestamp = 1;
+    message Package {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+        optional string package_name = 1;
+        message User {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional int32 uid = 1;
+            repeated .android.app.ApplicationStartInfoProto app_start_info = 2;
+        }
+        repeated User users = 2;
+    }
+    repeated Package packages = 2;
+}
diff --git a/core/proto/android/service/OWNERS b/core/proto/android/service/OWNERS
index 70cb50f..7a19155 100644
--- a/core/proto/android/service/OWNERS
+++ b/core/proto/android/service/OWNERS
@@ -1 +1,2 @@
 per-file sensor_service.proto = arthuri@google.com, bduddie@google.com, stange@google.com
+per-file package.proto = file:platform/frameworks/base:/PACKAGE_MANAGER_OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6daa5b9..8c91be8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4565,6 +4565,12 @@
     <permission android:name="android.permission.SET_DEBUG_APP"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- Allows an application to access the data in Dropbox.
+    <p>Not for use by third-party applications.
+    @FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission") -->
+    <permission android:name="android.permission.READ_DROPBOX_DATA"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- Allows an application to set the maximum number of (not needed)
          application processes that can be running.
          <p>Not for use by third-party applications. -->
@@ -4647,7 +4653,7 @@
         @hide
         @SystemApi -->
     <permission android:name="android.permission.STATUS_BAR_SERVICE"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|recents" />
 
     <!-- Allows an application to bind to third party quick settings tiles.
          <p>Should only be requested by the System, should be required by
@@ -4711,7 +4717,7 @@
          @hide
     -->
     <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
-        android:protectionLevel="signature|module" />
+        android:protectionLevel="signature|module|recents" />
 
     <!-- Allows an application to avoid all toast rate limiting restrictions.
          <p>Not for use by third-party applications.
@@ -7768,6 +7774,14 @@
     <permission android:name="android.permission.MANAGE_DISPLAYS"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows apps to reset hotword training data egress count for testing.
+     <p>CTS tests will use UiAutomation.AdoptShellPermissionIdentity() to gain access.
+     <p>Protection level: signature
+     @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds")
+     @hide -->
+    <permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT"
+                android:protectionLevel="signature" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable-watch/ic_lock_bugreport.xml b/core/res/res/drawable-watch/ic_lock_bugreport.xml
index 66dd392..b664fe4f 100644
--- a/core/res/res/drawable-watch/ic_lock_bugreport.xml
+++ b/core/res/res/drawable-watch/ic_lock_bugreport.xml
@@ -20,12 +20,12 @@
         android:viewportHeight="24.0"
         android:tint="@android:color/white">
     <path
-        android:fillColor="#FF000000"
+        android:fillColor="@android:color/white"
         android:pathData="M20,10V8h-2.81c-0.45,-0.78 -1.07,-1.46 -1.82,-1.96L17,4.41L15.59,3l-2.17,2.17c-0.03,-0.01 -0.05,-0.01 -0.08,-0.01c-0.16,-0.04 -0.32,-0.06 -0.49,-0.09c-0.06,-0.01 -0.11,-0.02 -0.17,-0.03C12.46,5.02 12.23,5 12,5h0c-0.49,0 -0.97,0.07 -1.42,0.18l0.02,-0.01L8.41,3L7,4.41l1.62,1.63l0.01,0C7.88,6.54 7.26,7.22 6.81,8H4v2h2.09C6.03,10.33 6,10.66 6,11v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3h0c2.22,0 4.15,-1.21 5.19,-3H20v-2h-2.09l0,0c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1l0,0H20zM16,15c0,2.21 -1.79,4 -4,4c-2.21,0 -4,-1.79 -4,-4v-4c0,-2.21 1.79,-4 4,-4h0c2.21,0 4,1.79 4,4V15z"/>
     <path
-        android:fillColor="#FF000000"
+        android:fillColor="@android:color/white"
         android:pathData="M10,14h4v2h-4z"/>
     <path
-        android:fillColor="#FF000000"
+        android:fillColor="@android:color/white"
         android:pathData="M10,10h4v2h-4z"/>
 </vector>
diff --git a/core/res/res/drawable-watch/ic_lock_power_off.xml b/core/res/res/drawable-watch/ic_lock_power_off.xml
index 34bc88c..b437a4b 100644
--- a/core/res/res/drawable-watch/ic_lock_power_off.xml
+++ b/core/res/res/drawable-watch/ic_lock_power_off.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24"
         android:tint="@android:color/white">
     <path
-        android:fillColor="@android:color/black"
+        android:fillColor="@android:color/white"
         android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
 </vector>
diff --git a/core/res/res/drawable-watch/ic_restart.xml b/core/res/res/drawable-watch/ic_restart.xml
index 24d7c34..52933aa 100644
--- a/core/res/res/drawable-watch/ic_restart.xml
+++ b/core/res/res/drawable-watch/ic_restart.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24"
         android:tint="@android:color/white">
     <path
-        android:fillColor="@android:color/black"
+        android:fillColor="@android:color/white"
         android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02c-2.83,-0.48 -5,-2.94 -5,-5.91zM20,13c0,-4.42 -3.58,-8 -8,-8 -0.06,0 -0.12,0.01 -0.18,0.01l1.09,-1.09L11.5,2.5 8,6l3.5,3.5 1.41,-1.41 -1.08,-1.08c0.06,0 0.12,-0.01 0.17,-0.01 3.31,0 6,2.69 6,6 0,2.97 -2.17,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93z"/>
 </vector>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index a6830a6..c0c6e05 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -135,11 +135,12 @@
     <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
     <drawable name="notification_template_divider">#29000000</drawable>
     <drawable name="notification_template_divider_media">#29ffffff</drawable>
-    <color name="notification_primary_text_color_light">@color/primary_text_default_material_light</color>
-    <color name="notification_primary_text_color_dark">@color/primary_text_default_material_dark</color>
-    <color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color>
+    <color name="notification_primary_text_color_light">@color/system_on_surface_light</color>
+    <color name="notification_primary_text_color_dark">@color/system_on_surface_dark</color>
+    <!-- Note that the primary and secondary notification text colors are, in fact, the same. -->
+    <color name="notification_secondary_text_color_light">@color/system_on_surface_light</color>
+    <color name="notification_secondary_text_color_dark">@color/system_on_surface_dark</color>
     <item name="notification_secondary_text_disabled_alpha" format="float" type="dimen">0.38</item>
-    <color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color>
     <color name="notification_default_color_dark">#ddffffff</color>
     <color name="notification_default_color_light">#a3202124</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5a1f2d1a..ab71b41 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1241,6 +1241,21 @@
    <!-- Activity name for the default target activity to be launched. [DO NOT TRANSLATE] -->
    <string name="config_primaryShortPressTargetActivity" translatable="false"></string>
 
+    <!-- Whether a single short press on POWER should be launched without multi-press delay.
+        When this value is set to true, POWER button's single short press behavior will execute
+        immediately upon key-up regardless of whether this press will be part of a multi-press
+        gesture in the future(therefore, not waiting for a multi-press detecting delay).
+
+       For Example, let's say a power button single press launches the app menu and power button
+       double press launches the camera. By configuring this variable to true, user will observe 2
+       things in order upon a double press:
+          1. App menu pops up immediately upon the first key up.
+          2. Camera starts as the double press behavior.
+
+        Note that this config has no effect on long press behavior.
+    -->
+    <bool name="config_shortPressEarlyOnPower">false</bool>
+
     <!-- Control the behavior of the search key.
             0 - Launch default search activity
             1 - Launch target activity defined by config_searchKeyTargetActivity
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8e1c09e..14bbb96 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -471,6 +471,7 @@
   <java-symbol type="string" name="config_primaryShortPressTargetActivity" />
   <java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
   <java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
+  <java-symbol type="bool" name="config_shortPressEarlyOnPower" />
   <java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
   <java-symbol type="integer" name="config_searchKeyBehavior" />
   <java-symbol type="string" name="config_searchKeyTargetActivity" />
diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp
index 054d10c..6be553b 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -40,6 +40,8 @@
         "androidx.test.rules",
         "truth",
         "testng",
+        "android.hardware.radio.flags-aconfig-java",
+        "flag-junit",
         "mockito-target-extended",
     ],
     libs: ["android.test.base"],
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
index d638fed..d4a88c4 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
@@ -16,8 +16,6 @@
 
 package android.hardware.radio;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -36,8 +34,14 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArraySet;
 
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -143,12 +147,17 @@
     @Mock
     private RadioTuner.Callback mTunerCallbackMock;
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void getIdentifierTypes_forFilter() {
         ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
                 FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
 
-        assertWithMessage("Filtered identifier types").that(filter.getIdentifierTypes())
+        mExpect.withMessage("Filtered identifier types").that(filter.getIdentifierTypes())
                 .containsExactlyElementsIn(FILTER_IDENTIFIER_TYPES);
     }
 
@@ -157,7 +166,7 @@
         ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
                 FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
 
-        assertWithMessage("Filtered identifiers").that(filter.getIdentifiers())
+        mExpect.withMessage("Filtered identifiers").that(filter.getIdentifiers())
                 .containsExactlyElementsIn(FILTER_IDENTIFIERS);
     }
 
@@ -166,7 +175,7 @@
         ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
                 FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
 
-        assertWithMessage("Filter including categories")
+        mExpect.withMessage("Filter including categories")
                 .that(filter.areCategoriesIncluded()).isEqualTo(INCLUDE_CATEGORIES);
     }
 
@@ -175,7 +184,7 @@
         ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
                 FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
 
-        assertWithMessage("Filter excluding modifications")
+        mExpect.withMessage("Filter excluding modifications")
                 .that(filter.areModificationsExcluded()).isEqualTo(EXCLUDE_MODIFICATIONS);
     }
 
@@ -184,7 +193,7 @@
         ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
                 FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
 
-        assertWithMessage("Filter vendor obtained from filter without vendor filter")
+        mExpect.withMessage("Filter vendor obtained from filter without vendor filter")
                 .that(filter.getVendorFilter()).isNull();
     }
 
@@ -192,13 +201,13 @@
     public void getVendorFilter_forFilterWithVendorFilter() {
         ProgramList.Filter vendorFilter = new ProgramList.Filter(VENDOR_FILTER);
 
-        assertWithMessage("Filter vendor obtained from filter with vendor filter")
+        mExpect.withMessage("Filter vendor obtained from filter with vendor filter")
                 .that(vendorFilter.getVendorFilter()).isEqualTo(VENDOR_FILTER);
     }
 
     @Test
     public void describeContents_forFilter() {
-        assertWithMessage("Filter contents").that(TEST_FILTER.describeContents()).isEqualTo(0);
+        mExpect.withMessage("Filter contents").that(TEST_FILTER.describeContents()).isEqualTo(0);
     }
 
     @Test
@@ -206,7 +215,7 @@
         ProgramList.Filter filterCompared = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
                 FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
 
-        assertWithMessage("Hash code of the same filter")
+        mExpect.withMessage("Hash code of the same filter")
                 .that(filterCompared.hashCode()).isEqualTo(TEST_FILTER.hashCode());
     }
 
@@ -214,7 +223,7 @@
     public void hashCode_withDifferentFilters_notEquals() {
         ProgramList.Filter filterCompared = new ProgramList.Filter();
 
-        assertWithMessage("Hash code of the different filter")
+        mExpect.withMessage("Hash code of the different filter")
                 .that(filterCompared.hashCode()).isNotEqualTo(TEST_FILTER.hashCode());
     }
 
@@ -227,7 +236,7 @@
 
         ProgramList.Filter filterFromParcel =
                 ProgramList.Filter.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Filter created from parcel")
+        mExpect.withMessage("Filter created from parcel")
                 .that(filterFromParcel).isEqualTo(TEST_FILTER);
     }
 
@@ -235,36 +244,37 @@
     public void newArray_forFilterCreator() {
         ProgramList.Filter[] filters = ProgramList.Filter.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("Program filters").that(filters).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("Program filters").that(filters).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
     public void isPurge_forChunk() {
-        assertWithMessage("Puring chunk").that(FM_DAB_ADD_CHUNK.isPurge()).isEqualTo(IS_PURGE);
+        mExpect.withMessage("Puring chunk").that(FM_DAB_ADD_CHUNK.isPurge()).isEqualTo(IS_PURGE);
     }
 
     @Test
     public void isComplete_forChunk() {
-        assertWithMessage("Complete chunk").that(FM_DAB_ADD_CHUNK.isComplete())
+        mExpect.withMessage("Complete chunk").that(FM_DAB_ADD_CHUNK.isComplete())
                 .isEqualTo(IS_COMPLETE);
     }
 
     @Test
     public void getModified_forChunk() {
-        assertWithMessage("Modified program info in chunk")
+        mExpect.withMessage("Modified program info in chunk")
                 .that(FM_DAB_ADD_CHUNK.getModified())
                 .containsExactly(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_1, DAB_PROGRAM_INFO_2);
     }
 
     @Test
     public void getRemoved_forChunk() {
-        assertWithMessage("Removed program identifiers in chunk")
+        mExpect.withMessage("Removed program identifiers in chunk")
                 .that(FM_DAB_ADD_CHUNK.getRemoved()).containsExactly(RDS_UNIQUE_IDENTIFIER);
     }
 
     @Test
     public void describeContents_forChunk() {
-        assertWithMessage("Chunk contents").that(FM_DAB_ADD_CHUNK.describeContents()).isEqualTo(0);
+        mExpect.withMessage("Chunk contents").that(FM_DAB_ADD_CHUNK.describeContents())
+                .isEqualTo(0);
     }
 
     @Test
@@ -276,7 +286,7 @@
 
         ProgramList.Chunk chunkFromParcel =
                 ProgramList.Chunk.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Chunk created from parcel")
+        mExpect.withMessage("Chunk created from parcel")
                 .that(chunkFromParcel).isEqualTo(FM_DAB_ADD_CHUNK);
     }
 
@@ -284,7 +294,7 @@
     public void newArray_forChunkCreator() {
         ProgramList.Chunk[] chunks = ProgramList.Chunk.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("Chunks").that(chunks).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("Chunks").that(chunks).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
@@ -295,7 +305,7 @@
         IllegalStateException thrown = assertThrows(IllegalStateException.class,
                 () -> mRadioTuner.getProgramList(parameters));
 
-        assertWithMessage("Exception for getting program list when not ready")
+        mExpect.withMessage("Exception for getting program list when not ready")
                 .that(thrown).hasMessageThat().contains("Program list is not ready yet");
     }
 
@@ -308,7 +318,7 @@
         RuntimeException thrown = assertThrows(RuntimeException.class,
                 () -> mRadioTuner.getProgramList(parameters));
 
-        assertWithMessage("Exception for getting program list when service is dead")
+        mExpect.withMessage("Exception for getting program list when service is dead")
                 .that(thrown).hasMessageThat().contains("Service died");
     }
 
@@ -330,7 +340,7 @@
 
         ProgramList nullProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
 
-        assertWithMessage("Exception for radio HAL client not supporting program list")
+        mExpect.withMessage("Exception for radio HAL client not supporting program list")
                 .that(nullProgramList).isNull();
     }
 
@@ -344,7 +354,7 @@
             mRadioTuner.getDynamicProgramList(TEST_FILTER);
         });
 
-        assertWithMessage("Exception for radio HAL client service died")
+        mExpect.withMessage("Exception for radio HAL client service died")
                 .that(thrown).hasMessageThat().contains("Service died");
     }
 
@@ -360,7 +370,7 @@
         verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
         verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(DAB_DMB_SID_EXT_IDENTIFIER);
         verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT).onComplete();
-        assertWithMessage("Program info in program list after adding FM and DAB info")
+        mExpect.withMessage("Program info in program list after adding FM and DAB info")
                 .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_1,
                         DAB_PROGRAM_INFO_2);
     }
@@ -378,7 +388,7 @@
         mTunerCallback.onProgramListUpdated(fmRemovedChunk);
 
         verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
-        assertWithMessage("Program info in program list after removing FM id")
+        mExpect.withMessage("Program info in program list after removing FM id")
                 .that(mProgramList.toList()).containsExactly(DAB_PROGRAM_INFO_1,
                         DAB_PROGRAM_INFO_2);
     }
@@ -397,7 +407,7 @@
 
         verify(mListCallbackMocks[0], after(TIMEOUT_MS).never()).onItemRemoved(
                 DAB_DMB_SID_EXT_IDENTIFIER);
-        assertWithMessage("Program info in program list after removing part of DAB ids")
+        mExpect.withMessage("Program info in program list after removing part of DAB ids")
                 .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_2);
     }
 
@@ -419,7 +429,7 @@
         mTunerCallback.onProgramListUpdated(dabRemovedChunk2);
 
         verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(DAB_DMB_SID_EXT_IDENTIFIER);
-        assertWithMessage("Program info in program list after removing all DAB ids")
+        mExpect.withMessage("Program info in program list after removing all DAB ids")
                 .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO);
     }
 
@@ -448,7 +458,7 @@
 
         verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
         verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(DAB_DMB_SID_EXT_IDENTIFIER);
-        assertWithMessage("Program list after purge chunk applied")
+        mExpect.withMessage("Program list after purge chunk applied")
                 .that(mProgramList.toList()).isEmpty();
     }
 
@@ -607,6 +617,49 @@
         verify(mTunerMock, CALLBACK_TIMEOUT).stopProgramListUpdates();
     }
 
+    @Test
+    public void get() throws Exception {
+        createRadioTuner();
+        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+        registerListCallbacks(/* numCallbacks= */ 1);
+        mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
+
+        mExpect.withMessage(
+                "FM program info in program list after updating with chunk of FM program")
+                .that(mProgramList.get(FM_IDENTIFIER)).isEqualTo(FM_PROGRAM_INFO);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void getProgramInfos() throws Exception {
+        createRadioTuner();
+        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+        registerListCallbacks(/* numCallbacks= */ 1);
+        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);
+        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
+        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(DAB_DMB_SID_EXT_IDENTIFIER);
+
+        mExpect.withMessage("FM program info in program list")
+                .that(mProgramList.getProgramInfos(FM_IDENTIFIER)).containsExactly(FM_PROGRAM_INFO);
+        mExpect.withMessage("All DAB program info in program list")
+                .that(mProgramList.getProgramInfos(DAB_DMB_SID_EXT_IDENTIFIER))
+                .containsExactly(DAB_PROGRAM_INFO_1, DAB_PROGRAM_INFO_2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void getProgramInfos_withIdNotFound() throws Exception {
+        createRadioTuner();
+        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+        registerListCallbacks(/* numCallbacks= */ 1);
+        mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
+
+        mExpect.withMessage("DAB program info in program list")
+                .that(mProgramList.getProgramInfos(DAB_DMB_SID_EXT_IDENTIFIER)).isEmpty();
+    }
+
     private static ProgramSelector createProgramSelector(int programType,
             ProgramSelector.Identifier identifier) {
         return new ProgramSelector(programType, identifier, /* secondaryIds= */ null,
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
index b9f4c3f..03de143 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
@@ -32,8 +32,12 @@
 import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArrayMap;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -42,6 +46,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -90,10 +95,26 @@
     private static final RadioManager.ModuleProperties AMFM_PROPERTIES =
             createAmFmProperties(/* dabFrequencyTable= */ null);
 
+    private static final int DAB_INFO_FLAG_LIVE_VALUE = 1;
+    private static final int DAB_INFO_FLAG_TUNED_VALUE = 1 << 4;
+    private static final int DAB_INFO_FLAG_STEREO_VALUE = 1 << 5;
+    private static final int HD_INFO_FLAG_LIVE_VALUE = 1;
+    private static final int HD_INFO_FLAG_TUNED_VALUE = 1 << 4;
+    private static final int HD_INFO_FLAG_STEREO_VALUE = 1 << 5;
+    private static final int HD_INFO_FLAG_SIGNAL_ACQUISITION_VALUE = 1 << 6;
+    private static final int HD_INFO_FLAG_SIS_ACQUISITION_VALUE = 1 << 7;
     /**
-     * Info flags with live, tuned and stereo enabled
+     * Info flags with live, tuned, and stereo enabled for DAB program
      */
-    private static final int INFO_FLAGS = 0b110001;
+    private static final int INFO_FLAGS_DAB = DAB_INFO_FLAG_LIVE_VALUE | DAB_INFO_FLAG_TUNED_VALUE
+            | DAB_INFO_FLAG_STEREO_VALUE;
+    /**
+     * HD program info flags with live, tuned, stereo enabled, signal acquired, SIS information
+     * available but audio unavailable
+     */
+    private static final int INFO_FLAGS_HD = HD_INFO_FLAG_LIVE_VALUE | HD_INFO_FLAG_TUNED_VALUE
+            | HD_INFO_FLAG_STEREO_VALUE | HD_INFO_FLAG_SIGNAL_ACQUISITION_VALUE
+            | HD_INFO_FLAG_SIS_ACQUISITION_VALUE;
     private static final int SIGNAL_QUALITY = 2;
     private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER =
             new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
@@ -112,9 +133,20 @@
                     new ProgramSelector.Identifier[]{
                             DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER},
                     /* vendorIds= */ null);
+
+    private static final long HD_FREQUENCY = 97_100;
+    private static final ProgramSelector.Identifier HD_STATION_EXT_IDENTIFIER =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT,
+                    /* value= */ (HD_FREQUENCY << 36) | 0x1L);
+    private static final ProgramSelector HD_SELECTOR = new ProgramSelector(
+            ProgramSelector.PROGRAM_TYPE_FM_HD, HD_STATION_EXT_IDENTIFIER,
+            new ProgramSelector.Identifier[]{}, /* vendorIds= */ null);
+
     private static final RadioMetadata METADATA = createMetadata();
     private static final RadioManager.ProgramInfo DAB_PROGRAM_INFO =
             createDabProgramInfo(DAB_SELECTOR);
+    private static final RadioManager.ProgramInfo HD_PROGRAM_INFO = createHdProgramInfo(
+            HD_SELECTOR);
 
     private static final int EVENT_ANNOUNCEMENT_TYPE = Announcement.TYPE_EVENT;
     private static final List<Announcement> TEST_ANNOUNCEMENT_LIST = Arrays.asList(
@@ -135,6 +167,9 @@
     @Mock
     private ICloseHandle mCloseHandleMock;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void getType_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
@@ -927,6 +962,27 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void isSignalAcquired_forProgramInfo() {
+        assertWithMessage("Signal acquisition status for HD program info")
+                .that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void isHdSisAvailable_forProgramInfo() {
+        assertWithMessage("SIS information acquisition status for HD program")
+                .that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void isHdAudioAvailable_forProgramInfo() {
+        assertWithMessage("Audio acquisition status for HD program")
+                .that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse();
+    }
+
+    @Test
     public void getSignalStrength_forProgramInfo() {
         assertWithMessage("Signal strength of DAB program info")
                 .that(DAB_PROGRAM_INFO.getSignalStrength()).isEqualTo(SIGNAL_QUALITY);
@@ -1156,9 +1212,18 @@
     }
 
     private static RadioManager.ProgramInfo createDabProgramInfo(ProgramSelector selector) {
-        return new RadioManager.ProgramInfo(selector, DAB_SID_EXT_IDENTIFIER,
-                DAB_FREQUENCY_IDENTIFIER, Arrays.asList(DAB_SID_EXT_IDENTIFIER_RELATED), INFO_FLAGS,
-                SIGNAL_QUALITY, METADATA, /* vendorInfo= */ null);
+        return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(),
+                DAB_FREQUENCY_IDENTIFIER, Arrays.asList(DAB_SID_EXT_IDENTIFIER_RELATED),
+                INFO_FLAGS_DAB, SIGNAL_QUALITY, METADATA, /* vendorInfo= */ null);
+    }
+
+    private static RadioManager.ProgramInfo createHdProgramInfo(ProgramSelector selector) {
+        long frequency = (selector.getPrimaryId().getValue() >> 32);
+        ProgramSelector.Identifier physicallyTunedToId = new ProgramSelector.Identifier(
+                ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, frequency);
+        return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(), physicallyTunedToId,
+                Collections.emptyList(), INFO_FLAGS_HD, SIGNAL_QUALITY, METADATA,
+                /* vendorInfo= */ null);
     }
 
     private void createRadioManager() throws RemoteException {
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
index e348a51..3891acc 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
@@ -22,12 +22,18 @@
 
 import android.graphics.Bitmap;
 import android.os.Parcel;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
+import java.util.Arrays;
 import java.util.Set;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -35,6 +41,8 @@
 
     private static final int CREATOR_ARRAY_SIZE = 3;
     private static final int INT_KEY_VALUE = 1;
+    private static final String ARTIST_KEY_VALUE = "artistTest";
+    private static final String[] UFIDS_VALUE = new String[]{"ufid1", "ufid2"};
     private static final long TEST_UTC_SECOND_SINCE_EPOCH = 200;
     private static final int TEST_TIME_ZONE_OFFSET_MINUTES = 1;
 
@@ -43,6 +51,9 @@
     @Mock
     private Bitmap mBitmapValue;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void describeContents_forClock() {
         RadioMetadata.Clock clock = new RadioMetadata.Clock(TEST_UTC_SECOND_SINCE_EPOCH,
@@ -97,7 +108,7 @@
             mBuilder.putInt(invalidIntKey, INT_KEY_VALUE);
         });
 
-        assertWithMessage("Exception for putting illegal int-value key %s", invalidIntKey)
+        assertWithMessage("Exception for putting illegal int-value for key %s", invalidIntKey)
                 .that(thrown).hasMessageThat()
                 .matches(".*" + invalidIntKey + ".*cannot.*int.*?");
     }
@@ -117,6 +128,42 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void putStringArray_withIllegalKey_throwsException() {
+        String invalidStringArrayKey = RadioMetadata.METADATA_KEY_HD_STATION_NAME_LONG;
+
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+            mBuilder.putStringArray(invalidStringArrayKey, UFIDS_VALUE);
+        });
+
+        assertWithMessage("Exception for putting illegal string-array-value for key %s",
+                invalidStringArrayKey).that(thrown).hasMessageThat()
+                .matches(".*" + invalidStringArrayKey + ".*cannot.*Array.*?");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void putStringArray_withNullKey_throwsException() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+            mBuilder.putStringArray(/* key= */ null, UFIDS_VALUE);
+        });
+
+        assertWithMessage("Exception for putting string-array with null key")
+                .that(thrown).hasMessageThat().contains("can not be null");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void putStringArray_withNullString_throwsException() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+            mBuilder.putStringArray(RadioMetadata.METADATA_KEY_UFIDS, /* value= */ null);
+        });
+
+        assertWithMessage("Exception for putting null string-array")
+                .that(thrown).hasMessageThat().contains("can not be null");
+    }
+
+    @Test
     public void containsKey_withKeyInMetadata() {
         String key = RadioMetadata.METADATA_KEY_RDS_PI;
         RadioMetadata metadata = mBuilder.putInt(key, INT_KEY_VALUE).build();
@@ -156,11 +203,10 @@
     @Test
     public void getString_withKeyInMetadata() {
         String key = RadioMetadata.METADATA_KEY_ARTIST;
-        String value = "artistTest";
-        RadioMetadata metadata = mBuilder.putString(key, value).build();
+        RadioMetadata metadata = mBuilder.putString(key, ARTIST_KEY_VALUE).build();
 
         assertWithMessage("String value for key %s in metadata", key)
-                .that(metadata.getString(key)).isEqualTo(value);
+                .that(metadata.getString(key)).isEqualTo(ARTIST_KEY_VALUE);
     }
 
     @Test
@@ -235,10 +281,62 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void getStringArray_withKeyInMetadata() {
+        String key = RadioMetadata.METADATA_KEY_UFIDS;
+        RadioMetadata metadata = mBuilder.putStringArray(key, UFIDS_VALUE).build();
+
+        assertWithMessage("String-array value for key %s not in metadata", key)
+                .that(metadata.getStringArray(key)).asList().isEqualTo(Arrays.asList(UFIDS_VALUE));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void getStringArray_withKeyNotInMetadata() {
+        String key = RadioMetadata.METADATA_KEY_UFIDS;
+        RadioMetadata metadata = mBuilder.build();
+
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+            metadata.getStringArray(key);
+        });
+
+        assertWithMessage("Exception for getting string array for string-array value for key %s "
+                + "not in metadata", key).that(thrown).hasMessageThat().contains("not found");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void getStringArray_withNullKey() {
+        RadioMetadata metadata = mBuilder.build();
+
+        NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+            metadata.getStringArray(/* key= */ null);
+        });
+
+        assertWithMessage("Exception for getting string array with null key")
+                .that(thrown).hasMessageThat().contains("can not be null");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void getStringArray_withInvalidKey() {
+        String invalidClockKey = RadioMetadata.METADATA_KEY_HD_STATION_NAME_LONG;
+        RadioMetadata metadata = mBuilder.build();
+
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+            metadata.getStringArray(invalidClockKey);
+        });
+
+        assertWithMessage("Exception for getting string array for key %s not of string-array type",
+                invalidClockKey).that(thrown).hasMessageThat()
+                .contains("string array");
+    }
+
+    @Test
     public void size_withNonEmptyMetadata() {
         RadioMetadata metadata = mBuilder
                 .putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
-                .putString(RadioMetadata.METADATA_KEY_ARTIST, "artistTest")
+                .putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
                 .build();
 
         assertWithMessage("Size of fields in non-empty metadata")
@@ -257,7 +355,7 @@
     public void keySet_withNonEmptyMetadata() {
         RadioMetadata metadata = mBuilder
                 .putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
-                .putString(RadioMetadata.METADATA_KEY_ARTIST, "artistTest")
+                .putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
                 .putBitmap(RadioMetadata.METADATA_KEY_ICON, mBitmapValue)
                 .build();
 
@@ -291,7 +389,7 @@
     public void equals_forMetadataWithSameContents_returnsTrue() {
         RadioMetadata metadata = mBuilder
                 .putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
-                .putString(RadioMetadata.METADATA_KEY_ARTIST, "artistTest")
+                .putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
                 .build();
         RadioMetadata.Builder copyBuilder = new RadioMetadata.Builder(metadata);
         RadioMetadata metadataCopied = copyBuilder.build();
@@ -315,10 +413,29 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
     public void writeToParcel_forRadioMetadata() {
         RadioMetadata metadataExpected = mBuilder
                 .putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
-                .putString(RadioMetadata.METADATA_KEY_ARTIST, "artistTest")
+                .putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
+                .build();
+        Parcel parcel = Parcel.obtain();
+
+        metadataExpected.writeToParcel(parcel, /* flags= */ 0);
+        parcel.setDataPosition(0);
+
+        RadioMetadata metadataFromParcel = RadioMetadata.CREATOR.createFromParcel(parcel);
+        assertWithMessage("Radio metadata created from parcel")
+                .that(metadataFromParcel).isEqualTo(metadataExpected);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void writeToParcel_forRadioMetadata_withStringArrayTypeMetadata() {
+        RadioMetadata metadataExpected = mBuilder
+                .putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
+                .putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
+                .putStringArray(RadioMetadata.METADATA_KEY_UFIDS, UFIDS_VALUE)
                 .build();
         Parcel parcel = Parcel.obtain();
 
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
index 6a6a951..7ca806b 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -35,9 +36,14 @@
 import android.graphics.Bitmap;
 import android.os.Build;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
 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;
@@ -77,6 +83,9 @@
     @Mock
     private RadioTuner.Callback mCallbackMock;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION;
@@ -604,6 +613,44 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void isConfigFlagSet_withForceAnalogWhenFmForceAnalogSupported()
+            throws Exception {
+        when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG_FM))
+                .thenReturn(true);
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(false);
+
+        assertWithMessage("Force analog with feature flag enabled and force FM supported")
+                .that(mRadioTuner.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void isConfigFlagSet_withForceAnalogWhenFmForceAnalogNotSupported()
+            throws Exception {
+        when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM))
+                .thenReturn(false);
+        when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(true);
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG_FM)).thenReturn(true);
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(false);
+
+        assertWithMessage("Force analog with feature flag enabled but force FM unsupported")
+                .that(mRadioTuner.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void isConfigFlagSet_withForceAnalogWhenHdRadioImprovedFeatureNotEnabled()
+            throws Exception {
+        when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(false);
+
+        assertWithMessage("Force analog without Force FM enabled")
+                .that(mRadioTuner.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).isFalse();
+    }
+
+    @Test
     public void isConfigFlagSet_whenServiceDied_fails() throws Exception {
         when(mTunerMock.isConfigFlagSet(anyInt())).thenThrow(new RemoteException());
 
@@ -636,6 +683,43 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void setConfigFlag_withForceAnalogWhenFmForceAnalogSupported() throws Exception {
+        when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
+
+        mRadioTuner.setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, /* value= */ false);
+
+        verify(mTunerMock, never()).setConfigFlag(eq(RadioManager.CONFIG_FORCE_ANALOG),
+                anyBoolean());
+        verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG_FM, false);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void setConfigFlag_withForceAnalogWhenFmForceAnalogNotSupported() throws Exception {
+        when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
+        when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM))
+                .thenReturn(false);
+
+        mRadioTuner.setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, /* value= */ false);
+
+        verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, false);
+        verify(mTunerMock, never()).setConfigFlag(eq(RadioManager.CONFIG_FORCE_ANALOG_FM),
+                anyBoolean());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
+    public void setConfigFlag_withForceAnalogWhenHdRadioImprovedFeatureNotEnabled()
+            throws Exception {
+        when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
+
+        mRadioTuner.setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, /* value= */ false);
+
+        verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, false);
+    }
+
+    @Test
     public void getParameters_forTunerAdapter() throws Exception {
         List<String> parameterKeys = List.of("ParameterKeyMock");
         Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 72969f7..37a499a 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -104,19 +104,15 @@
     private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
 
     private static final Path[] UI_TRACES_PREDUMPED = {
+            Paths.get("/data/misc/perfetto-traces/bugreport/systrace.pftrace"),
             Paths.get("/data/misc/wmtrace/ime_trace_clients.winscope"),
             Paths.get("/data/misc/wmtrace/ime_trace_managerservice.winscope"),
             Paths.get("/data/misc/wmtrace/ime_trace_service.winscope"),
             Paths.get("/data/misc/wmtrace/wm_trace.winscope"),
             Paths.get("/data/misc/wmtrace/wm_log.winscope"),
-            Paths.get("/data/misc/wmtrace/layers_trace.winscope"),
-            Paths.get("/data/misc/wmtrace/transactions_trace.winscope"),
-            Paths.get("/data/misc/wmtrace/transition_trace.winscope"),
+            Paths.get("/data/misc/wmtrace/wm_transition_trace.winscope"),
             Paths.get("/data/misc/wmtrace/shell_transition_trace.winscope"),
     };
-    private static final Path[] UI_TRACES_GENERATED_DURING_BUGREPORT = {
-            Paths.get("/data/misc/wmtrace/layers_trace_from_transactions.winscope"),
-    };
 
     private Handler mHandler;
     private Executor mExecutor;
@@ -210,7 +206,7 @@
 
         mBrm.preDumpUiData();
         waitTillDumpstateExitedOrTimeout();
-        List<File> expectedPreDumpedTraceFiles = copyFilesAsRoot(UI_TRACES_PREDUMPED);
+        List<File> expectedPreDumpedTraceFiles = copyFiles(UI_TRACES_PREDUMPED);
 
         BugreportCallbackImpl callback = new BugreportCallbackImpl();
         mBrm.startBugreport(mBugreportFd, null, fullWithUsePreDumpFlag(), mExecutor,
@@ -225,7 +221,6 @@
         assertFdsAreClosed(mBugreportFd);
 
         assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
-        assertThatBugreportContainsFiles(UI_TRACES_GENERATED_DURING_BUGREPORT);
 
         List<File> actualPreDumpedTraceFiles = extractFilesFromBugreport(UI_TRACES_PREDUMPED);
         assertThatAllFileContentsAreEqual(actualPreDumpedTraceFiles, expectedPreDumpedTraceFiles);
@@ -240,9 +235,9 @@
         // In some corner cases, data dumped as part of the full bugreport could be the same as the
         // pre-dumped data and this test would fail. Hence, here we create fake/artificial
         // pre-dumped data that we know it won't match with the full bugreport data.
-        createFilesWithFakeDataAsRoot(UI_TRACES_PREDUMPED, "system");
+        createFakeTraceFiles(UI_TRACES_PREDUMPED);
 
-        List<File> preDumpedTraceFiles = copyFilesAsRoot(UI_TRACES_PREDUMPED);
+        List<File> preDumpedTraceFiles = copyFiles(UI_TRACES_PREDUMPED);
 
         BugreportCallbackImpl callback = new BugreportCallbackImpl();
         mBrm.startBugreport(mBugreportFd, null, full(), mExecutor,
@@ -257,7 +252,6 @@
         assertFdsAreClosed(mBugreportFd);
 
         assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
-        assertThatBugreportContainsFiles(UI_TRACES_GENERATED_DURING_BUGREPORT);
 
         List<File> actualTraceFiles = extractFilesFromBugreport(UI_TRACES_PREDUMPED);
         assertThatAllFileContentsAreDifferent(preDumpedTraceFiles, actualTraceFiles);
@@ -480,7 +474,32 @@
         return f;
     }
 
-    private static void startPreDumpedUiTraces() {
+    private static void startPreDumpedUiTraces() throws Exception {
+        // Perfetto traces
+        String perfettoConfig =
+                "buffers: {\n"
+                + "    size_kb: 2048\n"
+                + "    fill_policy: RING_BUFFER\n"
+                + "}\n"
+                + "data_sources: {\n"
+                + "    config {\n"
+                + "        name: \"android.surfaceflinger.transactions\"\n"
+                + "    }\n"
+                + "}\n"
+                + "bugreport_score: 10\n";
+        File tmp = createTempFile("tmp", ".cfg");
+        Files.write(perfettoConfig.getBytes(StandardCharsets.UTF_8), tmp);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                "install -m 644 -o root -g root "
+                + tmp.getAbsolutePath() + " /data/misc/perfetto-configs/bugreport-manager-test.cfg"
+        );
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                "perfetto --background-wait"
+                + " --config /data/misc/perfetto-configs/bugreport-manager-test.cfg --txt"
+                + " --out /data/misc/perfetto-traces/not-used.perfetto-trace"
+        );
+
+        // Legacy traces
         InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
                 "cmd input_method tracing start"
         );
@@ -563,19 +582,24 @@
         return extractedFile;
     }
 
-    private static void createFilesWithFakeDataAsRoot(Path[] paths, String owner) throws Exception {
+    private static void createFakeTraceFiles(Path[] paths) throws Exception {
         File src = createTempFile("fake", ".data");
         Files.write("fake data".getBytes(StandardCharsets.UTF_8), src);
 
         for (Path path : paths) {
             InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
-                    "install -m 611 -o " + owner + " -g " + owner
-                    + " " + src.getAbsolutePath() + " " + path.toString()
+                    "install -m 644 -o system -g system "
+                    + src.getAbsolutePath() + " " + path.toString()
             );
         }
+
+        // Dumpstate executes "perfetto --save-for-bugreport" as shell
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                "chown shell:shell /data/misc/perfetto-traces/bugreport/systrace.pftrace"
+        );
     }
 
-    private static List<File> copyFilesAsRoot(Path[] paths) throws Exception {
+    private static List<File> copyFiles(Path[] paths) throws Exception {
         ArrayList<File> files = new ArrayList<File>();
         for (Path src : paths) {
             File dst = createTempFile(src.getFileName().toString(), ".copy");
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 7f3e014..1577d9c1c1 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -857,7 +857,12 @@
             assertEquals(cDay.getPrimaryAccentColor(), cNight.getPrimaryAccentColor());
             assertEquals(cDay.getSecondaryAccentColor(), cNight.getSecondaryAccentColor());
             assertEquals(cDay.getTertiaryAccentColor(), cNight.getTertiaryAccentColor());
-            assertEquals(cDay.getOnAccentTextColor(), cNight.getOnAccentTextColor());
+            assertEquals(cDay.getOnTertiaryAccentTextColor(),
+                    cNight.getOnTertiaryAccentTextColor());
+            assertEquals(cDay.getTertiaryFixedDimAccentColor(),
+                    cNight.getTertiaryFixedDimAccentColor());
+            assertEquals(cDay.getOnTertiaryFixedAccentTextColor(),
+                    cNight.getOnTertiaryFixedAccentTextColor());
             assertEquals(cDay.getProtectionColor(), cNight.getProtectionColor());
             assertEquals(cDay.getContrastColor(), cNight.getContrastColor());
             assertEquals(cDay.getRippleAlpha(), cNight.getRippleAlpha());
@@ -1830,7 +1835,9 @@
         assertThat(c.getPrimaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getSecondaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getTertiaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
-        assertThat(c.getOnAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
+        assertThat(c.getOnTertiaryAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
+        assertThat(c.getTertiaryFixedDimAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
+        assertThat(c.getOnTertiaryFixedAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getErrorColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getContrastColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getRippleAlpha()).isAtLeast(0x00);
@@ -1846,9 +1853,12 @@
         // These colors are only used for emphasized buttons; they do not need contrast
         assertContrastIsAtLeast(c.getSecondaryAccentColor(), c.getBackgroundColor(), 1);
         assertContrastIsAtLeast(c.getTertiaryAccentColor(), c.getBackgroundColor(), 1);
+        assertContrastIsAtLeast(c.getTertiaryFixedDimAccentColor(), c.getBackgroundColor(), 1);
 
         // The text that is used within the accent color DOES need to have contrast
-        assertContrastIsAtLeast(c.getOnAccentTextColor(), c.getTertiaryAccentColor(), 4.5);
+        assertContrastIsAtLeast(c.getOnTertiaryAccentTextColor(), c.getTertiaryAccentColor(), 4.5);
+        assertContrastIsAtLeast(c.getOnTertiaryFixedAccentTextColor(),
+                c.getTertiaryFixedDimAccentColor(), 4.5);
     }
 
     private void resolveColorsInNightMode(boolean nightMode, Notification.Colors c, int rawColor,
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 0f62b1c..930b1a4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -16,14 +16,19 @@
 
 package android.app.servertransaction;
 
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.clearInvocations;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.IDisplayManager;
+import android.os.Handler;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,8 +39,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.function.IntConsumer;
-
 /**
  * Tests for {@link ClientTransactionListenerController}.
  *
@@ -47,30 +50,36 @@
 @Presubmit
 public class ClientTransactionListenerControllerTest {
     @Mock
-    private IntConsumer mDisplayChangeListener;
+    private IDisplayManager mIDisplayManager;
+    @Mock
+    private DisplayManager.DisplayListener mListener;
 
+    private DisplayManagerGlobal mDisplayManager;
+    private Handler mHandler;
     private ClientTransactionListenerController mController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mController = spy(ClientTransactionListenerController.createInstanceForTesting());
+        mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
+        mHandler = getInstrumentation().getContext().getMainThreadHandler();
+        mController = spy(ClientTransactionListenerController.createInstanceForTesting(
+                mDisplayManager));
         doReturn(true).when(mController).isSyncWindowConfigUpdateFlagEnabled();
     }
 
     @Test
-    public void testRegisterDisplayChangeListener() {
-        mController.registerDisplayChangeListener(mDisplayChangeListener, Runnable::run);
+    public void testOnDisplayChanged() throws RemoteException {
+        // Mock IDisplayManager to return a display info to trigger display change.
+        final DisplayInfo newDisplayInfo = new DisplayInfo();
+        doReturn(newDisplayInfo).when(mIDisplayManager).getDisplayInfo(123);
+
+        mDisplayManager.registerDisplayListener(mListener, mHandler,
+                DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null /* packageName */);
 
         mController.onDisplayChanged(123);
+        mHandler.runWithScissors(() -> { }, 0);
 
-        verify(mDisplayChangeListener).accept(123);
-
-        clearInvocations(mDisplayChangeListener);
-        mController.unregisterDisplayChangeListener(mDisplayChangeListener);
-
-        mController.onDisplayChanged(321);
-
-        verify(mDisplayChangeListener, never()).accept(anyInt());
+        verify(mListener).onDisplayChanged(123);
     }
 }
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
new file mode 100644
index 0000000..2ec58d4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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.app.usage;
+
+import static android.view.Surface.ROTATION_90;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.usage.UsageEvents.Event;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ParcelableUsageEventListTest {
+    private static final int SMALL_TEST_EVENT_COUNT = 100;
+    private static final int LARGE_TEST_EVENT_COUNT = 10000;
+
+    private Random mRandom = new Random();
+
+    @Test
+    public void testSmallList() throws Exception {
+        testParcelableUsageEventList(SMALL_TEST_EVENT_COUNT);
+    }
+
+    @Test
+    public void testLargeList() throws Exception {
+        testParcelableUsageEventList(LARGE_TEST_EVENT_COUNT);
+    }
+
+    private void testParcelableUsageEventList(int eventCount) throws Exception {
+        List<Event> smallList = new ArrayList<>();
+        for (int i = 0; i < eventCount; i++) {
+            smallList.add(generateUsageEvent());
+        }
+
+        ParcelableUsageEventList slice;
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeParcelable(new ParcelableUsageEventList(smallList), 0);
+            parcel.setDataPosition(0);
+            slice = parcel.readParcelable(getClass().getClassLoader(),
+                    ParcelableUsageEventList.class);
+        } finally {
+            parcel.recycle();
+        }
+
+        assertNotNull(slice);
+        assertNotNull(slice.getList());
+        assertEquals(eventCount, slice.getList().size());
+
+        for (int i = 0; i < eventCount; i++) {
+            compareUsageEvent(smallList.get(i), slice.getList().get(i));
+        }
+    }
+
+    private Event generateUsageEvent() {
+        final Event event = new Event();
+        event.mEventType = mRandom.nextInt(Event.MAX_EVENT_TYPE + 1);
+        event.mPackage = anyString();
+        event.mClass = anyString();
+        event.mTimeStamp = anyLong();
+        event.mInstanceId = anyInt();
+        event.mTimeStamp = anyLong();
+
+        switch (event.mEventType) {
+            case Event.CONFIGURATION_CHANGE:
+                event.mConfiguration = new Configuration();
+                event.mConfiguration.seq = anyInt();
+                event.mConfiguration.screenLayout = Configuration.SCREENLAYOUT_ROUND_YES;
+                event.mConfiguration.smallestScreenWidthDp = 100;
+                event.mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+                event.mConfiguration.windowConfiguration.setRotation(ROTATION_90);
+                break;
+            case Event.SHORTCUT_INVOCATION:
+                event.mShortcutId = anyString();
+                break;
+            case Event.CHOOSER_ACTION:
+                event.mAction = anyString();
+                event.mContentType = anyString();
+                event.mContentAnnotations = new String[mRandom.nextInt(10)];
+                for (int i = 0; i < event.mContentAnnotations.length; i++) {
+                    event.mContentAnnotations[i] = anyString();
+                }
+                break;
+            case Event.STANDBY_BUCKET_CHANGED:
+                event.mBucketAndReason = anyInt();
+                break;
+            case Event.NOTIFICATION_INTERRUPTION:
+                event.mNotificationChannelId = anyString();
+                break;
+            case Event.LOCUS_ID_SET:
+                event.mLocusId = anyString();
+                break;
+        }
+
+        event.mFlags = anyInt();
+        return event;
+    }
+
+    private static void compareUsageEvent(Event ue1, Event ue2) {
+        assertEquals(ue1.mPackage, ue2.mPackage);
+        assertEquals(ue1.mClass, ue2.mClass);
+        assertEquals(ue1.mTaskRootPackage, ue2.mTaskRootPackage);
+        assertEquals(ue1.mTaskRootClass, ue2.mTaskRootClass);
+        assertEquals(ue1.mInstanceId, ue2.mInstanceId);
+        assertEquals(ue1.mEventType, ue2.mEventType);
+        assertEquals(ue1.mTimeStamp, ue2.mTimeStamp);
+
+        switch (ue1.mEventType) {
+            case Event.CONFIGURATION_CHANGE:
+                assertEquals(ue1.mConfiguration, ue2.mConfiguration);
+                break;
+            case Event.SHORTCUT_INVOCATION:
+                assertEquals(ue1.mShortcutId, ue2.mShortcutId);
+                break;
+            case Event.CHOOSER_ACTION:
+                assertEquals(ue1.mAction, ue2.mAction);
+                assertEquals(ue1.mContentType, ue2.mContentType);
+                assertTrue(Arrays.equals(ue1.mContentAnnotations, ue2.mContentAnnotations));
+                break;
+            case Event.STANDBY_BUCKET_CHANGED:
+                assertEquals(ue1.mBucketAndReason, ue2.mBucketAndReason);
+                break;
+            case Event.NOTIFICATION_INTERRUPTION:
+                assertEquals(ue1.mNotificationChannelId, ue1.mNotificationChannelId);
+                break;
+            case Event.LOCUS_ID_SET:
+                assertEquals(ue1.mLocusId, ue2.mLocusId);
+                break;
+        }
+
+        assertEquals(ue1.mFlags, ue2.mFlags);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java
deleted file mode 100644
index d8c9baa..0000000
--- a/core/tests/coretests/src/android/content/BrickDeniedTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2008 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.content;
-
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.SmallTest;
-
-/** Test to make sure brick intents <b>don't</b> work without permission. */
-public class BrickDeniedTest extends AndroidTestCase {
-    @SmallTest
-    public void testBrick() {
-        // Try both the old and new brick intent names.  Neither should work,
-        // since this test application doesn't have the required permission.
-        // If it does work, well, the test certainly won't pass.
-        getContext().sendBroadcast(new Intent("SHES_A_BRICK_HOUSE"));
-        getContext().sendBroadcast(new Intent("android.intent.action.BRICK"));
-    }
-}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index c2e6b60c..969ae8e 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -16,12 +16,19 @@
 
 package android.hardware.display;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -37,6 +44,13 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Tests for {@link DisplayManagerGlobal}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:DisplayManagerGlobalTest
+ */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DisplayManagerGlobalTest {
@@ -51,6 +65,9 @@
     @Mock
     private DisplayManager.DisplayListener mListener;
 
+    @Mock
+    private DisplayManager.DisplayListener mListener2;
+
     @Captor
     private ArgumentCaptor<IDisplayManagerCallback> mCallbackCaptor;
 
@@ -82,7 +99,11 @@
         Mockito.verifyNoMoreInteractions(mListener);
 
         Mockito.reset(mListener);
-        callback.onDisplayEvent(1, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+        // Mock IDisplayManager to return a different display info to trigger display change.
+        final DisplayInfo newDisplayInfo = new DisplayInfo();
+        newDisplayInfo.rotation++;
+        doReturn(newDisplayInfo).when(mDisplayManager).getDisplayInfo(displayId);
+        callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
         waitForHandler();
         Mockito.verify(mListener).onDisplayChanged(eq(displayId));
         Mockito.verifyNoMoreInteractions(mListener);
@@ -161,7 +182,44 @@
         mDisplayManagerGlobal.unregisterDisplayListener(mListener);
         inOrder.verify(mDisplayManager)
                 .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(0L));
+    }
 
+    @Test
+    public void testHandleDisplayChangeFromWindowManager() throws RemoteException {
+        // Mock IDisplayManager to return a display info to trigger display change.
+        final DisplayInfo newDisplayInfo = new DisplayInfo();
+        doReturn(newDisplayInfo).when(mDisplayManager).getDisplayInfo(123);
+        doReturn(newDisplayInfo).when(mDisplayManager).getDisplayInfo(321);
+
+        // Nothing happens when there is no listener.
+        mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(123);
+
+        // One listener listens on add/remove, and the other one listens on change.
+        mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
+                DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, null /* packageName */);
+        mDisplayManagerGlobal.registerDisplayListener(mListener2, mHandler,
+                DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null /* packageName */);
+
+        mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
+        waitForHandler();
+
+        verify(mListener, never()).onDisplayChanged(anyInt());
+        verify(mListener2).onDisplayChanged(321);
+
+        // Trigger the callback again even if the display info is not changed.
+        clearInvocations(mListener2);
+        mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
+        waitForHandler();
+
+        verify(mListener2).onDisplayChanged(321);
+
+        // No callback for non-existing display (no display info returned from IDisplayManager).
+        clearInvocations(mListener2);
+        mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(456);
+        waitForHandler();
+
+        verify(mListener2, never()).onDisplayChanged(anyInt());
     }
 
     private void waitForHandler() {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
index 06b62f8..dd116b5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
@@ -33,6 +33,7 @@
 
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.endsWith;
+import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -119,6 +120,11 @@
 
     @Before
     public void setUp() throws Exception {
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+        assumeFalse("AccessibilityShortcutChooserActivity not supported on watch",
+                pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
+
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         mDevice.wakeUp();
         when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo);
@@ -138,7 +144,9 @@
 
     @After
     public void cleanUp() {
-        mScenario.close();
+        if (mScenario != null) {
+            mScenario.close();
+        }
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 8fa6376..77202d1 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -21,16 +21,12 @@
 import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
 import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.annotation.XmlRes;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
-import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -540,66 +536,4 @@
     private void assertEquals(double expected, double actual) {
         Assert.assertEquals(expected, actual, 0.1);
     }
-
-    @Test
-    public void powerBrackets_specifiedInPowerProfile() {
-        mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
-        mProfile.initCpuPowerBrackets(8);
-
-        int cpuPowerBracketCount = mProfile.getCpuPowerBracketCount();
-        assertThat(cpuPowerBracketCount).isEqualTo(2);
-        assertThat(new int[]{
-                mProfile.getCpuPowerBracketForScalingStep(0, 0),
-                mProfile.getCpuPowerBracketForScalingStep(4, 0),
-                mProfile.getCpuPowerBracketForScalingStep(4, 1),
-        }).isEqualTo(new int[]{1, 1, 0});
-    }
-
-    @Test
-    public void powerBrackets_automatic() {
-        mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
-        CpuScalingPolicies scalingPolicies = new CpuScalingPolicies(
-                new SparseArray<>() {{
-                    put(0, new int[]{0, 1, 2});
-                    put(3, new int[]{3, 4});
-                }},
-                new SparseArray<>() {{
-                    put(0, new int[]{300000, 1000000, 2000000});
-                    put(3, new int[]{300000, 1000000, 2500000, 3000000});
-                }});
-
-        assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(3);
-        assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 0))
-                .isEqualTo("0/300(10.0)");
-        assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 1))
-                .isEqualTo("0/1000(20.0), 0/2000(30.0), 3/300(25.0)");
-        assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 2))
-                .isEqualTo("3/1000(35.0), 3/2500(50.0), 3/3000(60.0)");
-        assertThat(new int[]{
-                mProfile.getCpuPowerBracketForScalingStep(0, 0),
-                mProfile.getCpuPowerBracketForScalingStep(0, 1),
-                mProfile.getCpuPowerBracketForScalingStep(0, 2),
-                mProfile.getCpuPowerBracketForScalingStep(3, 0),
-                mProfile.getCpuPowerBracketForScalingStep(3, 1),
-                mProfile.getCpuPowerBracketForScalingStep(3, 2),
-                mProfile.getCpuPowerBracketForScalingStep(3, 3),
-        }).isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2});
-    }
-
-    @Test
-    public void powerBrackets_moreBracketsThanStates() {
-        mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
-        mProfile.initCpuPowerBrackets(8);
-
-        assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(7);
-        assertThat(new int[]{
-                mProfile.getCpuPowerBracketForScalingStep(0, 0),
-                mProfile.getCpuPowerBracketForScalingStep(0, 1),
-                mProfile.getCpuPowerBracketForScalingStep(0, 2),
-                mProfile.getCpuPowerBracketForScalingStep(3, 0),
-                mProfile.getCpuPowerBracketForScalingStep(3, 1),
-                mProfile.getCpuPowerBracketForScalingStep(3, 2),
-                mProfile.getCpuPowerBracketForScalingStep(3, 3),
-        }).isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6});
-    }
 }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index cc73ece..35498b7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2365,6 +2365,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
+    "33989965": {
+      "message": " Met condition %s for #%d (%d left)",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "34106798": {
       "message": "Content Recording: Display %d state was (%d), is now (%d), so update recording?",
       "level": "VERBOSE",
@@ -4483,6 +4489,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
+    "2053743391": {
+      "message": " Add condition %s for #%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "2060978050": {
       "message": "moveWindowTokenToDisplay: Attempted to move token: %s to non-exiting displayId=%d",
       "level": "WARN",
@@ -4543,6 +4555,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "2124732293": {
+      "message": "#%d: Met condition: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "2128917433": {
       "message": "onProposedRotationChanged, rotation=%d",
       "level": "VERBOSE",
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 4eaa013..92c4de6 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -27,6 +27,9 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.Size;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
@@ -614,6 +617,7 @@
         mCompatScaling = mInvCompatScaling = 1;
         setTextLocales(LocaleList.getAdjustedDefault());
         mColor = Color.pack(Color.BLACK);
+        resetElegantTextHeight();
     }
 
     /**
@@ -654,7 +658,7 @@
 
         mBidiFlags = BIDI_DEFAULT_LTR;
         setTextLocales(LocaleList.getAdjustedDefault());
-        setElegantTextHeight(false);
+        resetElegantTextHeight();
         mFontFeatureSettings = null;
         mFontVariationSettings = null;
 
@@ -1735,12 +1739,30 @@
     /**
      * Get the elegant metrics flag.
      *
+     * From API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default value will be true by
+     * default if the app has a target SDK of API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or
+     * later.
+     *
      * @return true if elegant metrics are enabled for text drawing.
      */
     public boolean isElegantTextHeight() {
-        return nIsElegantTextHeight(mNativePaint);
+        int rawValue = nGetElegantTextHeight(mNativePaint);
+        switch (rawValue) {
+            case ELEGANT_TEXT_HEIGHT_DISABLED:
+                return false;
+            case ELEGANT_TEXT_HEIGHT_ENABLED:
+                return true;
+            case ELEGANT_TEXT_HEIGHT_UNSET:
+            default:
+                return com.android.text.flags.Flags.deprecateUiFonts();
+        }
     }
 
+    // Note: the following three values must be equal to the ones in the JNI file: Paint.cpp
+    private static final int ELEGANT_TEXT_HEIGHT_UNSET = -1;
+    private static final int ELEGANT_TEXT_HEIGHT_ENABLED = 0;
+    private static final int ELEGANT_TEXT_HEIGHT_DISABLED = 1;
+
     /**
      * Set the paint's elegant height metrics flag. This setting selects font
      * variants that have not been compacted to fit Latin-based vertical
@@ -1749,7 +1771,29 @@
      * @param elegant set the paint's elegant metrics flag for drawing text.
      */
     public void setElegantTextHeight(boolean elegant) {
-        nSetElegantTextHeight(mNativePaint, elegant);
+        nSetElegantTextHeight(mNativePaint,
+                elegant ? ELEGANT_TEXT_HEIGHT_ENABLED : ELEGANT_TEXT_HEIGHT_DISABLED);
+    }
+
+    /**
+     * A change ID for deprecating UI fonts.
+     *
+     * From API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default value will be true by
+     * default if the app has a target SDK of API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or
+     * later.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long DEPRECATE_UI_FONT = 279646685L;
+
+    private void resetElegantTextHeight() {
+        if (CompatChanges.isChangeEnabled(DEPRECATE_UI_FONT)) {
+            nSetElegantTextHeight(mNativePaint, ELEGANT_TEXT_HEIGHT_UNSET);
+        } else {
+            nSetElegantTextHeight(mNativePaint, ELEGANT_TEXT_HEIGHT_DISABLED);
+        }
     }
 
     /**
@@ -3660,9 +3704,9 @@
     @CriticalNative
     private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
     @CriticalNative
-    private static native boolean nIsElegantTextHeight(long paintPtr);
+    private static native int nGetElegantTextHeight(long paintPtr);
     @CriticalNative
-    private static native void nSetElegantTextHeight(long paintPtr, boolean elegant);
+    private static native void nSetElegantTextHeight(long paintPtr, int elegant);
     @CriticalNative
     private static native float nGetTextSize(long paintPtr);
     @CriticalNative
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index dc1773b..62195856 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -17,11 +17,17 @@
 package android.graphics.text;
 
 import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+import static com.android.text.flags.Flags.FLAG_WORD_STYLE_AUTO;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
+import android.os.LocaleList;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -37,6 +43,14 @@
 public final class LineBreakConfig {
 
     /**
+     * A feature ID for automatic line break word style.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long WORD_STYLE_AUTO = 280005585L;
+
+    /**
      * No hyphenation preference is specified.
      *
      * <p>
@@ -112,8 +126,11 @@
      * </pre>
      *
      * <p>
-     * This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if this value is used for text
-     * layout/rendering.
+     * This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if the target SDK version is API
+     * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or before and this value is used for text
+     * layout/rendering. This value is resolved to {@link #LINE_BREAK_STYLE_AUTO} if the target SDK
+     * version is API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or after and this value is
+     * used for text layout/rendering.
      */
     @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
     public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1;
@@ -154,10 +171,29 @@
     @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
     public static final int LINE_BREAK_STYLE_NO_BREAK = 4;
 
+    /**
+     * A special value for the line breaking style option.
+     *
+     * <p>
+     * The auto option for the line break style set the line break style based on the locale of the
+     * text rendering context. You can specify the context locale by
+     * {@link android.widget.TextView#setTextLocales(LocaleList)} or
+     * {@link android.graphics.Paint#setTextLocales(LocaleList)}.
+     *
+     * <p>
+     * In the API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, auto option does followings:
+     * - If at least one locale in the locale list contains Japanese script, this option is
+     * equivalent to {@link #LINE_BREAK_STYLE_STRICT}.
+     * - Otherwise, this option is equivalent to {@link #LINE_BREAK_STYLE_NONE}.
+     */
+    @FlaggedApi(FLAG_WORD_STYLE_AUTO)
+    public static final int LINE_BREAK_STYLE_AUTO = 5;
+
     /** @hide */
     @IntDef(prefix = { "LINE_BREAK_STYLE_" }, value = {
             LINE_BREAK_STYLE_NONE, LINE_BREAK_STYLE_LOOSE, LINE_BREAK_STYLE_NORMAL,
-            LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED, LINE_BREAK_STYLE_NO_BREAK
+            LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED, LINE_BREAK_STYLE_NO_BREAK,
+            LINE_BREAK_STYLE_AUTO
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface LineBreakStyle {}
@@ -183,8 +219,11 @@
      *     // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
      * </pre>
      *
-     * This value is resolved to {@link #LINE_BREAK_WORD_STYLE_NONE} if this value is used for
-     * text layout/rendering.
+     * This value is resolved to {@link #LINE_BREAK_WORD_STYLE_NONE} if the target SDK version is
+     * API {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or before and this value is used for text
+     * layout/rendering. This value is resolved to {@link #LINE_BREAK_WORD_STYLE_AUTO} if the target
+     * SDK version is API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or after and this value is
+     * used for text layout/rendering.
      */
     @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
     public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1;
@@ -204,9 +243,29 @@
      */
     public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1;
 
+    /**
+     * A special value for the line breaking word style option.
+     *
+     * <p>
+     * The auto option for the line break word style does some heuristics based on locales and line
+     * count.
+     *
+     * <p>
+     * In the API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, auto option does followings:
+     * - If at least one locale in the locale list contains Korean script, this option is equivalent
+     * to {@link #LINE_BREAK_WORD_STYLE_PHRASE}.
+     * - If not, then if at least one locale in the locale list contains Japanese script, this
+     * option is equivalent to {@link #LINE_BREAK_WORD_STYLE_PHRASE} if the result of its line
+     * count is less than 5 lines.
+     * - Otherwise, this option is equivalent to {@link #LINE_BREAK_WORD_STYLE_NONE}.
+     */
+    @FlaggedApi(FLAG_WORD_STYLE_AUTO)
+    public static final int LINE_BREAK_WORD_STYLE_AUTO = 2;
+
     /** @hide */
     @IntDef(prefix = { "LINE_BREAK_WORD_STYLE_" }, value = {
-        LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE, LINE_BREAK_WORD_STYLE_UNSPECIFIED
+        LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE, LINE_BREAK_WORD_STYLE_UNSPECIFIED,
+            LINE_BREAK_WORD_STYLE_AUTO
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface LineBreakWordStyle {}
@@ -425,11 +484,13 @@
      * @hide
      */
     public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) {
+        final int defaultStyle = CompatChanges.isChangeEnabled(WORD_STYLE_AUTO)
+                ? LINE_BREAK_STYLE_AUTO : LINE_BREAK_STYLE_NONE;
         if (config == null) {
-            return LINE_BREAK_STYLE_NONE;
+            return defaultStyle;
         }
         return config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED
-                ? LINE_BREAK_STYLE_NONE : config.mLineBreakStyle;
+                ? defaultStyle : config.mLineBreakStyle;
     }
 
     /**
@@ -451,11 +512,13 @@
      */
     public static @LineBreakWordStyle int getResolvedLineBreakWordStyle(
             @Nullable LineBreakConfig config) {
+        final int defaultWordStyle = CompatChanges.isChangeEnabled(WORD_STYLE_AUTO)
+                ? LINE_BREAK_WORD_STYLE_AUTO : LINE_BREAK_WORD_STYLE_NONE;
         if (config == null) {
-            return LINE_BREAK_WORD_STYLE_NONE;
+            return defaultWordStyle;
         }
         return config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED
-                ? LINE_BREAK_WORD_STYLE_NONE : config.mLineBreakWordStyle;
+                ? defaultWordStyle : config.mLineBreakWordStyle;
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 49606f0..7743ad5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -1763,6 +1763,15 @@
             return;
         }
 
+        if (container.isFinished()) {
+            return;
+        }
+
+        if (container.isOverlay()) {
+            updateOverlayContainer(wct, container);
+            return;
+        }
+
         if (launchPlaceholderIfNecessary(wct, container)) {
             // Placeholder was launched, the positions will be updated when the activity is added
             // to the secondary container.
@@ -1783,6 +1792,25 @@
         updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */);
     }
 
+
+    @VisibleForTesting
+    // Suppress GuardedBy warning because lint ask to mark this method as
+    // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+    @SuppressWarnings("GuardedBy")
+    @GuardedBy("mLock")
+    void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
+                                @NonNull TaskFragmentContainer container) {
+        final TaskContainer taskContainer = container.getTaskContainer();
+        // Dismiss the overlay container if it's the only container in the task and there's no
+        // direct activity in the parent task.
+        if (taskContainer.getTaskFragmentContainers().size() == 1
+                && !taskContainer.hasDirectActivity()) {
+            container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+        }
+
+        // TODO(b/295805054): Add the logic to update overlay container
+    }
+
     /**
      * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
      * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 9e53380..eeb3ccf 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -75,6 +75,8 @@
 
     private boolean mIsVisible;
 
+    private boolean mHasDirectActivity;
+
     /**
      * TaskFragments that the organizer has requested to be closed. They should be removed when
      * the organizer receives
@@ -102,8 +104,9 @@
         mConfiguration = taskProperties.getConfiguration();
         mDisplayId = taskProperties.getDisplayId();
         // Note that it is always called when there's a new Activity is started, which implies
-        // the host task is visible.
+        // the host task is visible and has an activity in the task.
         mIsVisible = true;
+        mHasDirectActivity = true;
     }
 
     int getTaskId() {
@@ -118,6 +121,10 @@
         return mIsVisible;
     }
 
+    boolean hasDirectActivity() {
+        return mHasDirectActivity;
+    }
+
     @NonNull
     TaskProperties getTaskProperties() {
         return new TaskProperties(mDisplayId, mConfiguration);
@@ -127,6 +134,7 @@
         mConfiguration.setTo(info.getConfiguration());
         mDisplayId = info.getDisplayId();
         mIsVisible = info.isVisible();
+        mHasDirectActivity = info.hasDirectActivity();
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 405f0b2..e74d5fb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -58,6 +58,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentParentInfo;
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.NonNull;
@@ -413,6 +414,33 @@
                 .isEqualTo(overlayContainer);
     }
 
+    @Test
+    public void testUpdateContainer_dontInvokeUpdateOverlayForNonOverlayContainer() {
+        TaskFragmentContainer taskFragmentContainer = createMockTaskFragmentContainer(mActivity);
+
+        mSplitController.updateContainer(mTransaction, taskFragmentContainer);
+        verify(mSplitController, never()).updateOverlayContainer(any(), any());
+    }
+
+    @Test
+    public void testUpdateOverlayContainer_dismissOverlayIfNeeded() {
+        TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+
+        mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
+
+        final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+        assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
+
+        taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+
+        mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
+
+        assertWithMessage("The overlay must be dismissed since there's no activity"
+                + " in the task and other taskFragment.")
+                .that(taskContainer.getTaskFragmentContainers()).isEmpty();
+    }
+
     /**
      * A simplified version of {@link SplitController.ActivityStartMonitor
      * #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 96839c5..02031a6 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1139,7 +1139,7 @@
     public void testOnTransactionReady_taskFragmentParentInfoChanged() {
         final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
-                DEFAULT_DISPLAY, true);
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */);
         transaction.addChange(new TaskFragmentTransaction.Change(
                 TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
                 .setTaskId(TASK_ID)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 2188996..e3f5169 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -79,14 +79,14 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
 
         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
                 taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
 
         assertEquals(WINDOWING_MODE_FREEFORM,
                 taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
@@ -106,13 +106,13 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
 
         assertFalse(taskContainer.isInPictureInPicture());
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
 
         assertTrue(taskContainer.isInPictureInPicture());
     }
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 7c0d0e3..51c71b1 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -20,3 +20,10 @@
     description: "Enables desktop windowing"
     bug: "304778354"
 }
+
+flag {
+    name: "enable_split_contextual"
+    namespace: "multitasking"
+    description: "Enables invoking split contextually"
+    bug: "276361926"
+}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index fa56516..c525a29 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -35,8 +35,8 @@
 
         <ImageView
             android:id="@+id/application_icon"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
+            android:layout_width="@dimen/desktop_mode_caption_icon_radius"
+            android:layout_height="@dimen/desktop_mode_caption_icon_radius"
             android:layout_gravity="center_vertical"
             android:contentDescription="@string/app_icon_text" />
 
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index ee9f070..c6f85a0 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -22,6 +22,7 @@
     android:orientation="vertical">
 
     <LinearLayout
+        android:id="@+id/app_info_pill"
         android:layout_width="match_parent"
         android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
         android:layout_marginTop="@dimen/desktop_mode_handle_menu_margin_top"
@@ -33,10 +34,10 @@
 
         <ImageView
             android:id="@+id/application_icon"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:layout_marginStart="14dp"
-            android:layout_marginEnd="14dp"
+            android:layout_width="@dimen/desktop_mode_caption_icon_radius"
+            android:layout_height="@dimen/desktop_mode_caption_icon_radius"
+            android:layout_marginStart="12dp"
+            android:layout_marginEnd="12dp"
             android:contentDescription="@string/app_icon_text"/>
 
         <TextView
@@ -66,6 +67,7 @@
     </LinearLayout>
 
     <LinearLayout
+        android:id="@+id/windowing_pill"
         android:layout_width="match_parent"
         android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
         android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
@@ -116,6 +118,7 @@
     </LinearLayout>
 
     <LinearLayout
+        android:id="@+id/more_actions_pill"
         android:layout_width="match_parent"
         android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
         android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1f6f7ae..c4be384 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -454,6 +454,9 @@
     <!-- The radius of the caption menu corners. -->
     <dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
 
+    <!-- The radius of the caption menu icon. -->
+    <dimen name="desktop_mode_caption_icon_radius">28dp</dimen>
+
     <!-- The radius of the caption menu shadow. -->
     <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index ac5ba51e..3660fa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -57,6 +57,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
+import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import android.view.WindowManagerPolicyConstants;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -212,7 +213,8 @@
     private ExpandedViewAnimationController mExpandedViewAnimationController;
 
     private View mScrim;
-    private boolean mScrimAnimating;
+    @Nullable
+    private ViewPropertyAnimator mScrimAnimation;
     private View mManageMenuScrim;
     private FrameLayout mExpandedViewContainer;
 
@@ -748,8 +750,8 @@
             float collapsed = -Math.min(dy, 0);
             mExpandedViewAnimationController.updateDrag((int) collapsed);
 
-            // Update scrim
-            if (!mScrimAnimating) {
+            // Update scrim if it's not animating already
+            if (mScrimAnimation == null) {
                 mScrim.setAlpha(getScrimAlphaForDrag(collapsed));
             }
         }
@@ -768,8 +770,8 @@
             } else {
                 mExpandedViewAnimationController.animateBackToExpanded();
 
-                // Update scrim
-                if (!mScrimAnimating) {
+                // Update scrim if it's not animating already
+                if (mScrimAnimation == null) {
                     showScrim(true, null /* runnable */);
                 }
             }
@@ -2237,26 +2239,27 @@
     private void showScrim(boolean show, Runnable after) {
         AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationStart(Animator animation) {
-                mScrimAnimating = true;
-            }
-
-            @Override
             public void onAnimationEnd(Animator animation) {
-                mScrimAnimating = false;
+                mScrimAnimation = null;
                 if (after != null) {
                     after.run();
                 }
             }
         };
+        if (mScrimAnimation != null) {
+            // Cancel scrim animation if it animates
+            mScrimAnimation.cancel();
+        }
         if (show) {
-            mScrim.animate()
+            mScrimAnimation = mScrim.animate();
+            mScrimAnimation
                     .setInterpolator(ALPHA_IN)
                     .alpha(BUBBLE_EXPANDED_SCRIM_ALPHA)
                     .setListener(listener)
                     .start();
         } else {
-            mScrim.animate()
+            mScrimAnimation = mScrim.animate();
+            mScrimAnimation
                     .alpha(0f)
                     .setInterpolator(ALPHA_OUT)
                     .setListener(listener)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index de03f58..9cd318f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -25,6 +25,7 @@
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE;
@@ -515,7 +516,7 @@
         }
 
         if (backgroundColorForTransition != 0) {
-            addBackgroundColorOnTDA(info, backgroundColorForTransition, startTransaction,
+            addBackgroundColor(info, backgroundColorForTransition, startTransaction,
                     finishTransaction);
         }
 
@@ -546,7 +547,7 @@
         return true;
     }
 
-    private void addBackgroundColorOnTDA(@NonNull TransitionInfo info,
+    private void addBackgroundColor(@NonNull TransitionInfo info,
             @ColorInt int color, @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction) {
         final Color bgColor = Color.valueOf(color);
@@ -558,9 +559,19 @@
                     .setName("animation-background")
                     .setCallsite("DefaultTransitionHandler")
                     .setColorLayer();
-
-            mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
             final SurfaceControl backgroundSurface = colorLayerBuilder.build();
+
+            // Attaching the background surface to the transition root could unexpectedly make it
+            // cover one of the split root tasks. To avoid this, put the background surface just
+            // above the display area when split is on.
+            final boolean isSplitTaskInvolved =
+                    info.getChanges().stream().anyMatch(c-> c.getTaskInfo() != null
+                            && c.getTaskInfo().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW);
+            if (isSplitTaskInvolved) {
+                mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
+            } else {
+                startTransaction.reparent(backgroundSurface, info.getRootLeash());
+            }
             startTransaction.setColor(backgroundSurface, colorArray)
                     .setLayer(backgroundSurface, -1)
                     .show(backgroundSurface);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index ca2c3b4..293b660 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -146,13 +146,13 @@
                 });
             }
         };
+        // If the remote is actually in the same process, then make a copy of parameters since
+        // remote impls assume that they have to clean-up native references.
+        final SurfaceControl.Transaction remoteStartT =
+                copyIfLocal(startTransaction, remote.getRemoteTransition());
+        final TransitionInfo remoteInfo =
+                remoteStartT == startTransaction ? info : info.localRemoteCopy();
         try {
-            // If the remote is actually in the same process, then make a copy of parameters since
-            // remote impls assume that they have to clean-up native references.
-            final SurfaceControl.Transaction remoteStartT =
-                    copyIfLocal(startTransaction, remote.getRemoteTransition());
-            final TransitionInfo remoteInfo =
-                    remoteStartT == startTransaction ? info : info.localRemoteCopy();
             handleDeath(remote.asBinder(), finishCallback);
             remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
             // assume that remote will apply the start transaction.
@@ -160,6 +160,10 @@
             Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
         } catch (RemoteException e) {
             Log.e(Transitions.TAG, "Error running remote transition.", e);
+            if (remoteStartT != startTransaction) {
+                remoteStartT.close();
+            }
+            startTransaction.apply();
             unhandleDeath(remote.asBinder(), finishCallback);
             mRequestedRemotes.remove(transition);
             mMainExecutor.execute(() -> finishCallback.onTransitionFinished(null /* wct */));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index bb262d3..a976584 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -20,6 +20,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.windowingModeToString;
 
+import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
+
 import android.app.ActivityManager;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
@@ -27,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -44,6 +47,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -93,7 +97,8 @@
 
     private ResizeVeil mResizeVeil;
 
-    private Drawable mAppIcon;
+    private Drawable mAppIconDrawable;
+    private Bitmap mAppIconBitmap;
     private CharSequence mAppName;
 
     private ExclusionRegionListener mExclusionRegionListener;
@@ -255,7 +260,7 @@
                         mOnCaptionButtonClickListener,
                         mOnCaptionLongClickListener,
                         mAppName,
-                        mAppIcon
+                        mAppIconBitmap
                 );
             } else {
                 throw new IllegalArgumentException("Unexpected layout resource id");
@@ -362,10 +367,15 @@
         String packageName = mTaskInfo.realActivity.getPackageName();
         PackageManager pm = mContext.getApplicationContext().getPackageManager();
         try {
-            IconProvider provider = new IconProvider(mContext);
-            mAppIcon = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity,
+            final IconProvider provider = new IconProvider(mContext);
+            mAppIconDrawable = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity,
                     PackageManager.ComponentInfoFlags.of(0)));
-            ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
+            final Resources resources = mContext.getResources();
+            final BaseIconFactory factory = new BaseIconFactory(mContext,
+                    resources.getDisplayMetrics().densityDpi,
+                    resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
+            mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT);
+            final ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
                     PackageManager.ApplicationInfoFlags.of(0));
             mAppName = pm.getApplicationLabel(applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
@@ -386,7 +396,7 @@
      * until a resize event calls showResizeVeil below.
      */
     void createResizeVeil() {
-        mResizeVeil = new ResizeVeil(mContext, mAppIcon, mTaskInfo,
+        mResizeVeil = new ResizeVeil(mContext, mAppIconDrawable, mTaskInfo,
                 mSurfaceControlBuilderSupplier, mDisplay, mSurfaceControlTransactionSupplier);
     }
 
@@ -455,26 +465,29 @@
     }
 
     /**
-     * Create and display handle menu window
+     * Create and display handle menu window.
      */
     void createHandleMenu() {
         mHandleMenu = new HandleMenu.Builder(this)
-                .setAppIcon(mAppIcon)
+                .setAppIcon(mAppIconBitmap)
                 .setAppName(mAppName)
                 .setOnClickListener(mOnCaptionButtonClickListener)
                 .setOnTouchListener(mOnCaptionTouchListener)
                 .setLayoutId(mRelayoutParams.mLayoutResId)
                 .setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
                 .setWindowingButtonsVisible(DesktopModeStatus.isEnabled())
+                .setCaptionHeight(mResult.mCaptionHeight)
                 .build();
+        mWindowDecorViewHolder.onHandleMenuOpened();
         mHandleMenu.show();
     }
 
     /**
-     * Close the handle menu window
+     * Close the handle menu window.
      */
     void closeHandleMenu() {
         if (!isHandleMenuActive()) return;
+        mWindowDecorViewHolder.onHandleMenuClosed();
         mHandleMenu.close();
         mHandleMenu = null;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 15f8f1c..1941d66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -29,9 +29,9 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.PointF;
-import android.graphics.drawable.Drawable;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -58,7 +58,7 @@
     private WindowDecoration.AdditionalWindow mHandleMenuWindow;
     private final PointF mHandleMenuPosition = new PointF();
     private final boolean mShouldShowWindowingPill;
-    private final Drawable mAppIcon;
+    private final Bitmap mAppIconBitmap;
     private final CharSequence mAppName;
     private final View.OnClickListener mOnClickListener;
     private final View.OnTouchListener mOnTouchListener;
@@ -71,10 +71,13 @@
     private int mMenuHeight;
     private int mMenuWidth;
 
+    private final int mCaptionHeight;
+
 
     HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
             View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
-            Drawable appIcon, CharSequence appName, boolean shouldShowWindowingPill) {
+            Bitmap appIcon, CharSequence appName, boolean shouldShowWindowingPill,
+            int captionHeight) {
         mParentDecor = parentDecor;
         mContext = mParentDecor.mDecorWindowContext;
         mTaskInfo = mParentDecor.mTaskInfo;
@@ -83,9 +86,10 @@
         mCaptionY = captionY;
         mOnClickListener = onClickListener;
         mOnTouchListener = onTouchListener;
-        mAppIcon = appIcon;
+        mAppIconBitmap = appIcon;
         mAppName = appName;
         mShouldShowWindowingPill = shouldShowWindowingPill;
+        mCaptionHeight = captionHeight;
         loadHandleMenuDimensions();
         updateHandleMenuPillPositions();
     }
@@ -98,6 +102,7 @@
         ssg.addTransaction(t);
         ssg.markSyncReady();
         setupHandleMenu();
+        animateHandleMenu();
     }
 
     private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
@@ -109,6 +114,21 @@
     }
 
     /**
+     * Animates the appearance of the handle menu and its three pills.
+     */
+    private void animateHandleMenu() {
+        final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView();
+        final HandleMenuAnimator handleMenuAnimator = new HandleMenuAnimator(handleMenuView,
+                mMenuWidth, mCaptionHeight);
+        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+            handleMenuAnimator.animateCaptionHandleExpandToOpen();
+        } else {
+            handleMenuAnimator.animateOpen();
+        }
+    }
+
+    /**
      * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
      * pill.
      */
@@ -130,7 +150,7 @@
         final ImageView appIcon = handleMenu.findViewById(R.id.application_icon);
         final TextView appName = handleMenu.findViewById(R.id.application_name);
         collapseBtn.setOnClickListener(mOnClickListener);
-        appIcon.setImageDrawable(mAppIcon);
+        appIcon.setImageBitmap(mAppIconBitmap);
         appName.setText(mAppName);
     }
 
@@ -315,13 +335,14 @@
     static final class Builder {
         private final WindowDecoration mParent;
         private CharSequence mName;
-        private Drawable mAppIcon;
+        private Bitmap mAppIcon;
         private View.OnClickListener mOnClickListener;
         private View.OnTouchListener mOnTouchListener;
         private int mLayoutId;
         private int mCaptionX;
         private int mCaptionY;
         private boolean mShowWindowingPill;
+        private int mCaptionHeight;
 
 
         Builder(@NonNull WindowDecoration parent) {
@@ -333,7 +354,7 @@
             return this;
         }
 
-        Builder setAppIcon(@Nullable Drawable appIcon) {
+        Builder setAppIcon(@Nullable Bitmap appIcon) {
             mAppIcon = appIcon;
             return this;
         }
@@ -364,9 +385,14 @@
             return this;
         }
 
+        Builder setCaptionHeight(int captionHeight) {
+            mCaptionHeight = captionHeight;
+            return this;
+        }
+
         HandleMenu build() {
             return new HandleMenu(mParent, mLayoutId, mCaptionX, mCaptionY, mOnClickListener,
-                    mOnTouchListener, mAppIcon, mName, mShowWindowingPill);
+                    mOnTouchListener, mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
new file mode 100644
index 0000000..531de1f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.windowdecor
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.view.View
+import android.view.View.ALPHA
+import android.view.View.SCALE_X
+import android.view.View.SCALE_Y
+import android.view.View.TRANSLATION_Y
+import android.view.View.TRANSLATION_Z
+import android.view.ViewGroup
+import androidx.core.view.children
+import com.android.wm.shell.R
+import com.android.wm.shell.animation.Interpolators
+
+/** Animates the Handle Menu opening. */
+class HandleMenuAnimator(
+    private val handleMenu: View,
+    private val menuWidth: Int,
+    private val captionHeight: Float
+) {
+    companion object {
+        private const val MENU_Y_TRANSLATION_DURATION: Long = 150
+        private const val HEADER_NONFREEFORM_SCALE_DURATION: Long = 150
+        private const val HEADER_FREEFORM_SCALE_DURATION: Long = 217
+        private const val HEADER_ELEVATION_DURATION: Long = 83
+        private const val HEADER_CONTENT_ALPHA_DURATION: Long = 100
+        private const val BODY_SCALE_DURATION: Long = 180
+        private const val BODY_ALPHA_DURATION: Long = 150
+        private const val BODY_ELEVATION_DURATION: Long = 83
+        private const val BODY_CONTENT_ALPHA_DURATION: Long = 167
+
+        private const val ELEVATION_DELAY: Long = 33
+        private const val HEADER_CONTENT_ALPHA_DELAY: Long = 67
+        private const val BODY_SCALE_DELAY: Long = 50
+        private const val BODY_ALPHA_DELAY: Long = 133
+
+        private const val HALF_INITIAL_SCALE: Float = 0.5f
+        private const val NONFREEFORM_HEADER_INITIAL_SCALE_X: Float = 0.6f
+        private const val NONFREEFORM_HEADER_INITIAL_SCALE_Y: Float = 0.05f
+    }
+
+    private val animators: MutableList<Animator> = mutableListOf()
+
+    private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill)
+    private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill)
+    private val moreActionsPill: ViewGroup = handleMenu.requireViewById(R.id.more_actions_pill)
+
+    /** Animates the opening of the handle menu. */
+    fun animateOpen() {
+        prepareMenuForAnimation()
+        appInfoPillExpand()
+        animateAppInfoPill()
+        animateWindowingPill()
+        animateMoreActionsPill()
+        runAnimations()
+    }
+
+    /**
+     * Animates the opening of the handle menu. The caption handle in full screen and split screen
+     * will expand until it assumes the shape of the app info pill. Then, the other two pills will
+     * appear.
+     */
+    fun animateCaptionHandleExpandToOpen() {
+        prepareMenuForAnimation()
+        captionHandleExpandIntoAppInfoPill()
+        animateAppInfoPill()
+        animateWindowingPill()
+        animateMoreActionsPill()
+        runAnimations()
+    }
+
+    /**
+     * Prepares the handle menu for animation. Presets the opacity of necessary menu components.
+     * Presets pivots of handle menu and body pills for scaling animation.
+     */
+    private fun prepareMenuForAnimation() {
+        // Preset opacity
+        appInfoPill.children.forEach { it.alpha = 0f }
+        windowingPill.alpha = 0f
+        moreActionsPill.alpha = 0f
+
+        // Setup pivots.
+        handleMenu.pivotX = menuWidth / 2f
+        handleMenu.pivotY = 0f
+
+        windowingPill.pivotX = menuWidth / 2f
+        windowingPill.pivotY = appInfoPill.measuredHeight.toFloat()
+
+        moreActionsPill.pivotX = menuWidth / 2f
+        moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat()
+    }
+
+    private fun animateAppInfoPill() {
+        // Header Elevation Animation
+        animators +=
+            ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Z, 1f).apply {
+                startDelay = ELEVATION_DELAY
+                duration = HEADER_ELEVATION_DURATION
+            }
+
+        // Content Opacity Animation
+        appInfoPill.children.forEach {
+            animators +=
+                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
+                    startDelay = HEADER_CONTENT_ALPHA_DELAY
+                    duration = HEADER_CONTENT_ALPHA_DURATION
+                }
+        }
+    }
+
+    private fun captionHandleExpandIntoAppInfoPill() {
+        // Header scaling animation
+        animators +=
+            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X, 1f)
+                .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }
+
+        animators +=
+            ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y, 1f)
+                .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }
+
+        // Downward y-translation animation
+        val yStart: Float = -captionHeight / 2
+        animators +=
+            ObjectAnimator.ofFloat(handleMenu, TRANSLATION_Y, yStart, 0f).apply {
+                duration = MENU_Y_TRANSLATION_DURATION
+            }
+    }
+
+    private fun appInfoPillExpand() {
+        // Header scaling animation
+        animators +=
+            ObjectAnimator.ofFloat(appInfoPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
+                duration = HEADER_FREEFORM_SCALE_DURATION
+            }
+
+        animators +=
+            ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
+                duration = HEADER_FREEFORM_SCALE_DURATION
+            }
+    }
+
+    private fun animateWindowingPill() {
+        // Windowing X & Y Scaling Animation
+        animators +=
+            ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
+                startDelay = BODY_SCALE_DELAY
+                duration = BODY_SCALE_DURATION
+            }
+
+        animators +=
+            ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
+                startDelay = BODY_SCALE_DELAY
+                duration = BODY_SCALE_DURATION
+            }
+
+        // Windowing Opacity Animation
+        animators +=
+            ObjectAnimator.ofFloat(windowingPill, ALPHA, 1f).apply {
+                startDelay = BODY_ALPHA_DELAY
+                duration = BODY_ALPHA_DURATION
+            }
+
+        // Windowing Elevation Animation
+        animators +=
+            ObjectAnimator.ofFloat(windowingPill, TRANSLATION_Z, 1f).apply {
+                startDelay = ELEVATION_DELAY
+                duration = BODY_ELEVATION_DURATION
+            }
+
+        // Windowing Content Opacity Animation
+        windowingPill.children.forEach {
+            animators +=
+                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
+                    startDelay = BODY_ALPHA_DELAY
+                    duration = BODY_CONTENT_ALPHA_DURATION
+                    interpolator = Interpolators.FAST_OUT_SLOW_IN
+                }
+        }
+    }
+
+    private fun animateMoreActionsPill() {
+        // More Actions X & Y Scaling Animation
+        animators +=
+            ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
+                startDelay = BODY_SCALE_DELAY
+                duration = BODY_SCALE_DURATION
+            }
+
+        animators +=
+            ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
+                startDelay = BODY_SCALE_DELAY
+                duration = BODY_SCALE_DURATION
+            }
+
+        // More Actions Opacity Animation
+        animators +=
+            ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 1f).apply {
+                startDelay = BODY_ALPHA_DELAY
+                duration = BODY_ALPHA_DURATION
+            }
+
+        // More Actions Elevation Animation
+        animators +=
+            ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Z, 1f).apply {
+                startDelay = ELEVATION_DELAY
+                duration = BODY_ELEVATION_DURATION
+            }
+
+        // More Actions Content Opacity Animation
+        moreActionsPill.children.forEach {
+            animators +=
+                ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
+                    startDelay = BODY_ALPHA_DELAY
+                    duration = BODY_CONTENT_ALPHA_DURATION
+                    interpolator = Interpolators.FAST_OUT_SLOW_IN
+                }
+        }
+    }
+
+    /** Runs the list of animators concurrently. */
+    private fun runAnimations() {
+        val animatorSet = AnimatorSet()
+        animatorSet.playTogether(animators)
+        animatorSet.start()
+        animators.clear()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 044c033..634b755 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -269,10 +269,10 @@
                     .build();
         }
 
-        final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
+        outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
         final int captionWidth = taskBounds.width();
 
-        startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
+        startT.setWindowCrop(mCaptionContainerSurface, captionWidth, outResult.mCaptionHeight)
                 .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                 .show(mCaptionContainerSurface);
 
@@ -283,7 +283,7 @@
             mCaptionInsetsRect.set(taskBounds);
             if (mIsCaptionVisible) {
                 mCaptionInsetsRect.bottom =
-                        mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
+                        mCaptionInsetsRect.top + outResult.mCaptionHeight + params.mCaptionY;
                 wct.addInsetsSource(mTaskInfo.token,
                         mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
                 wct.addInsetsSource(mTaskInfo.token,
@@ -348,7 +348,7 @@
         // Caption view
         mCaptionWindowManager.setConfiguration(taskConfig);
         final WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(captionWidth, captionHeight,
+                new WindowManager.LayoutParams(captionWidth, outResult.mCaptionHeight,
                         WindowManager.LayoutParams.TYPE_APPLICATION,
                         WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
         lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
@@ -569,6 +569,7 @@
     }
 
     static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
+        int mCaptionHeight;
         int mWidth;
         int mHeight;
         T mRootView;
@@ -576,6 +577,7 @@
         void reset() {
             mWidth = 0;
             mHeight = 0;
+            mCaptionHeight = 0;
             mRootView = null;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 6b59cce..d64312a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -2,7 +2,7 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.res.ColorStateList
-import android.graphics.drawable.Drawable
+import android.graphics.Bitmap
 import android.graphics.drawable.GradientDrawable
 import android.view.View
 import android.view.View.OnLongClickListener
@@ -22,7 +22,7 @@
         onCaptionButtonClickListener: View.OnClickListener,
         onLongClickListener: OnLongClickListener,
         appName: CharSequence,
-        appIcon: Drawable
+        appIconBitmap: Bitmap
 ) : DesktopModeWindowDecorationViewHolder(rootView) {
 
     private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
@@ -44,11 +44,10 @@
         maximizeWindowButton.onLongClickListener = onLongClickListener
         closeWindowButton.setOnTouchListener(onCaptionTouchListener)
         appNameTextView.text = appName
-        appIconImageView.setImageDrawable(appIcon)
+        appIconImageView.setImageBitmap(appIconBitmap)
     }
 
     override fun bindData(taskInfo: RunningTaskInfo) {
-
         val captionDrawable = captionView.background as GradientDrawable
         taskInfo.taskDescription?.statusBarColor?.let {
             captionDrawable.setColor(it)
@@ -63,6 +62,10 @@
         appNameTextView.setTextColor(getCaptionAppNameTextColor(taskInfo))
     }
 
+    override fun onHandleMenuOpened() {}
+
+    override fun onHandleMenuClosed() {}
+
     private fun getCaptionAppNameTextColor(taskInfo: RunningTaskInfo): Int {
         return if (shouldUseLightCaptionColors(taskInfo)) {
             context.getColor(R.color.desktop_mode_caption_app_name_light)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
index 9374ac9..9dc86db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -1,11 +1,13 @@
 package com.android.wm.shell.windowdecor.viewholder
 
+import android.animation.ObjectAnimator
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.res.ColorStateList
 import android.graphics.drawable.GradientDrawable
 import android.view.View
 import android.widget.ImageButton
 import com.android.wm.shell.R
+import com.android.wm.shell.animation.Interpolators
 
 /**
  * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen). It
@@ -17,6 +19,10 @@
         onCaptionButtonClickListener: View.OnClickListener
 ) : DesktopModeWindowDecorationViewHolder(rootView) {
 
+    companion object {
+        private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
+    }
+
     private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
     private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
 
@@ -35,6 +41,14 @@
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
     }
 
+    override fun onHandleMenuOpened() {
+        animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
+    }
+
+    override fun onHandleMenuClosed() {
+        animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
+    }
+
     private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
         return if (shouldUseLightCaptionColors(taskInfo)) {
             context.getColor(R.color.desktop_mode_caption_handle_bar_light)
@@ -42,4 +56,14 @@
             context.getColor(R.color.desktop_mode_caption_handle_bar_dark)
         }
     }
+
+    /** Animate appearance/disappearance of caption handle as the handle menu is animated. */
+    private fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
+        val animator =
+            ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
+                duration = CAPTION_HANDLE_ANIMATION_DURATION
+                interpolator = Interpolators.FAST_OUT_SLOW_IN
+            }
+        animator.start()
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
index 49e8d15..8b405f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
@@ -35,4 +35,10 @@
           }
         } ?: false
   }
+
+    /** Callback when the handle menu is opened. */
+    abstract fun onHandleMenuOpened()
+
+    /** Callback when the handle menu is closed. */
+    abstract fun onHandleMenuClosed()
 }
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index ffb329d..00d049c 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -33,6 +33,14 @@
 #endif  // __ANDROID__
 }
 
+inline bool deprecate_ui_fonts() {
+#ifdef __ANDROID__
+    return com_android_text_flags_deprecate_ui_fonts();
+#else
+    return true;
+#endif  // __ANDROID__
+}
+
 }  // namespace text_feature
 
 }  // namespace android
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index bcfb4c8..7552b56d 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -16,12 +16,15 @@
 
 #include "MinikinUtils.h"
 
-#include <string>
-
 #include <log/log.h>
-
+#include <minikin/FamilyVariant.h>
 #include <minikin/MeasuredText.h>
 #include <minikin/Measurement.h>
+
+#include <optional>
+#include <string>
+
+#include "FeatureFlags.h"
 #include "Paint.h"
 #include "SkPathMeasure.h"
 #include "Typeface.h"
@@ -43,9 +46,17 @@
     minikinPaint.wordSpacing = paint->getWordSpacing();
     minikinPaint.fontFlags = MinikinFontSkia::packFontFlags(font);
     minikinPaint.localeListId = paint->getMinikinLocaleListId();
-    minikinPaint.familyVariant = paint->getFamilyVariant();
     minikinPaint.fontStyle = resolvedFace->fStyle;
     minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
+
+    const std::optional<minikin::FamilyVariant>& familyVariant = paint->getFamilyVariant();
+    if (familyVariant.has_value()) {
+        minikinPaint.familyVariant = familyVariant.value();
+    } else {
+        minikinPaint.familyVariant = text_feature::deprecate_ui_fonts()
+                                             ? minikin::FamilyVariant::ELEGANT
+                                             : minikin::FamilyVariant::DEFAULT;
+    }
     return minikinPaint;
 }
 
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 4a8f3e1..caffdfc 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -94,9 +94,10 @@
 
     uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; }
 
+    void resetFamilyVariant() { mFamilyVariant.reset(); }
     void setFamilyVariant(minikin::FamilyVariant variant) { mFamilyVariant = variant; }
 
-    minikin::FamilyVariant getFamilyVariant() const { return mFamilyVariant; }
+    std::optional<minikin::FamilyVariant> getFamilyVariant() const { return mFamilyVariant; }
 
     void setStartHyphenEdit(uint32_t startHyphen) {
         mHyphenEdit = minikin::packHyphenEdit(
@@ -171,7 +172,7 @@
     float mWordSpacing = 0;
     std::string mFontFeatureSettings;
     uint32_t mMinikinLocaleListId;
-    minikin::FamilyVariant mFamilyVariant;
+    std::optional<minikin::FamilyVariant> mFamilyVariant;
     uint32_t mHyphenEdit = 0;
     // The native Typeface object has the same lifetime of the Java Typeface
     // object. The Java Paint object holds a strong reference to the Java Typeface
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 1463945..8c71d6f 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -935,15 +935,39 @@
         obj->setMinikinLocaleListId(minikinLocaleListId);
     }
 
-    static jboolean isElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+    // Note: Following three values must be equal to the ones in Java file: Paint.java.
+    constexpr jint ELEGANT_TEXT_HEIGHT_UNSET = -1;
+    constexpr jint ELEGANT_TEXT_HEIGHT_ENABLED = 0;
+    constexpr jint ELEGANT_TEXT_HEIGHT_DISABLED = 1;
+
+    static jint getElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        return obj->getFamilyVariant() == minikin::FamilyVariant::ELEGANT;
+        const std::optional<minikin::FamilyVariant>& familyVariant = obj->getFamilyVariant();
+        if (familyVariant.has_value()) {
+            if (familyVariant.value() == minikin::FamilyVariant::ELEGANT) {
+                return ELEGANT_TEXT_HEIGHT_ENABLED;
+            } else {
+                return ELEGANT_TEXT_HEIGHT_DISABLED;
+            }
+        } else {
+            return ELEGANT_TEXT_HEIGHT_UNSET;
+        }
     }
 
-    static void setElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) {
+    static void setElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint value) {
         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        obj->setFamilyVariant(
-                aa ? minikin::FamilyVariant::ELEGANT : minikin::FamilyVariant::DEFAULT);
+        switch (value) {
+            case ELEGANT_TEXT_HEIGHT_ENABLED:
+                obj->setFamilyVariant(minikin::FamilyVariant::ELEGANT);
+                return;
+            case ELEGANT_TEXT_HEIGHT_DISABLED:
+                obj->setFamilyVariant(minikin::FamilyVariant::DEFAULT);
+                return;
+            case ELEGANT_TEXT_HEIGHT_UNSET:
+            default:
+                obj->resetFamilyVariant();
+                return;
+        }
     }
 
     static jfloat getTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
@@ -1178,8 +1202,8 @@
         {"nSetTextAlign", "(JI)V", (void*)PaintGlue::setTextAlign},
         {"nSetTextLocalesByMinikinLocaleListId", "(JI)V",
          (void*)PaintGlue::setTextLocalesByMinikinLocaleListId},
-        {"nIsElegantTextHeight", "(J)Z", (void*)PaintGlue::isElegantTextHeight},
-        {"nSetElegantTextHeight", "(JZ)V", (void*)PaintGlue::setElegantTextHeight},
+        {"nGetElegantTextHeight", "(J)I", (void*)PaintGlue::getElegantTextHeight},
+        {"nSetElegantTextHeight", "(JI)V", (void*)PaintGlue::setElegantTextHeight},
         {"nGetTextSize", "(J)F", (void*)PaintGlue::getTextSize},
         {"nSetTextSize", "(JF)V", (void*)PaintGlue::setTextSize},
         {"nGetTextScaleX", "(J)F", (void*)PaintGlue::getTextScaleX},
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index e28ad67..200d4ef 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -502,7 +502,7 @@
      *       <td colspan="4"><strong>BDS</strong></td>
      *       <td colspan="3"><strong>GAL</strong></td>
      *       <td><strong>SBAS</strong></td>
-     *       <td><strong>IRNSS</strong></td>
+     *       <td><strong>NavIC</strong></td>
      *     </tr>
      *     <tr>
      *       <td><strong>State Flag</strong></td>
@@ -1528,17 +1528,17 @@
      * <p>Similar to the Attribute field described in RINEX 4.00, e.g., in Tables 9-16 (see
      * https://igs.org/wg/rinex/#documents-formats).
      *
-     * <p>Returns "A" for GALILEO E1A, GALILEO E6A, IRNSS L5A SPS, IRNSS SA SPS, GLONASS G1a L1OCd,
+     * <p>Returns "A" for GALILEO E1A, GALILEO E6A, NavIC L5A SPS, NavIC SA SPS, GLONASS G1a L1OCd,
      * GLONASS G2a L2CSI.
      *
-     * <p>Returns "B" for GALILEO E1B, GALILEO E6B, IRNSS L5B RS (D), IRNSS SB RS (D), GLONASS G1a
+     * <p>Returns "B" for GALILEO E1B, GALILEO E6B, NavIC L5B RS (D), NavIC SB RS (D), GLONASS G1a
      * L1OCp, GLONASS G2a L2OCp, QZSS L1Sb.
      *
      * <p>Returns "C" for GPS L1 C/A, GPS L2 C/A, GLONASS G1 C/A, GLONASS G2 C/A, GALILEO E1C,
-     * GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C RS (P), IRNSS SC RS (P).
+     * GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, NavIC L5C RS (P), NavIC SC RS (P).
      *
      * <p>Returns "D" for GPS L2 (L1(C/A) + (P2-P1) (semi-codeless)), QZSS L5S(I), BDS B1C Data,
-     * BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data.
+     * BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data, NavIC L1 Data.
      *
      * <p>Returns “E” for QZSS L1 C/B, QZSS L6E.
      *
@@ -1553,7 +1553,7 @@
      * <p>Returns "N" for GPS L1 codeless, GPS L2 codeless.
      *
      * <p>Returns "P" for GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P, BDS B1C Pilot, BDS B2a Pilot,
-     * BDS B2b Pilot, BDS B2 (B2a+B2b) Pilot, BDS B3a Pilot, QZSS L5S(Q).
+     * BDS B2b Pilot, BDS B2 (B2a+B2b) Pilot, BDS B3a Pilot, QZSS L5S(Q), NavIC L1 Pilot.
      *
      * <p>Returns "Q" for GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q, GALILEO E5a+b Q,
      * SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
@@ -1567,7 +1567,8 @@
      * GLONASS G2a L2CSI+L2OCp, GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO
      * E5b (I+Q), GALILEO E5a+b (I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C
      * (M+L), QZSS L5 (I+Q), QZSS L6 (D+P), BDS B1 (I+Q), BDS B1C Data+Pilot, BDS B2a Data+Pilot,
-     * BDS B2 (I+Q), BDS B2 (B2a+B2b) Data+Pilot, BDS B3 (I+Q), IRNSS L5 (B+C), IRNSS S (B+C).
+     * BDS B2 (I+Q), BDS B2 (B2a+B2b) Data+Pilot, BDS B3 (I+Q), NavIC L5 (B+C), NavIC S (B+C),
+     * NavIC L1 Data+Pilot.
      *
      * <p>Returns "Y" for GPS L1Y, GPS L2Y.
      *
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index af3c295..5a27435 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -68,7 +68,7 @@
     /**
      * The unique address of the device. Some devices don't have addresses, only an empty string.
      */
-    private final @NonNull String mAddress;
+    private @NonNull String mAddress;
     /**
      * The non-unique name of the device. Some devices don't have names, only an empty string.
      * Should not be used as a unique identifier for a device.
@@ -188,6 +188,21 @@
 
     /**
      * @hide
+     * Copy Constructor.
+     * @param ada the copied AudioDeviceAttributes
+     */
+    public AudioDeviceAttributes(AudioDeviceAttributes ada) {
+        mRole = ada.getRole();
+        mType = ada.getType();
+        mAddress = ada.getAddress();
+        mName = ada.getName();
+        mNativeType = ada.getInternalType();
+        mAudioProfiles = ada.getAudioProfiles();
+        mAudioDescriptors = ada.getAudioDescriptors();
+    }
+
+    /**
+     * @hide
      * Returns the role of a device
      * @return the role
      */
@@ -218,6 +233,15 @@
 
     /**
      * @hide
+     * Sets the device address. Only used by audio service.
+     */
+    public void setAddress(@NonNull String address) {
+        Objects.requireNonNull(address);
+        mAddress = address;
+    }
+
+    /**
+     * @hide
      * Returns the name of the audio device, or an empty string for devices without one
      * @return the device name
      */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5b88079..9ad5c3e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -20,6 +20,8 @@
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
 import static android.content.Context.DEVICE_ID_DEFAULT;
 
+import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
+
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -1060,8 +1062,17 @@
      * @see #isVolumeFixed()
      */
     public void adjustVolume(int direction, @PublicVolumeFlags int flags) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
-        helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+        if (autoPublicVolumeApiHardening()) {
+            final IAudioService service = getService();
+            try {
+                service.adjustVolume(direction, flags);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+            helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+        }
     }
 
     /**
@@ -1090,8 +1101,17 @@
      */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType,
             @PublicVolumeFlags int flags) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
-        helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+        if (autoPublicVolumeApiHardening()) {
+            final IAudioService service = getService();
+            try {
+                service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+            helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+        }
     }
 
     /** @hide */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0e7718b..8584dbc 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -498,6 +498,10 @@
             in String packageName, int uid, int pid, in UserHandle userHandle,
             int targetSdkVersion);
 
+    oneway void adjustVolume(int direction, int flags);
+
+    oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
+
     boolean isMusicActive(in boolean remotely);
 
     int getDeviceMaskForStream(in int streamType);
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index c9cfa67..386534b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -34,3 +34,10 @@
     description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
     bug: "293743975"
 }
+
+flag {
+    namespace: "media_solutions"
+    name: "enable_waiting_state_for_system_session_creation_request"
+    description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
+    bug: "307723189"
+}
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 10c880d..24efbd1 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -158,26 +158,6 @@
             in @nullable IMediaProjection projection);
 
     /**
-     * Notifies system server that we are handling a particular state during the consent flow.
-     *
-     * <p>Only used for emitting atoms.
-     *
-     * @param hostUid               The uid of the process requesting consent to capture, may be an app or
-     *                              SystemUI.
-     * @param state                 The state that SystemUI is handling during the consent flow.
-     *                              Must be a valid
-     *                              state defined in the MediaProjectionState enum.
-     * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
-     *                              Indicates the entry point for requesting the permission. Must be
-     *                              a valid state defined
-     *                              in the SessionCreationSource enum.
-     */
-    @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
-    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
-            + ".permission.MANAGE_MEDIA_PROJECTION)")
-    oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
-
-    /**
      * Notifies system server that the permission request was initiated.
      *
      * <p>Only used for emitting atoms.
@@ -208,6 +188,19 @@
     oneway void notifyPermissionRequestDisplayed(int hostUid);
 
     /**
+     * Notifies system server that the permission request was cancelled.
+     *
+     * <p>Only used for emitting atoms.
+     *
+     * @param hostUid The uid of the process requesting consent to capture, may be an app or
+     *                SystemUI.
+     */
+    @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+            + ".permission.MANAGE_MEDIA_PROJECTION)")
+    oneway void notifyPermissionRequestCancelled(int hostUid);
+
+    /**
      * Notifies system server that the app selector was displayed.
      *
      * <p>Only used for emitting atoms.
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
index 0d4af2a..47ca944 100644
--- a/packages/CredentialManager/shared/Android.bp
+++ b/packages/CredentialManager/shared/Android.bp
@@ -16,5 +16,6 @@
         "androidx.core_core-ktx",
         "androidx.credentials_credentials",
         "guava",
+        "hilt_android",
     ],
 }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
index 1cce3ba..5738fee 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
@@ -25,8 +25,11 @@
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.model.Password
 import com.android.credentialmanager.model.Request
+import javax.inject.Inject
+import javax.inject.Singleton
 
-class PasswordRepository {
+@Singleton
+class PasswordRepository @Inject constructor() {
 
     suspend fun selectPassword(
         password: Password,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
index 5ab5ab9..1973fc1 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
@@ -16,17 +16,20 @@
 
 package com.android.credentialmanager.repository
 
-import android.app.Application
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.util.Log
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.parse
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import javax.inject.Inject
+import javax.inject.Singleton
 
-class RequestRepository(
-    private val application: Application,
+@Singleton
+class RequestRepository @Inject constructor(
+        private val packageManager: PackageManager,
 ) {
 
     private val _requests = MutableStateFlow<Request?>(null)
@@ -34,7 +37,7 @@
 
     suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
         val request = intent.parse(
-            packageManager = application.packageManager,
+            packageManager = packageManager,
             previousIntent = previousIntent
         )
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 477e61d..f0fa6c5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -336,7 +336,10 @@
             return result
         }
 
-        private fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
+        /**
+         * @hide
+         */
+        fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
             try {
                 when (slice.spec?.type) {
                     TYPE_PASSWORD_CREDENTIAL -> return PasswordCredentialEntry.fromSlice(slice)!!
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index d35dcb5..81cbd5a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -23,6 +23,8 @@
 import android.credentials.GetCandidateCredentialsException
 import android.credentials.GetCandidateCredentialsResponse
 import android.credentials.GetCredentialRequest
+import android.credentials.ui.GetCredentialProviderData
+import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
@@ -42,6 +44,9 @@
 import org.json.JSONException
 import android.widget.inline.InlinePresentationSpec
 import androidx.autofill.inline.v1.InlineSuggestionUi
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
 import com.android.credentialmanager.GetFlowUtils
 import com.android.credentialmanager.getflow.CredentialEntryInfo
 import com.android.credentialmanager.getflow.ProviderDisplayInfo
@@ -110,6 +115,34 @@
         )
     }
 
+    private fun getEntryToIconMap(
+            candidateProviderDataList: MutableList<GetCredentialProviderData>
+    ): Map<String, Icon> {
+        val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
+        candidateProviderDataList.forEach { provider ->
+            provider.credentialEntries.forEach { entry ->
+                val credentialEntry = GetFlowUtils.parseCredentialEntryFromSlice(entry.slice)
+                when (credentialEntry) {
+                    is PasswordCredentialEntry -> {
+                        entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
+                    }
+                    is PublicKeyCredentialEntry -> {
+                        entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
+                    }
+                    is CustomCredentialEntry -> {
+                        entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
+                    }
+                }
+            }
+        }
+        return entryIconMap
+    }
+
+    private fun getDefaultIcon(): Icon {
+        return Icon.createWithResource(
+                this, com.android.credentialmanager.R.drawable.ic_other_sign_in_24)
+    }
+
     private fun convertToFillResponse(
             getCredResponse: GetCandidateCredentialsResponse,
             filLRequest: FillRequest
@@ -120,6 +153,8 @@
         if (providerList.isEmpty()) {
             return null
         }
+        val entryIconMap: Map<String, Icon> =
+                getEntryToIconMap(getCredResponse.candidateProviderDataList)
         var totalEntryCount = 0
         providerList.forEach { provider ->
             totalEntryCount += provider.credentialEntryList.size
@@ -174,6 +209,10 @@
                     val sliceBuilder = InlineSuggestionUi
                             .newContentBuilder(pendingIntent)
                             .setTitle(credentialEntry.userName)
+                    val icon: Icon =
+                            entryIconMap[credentialEntry.entryKey + credentialEntry.entrySubkey]
+                                    ?: getDefaultIcon()
+                    sliceBuilder.setStartIcon(icon)
                     inlinePresentation = InlinePresentation(
                             sliceBuilder.build().slice, spec, /* pinned= */ false)
                 }
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index e5f5cc2..c883b1f2 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -22,6 +22,7 @@
 
     static_libs: [
         "CredentialManagerShared",
+        "hilt_android",
         "Horologist",
         "PlatformComposeCore",
         "androidx.activity_activity-compose",
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 273d0b1..0a63cb7 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -29,13 +29,13 @@
 import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import dagger.hilt.android.AndroidEntryPoint
 import kotlinx.coroutines.launch
 
-class CredentialSelectorActivity : ComponentActivity() {
+@AndroidEntryPoint(ComponentActivity::class)
+class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
 
-    private val viewModel: CredentialSelectorViewModel by viewModels {
-        CredentialSelectorViewModel.Factory
-    }
+    private val viewModel: CredentialSelectorViewModel by viewModels()
 
     @OptIn(ExperimentalHorologistApi::class)
     override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
index e8e4033..6bd166e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -17,18 +17,7 @@
 package com.android.credentialmanager
 
 import android.app.Application
-import com.android.credentialmanager.di.inject
-import com.android.credentialmanager.repository.PasswordRepository
-import com.android.credentialmanager.repository.RequestRepository
+import dagger.hilt.android.HiltAndroidApp
 
-class CredentialSelectorApp : Application() {
-
-    lateinit var requestRepository: RequestRepository
-    lateinit var passwordRepository: PasswordRepository
-
-    override fun onCreate() {
-        super.onCreate()
-
-        inject()
-    }
-}
\ No newline at end of file
+@HiltAndroidApp(Application::class)
+class CredentialSelectorApp : Hilt_CredentialSelectorApp()
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index d557dc0..435cd37 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -18,20 +18,20 @@
 
 import android.content.Intent
 import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
 import androidx.lifecycle.viewModelScope
-import androidx.lifecycle.viewmodel.CreationExtras
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.repository.RequestRepository
 import com.android.credentialmanager.ui.mappers.toGet
+import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
+import javax.inject.Inject
 
-class CredentialSelectorViewModel(
+@HiltViewModel
+class CredentialSelectorViewModel @Inject constructor(
     private val requestRepository: RequestRepository,
 ) : ViewModel() {
 
@@ -56,22 +56,6 @@
             requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
         }
     }
-
-    companion object {
-        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
-            @Suppress("UNCHECKED_CAST")
-            override fun <T : ViewModel> create(
-                modelClass: Class<T>,
-                extras: CreationExtras
-            ): T {
-                val application = checkNotNull(extras[APPLICATION_KEY])
-
-                return CredentialSelectorViewModel(
-                    requestRepository = (application as CredentialSelectorApp).requestRepository,
-                ) as T
-            }
-        }
-    }
 }
 
 sealed class CredentialSelectorUiState {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
new file mode 100644
index 0000000..cb1a4a1
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -0,0 +1,18 @@
+package com.android.credentialmanager.di
+
+import android.content.Context
+import android.content.pm.PackageManager
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+@Module
+@InstallIn(SingletonComponent::class)
+internal object AppModule {
+    @Provides
+    @JvmStatic
+    fun providePackageManager(@ApplicationContext context: Context): PackageManager =
+            context.packageManager
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
deleted file mode 100644
index 1e8f83d..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.android.credentialmanager.di
-
-import android.app.Application
-import com.android.credentialmanager.CredentialSelectorApp
-import com.android.credentialmanager.repository.PasswordRepository
-import com.android.credentialmanager.repository.RequestRepository
-
-// TODO b/301601582 add Hilt for dependency injection
-
-fun CredentialSelectorApp.inject() {
-    requestRepository = requestRepository(application = this)
-    passwordRepository = passwordRepository()
-}
-
-private fun requestRepository(
-    application: Application,
-): RequestRepository = RequestRepository(
-    application = application,
-)
-
-private fun passwordRepository(): PasswordRepository = PasswordRepository()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index c87cfd3..81a0672 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -47,8 +47,7 @@
     columnState: ScalingLazyColumnState,
     onCloseApp: () -> Unit,
     modifier: Modifier = Modifier,
-    viewModel: SinglePasswordScreenViewModel =
-        viewModel(factory = SinglePasswordScreenViewModel.Factory),
+    viewModel: SinglePasswordScreenViewModel = viewModel(),
 ) {
     viewModel.initialize()
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index 3167e67..43514a0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -21,11 +21,7 @@
 import androidx.activity.result.IntentSenderRequest
 import androidx.annotation.MainThread
 import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
 import androidx.lifecycle.viewModelScope
-import androidx.lifecycle.viewmodel.CreationExtras
-import com.android.credentialmanager.CredentialSelectorApp
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.ktx.getIntentSenderRequest
 import com.android.credentialmanager.model.Password
@@ -33,12 +29,15 @@
 import com.android.credentialmanager.repository.PasswordRepository
 import com.android.credentialmanager.repository.RequestRepository
 import com.android.credentialmanager.ui.model.PasswordUiModel
+import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
+import javax.inject.Inject
 
-class SinglePasswordScreenViewModel(
+@HiltViewModel
+class SinglePasswordScreenViewModel @Inject constructor(
     private val requestRepository: RequestRepository,
     private val passwordRepository: PasswordRepository,
 ) : ViewModel() {
@@ -105,23 +104,6 @@
             _uiState.value = SinglePasswordScreenUiState.Completed
         }
     }
-
-    companion object {
-        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
-            @Suppress("UNCHECKED_CAST")
-            override fun <T : ViewModel> create(
-                modelClass: Class<T>,
-                extras: CreationExtras
-            ): T {
-                val application = checkNotNull(extras[APPLICATION_KEY])
-
-                return SinglePasswordScreenViewModel(
-                    requestRepository = (application as CredentialSelectorApp).requestRepository,
-                    passwordRepository = application.passwordRepository,
-                ) as T
-            }
-        }
-    }
 }
 
 sealed class SinglePasswordScreenUiState {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index 155cfbb..b633337 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -173,13 +173,12 @@
         return mCoordinatorLayout;
     }
 
-    /** Sets the title on the collapsing layout, delegating to host if needed. */
+    /** Sets the title on the collapsing layout and delegates to host. */
     public void setTitle(CharSequence title) {
         if (mCollapsingToolbarLayout != null) {
             mCollapsingToolbarLayout.setTitle(title);
-        } else {
-            mHostCallback.setOuterTitle(title);
         }
+        mHostCallback.setOuterTitle(title);
     }
 
     /** Returns an instance of collapsing toolbar. */
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9687674..6eaabbb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1098,19 +1098,6 @@
     <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
     <string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> left</string>
 
-    <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
-    <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shut down soon</string>
-    <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
-    <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shut down soon</string>
-    <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
-    <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shut down soon</string>
-    <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
-    <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
-    <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
-    <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
-    <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
-    <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
-
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
     <string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
     <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 363e20aa..7fbd35b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -19,7 +19,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
-import android.widget.Switch;
+import android.widget.CompoundButton;
 
 import androidx.annotation.Keep;
 import androidx.annotation.Nullable;
@@ -35,7 +35,7 @@
  */
 public class PrimarySwitchPreference extends RestrictedPreference {
 
-    private Switch mSwitch;
+    private CompoundButton mSwitch;
     private boolean mChecked;
     private boolean mCheckedSet;
     private boolean mEnableSwitch = true;
@@ -65,7 +65,7 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        mSwitch = (Switch) holder.findViewById(R.id.switchWidget);
+        mSwitch = (CompoundButton) holder.findViewById(R.id.switchWidget);
         if (mSwitch != null) {
             mSwitch.setOnClickListener(v -> {
                 if (mSwitch != null && !mSwitch.isEnabled()) {
@@ -153,7 +153,7 @@
         setSwitchEnabled(admin == null);
     }
 
-    public Switch getSwitch() {
+    public CompoundButton getSwitch() {
         return mSwitch;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 758f090..60321eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -36,18 +36,17 @@
 import android.widget.TextView;
 
 import androidx.annotation.VisibleForTesting;
-import androidx.core.content.res.TypedArrayUtils;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceViewHolder;
-import androidx.preference.SwitchPreference;
+import androidx.preference.SwitchPreferenceCompat;
 
 import com.android.settingslib.utils.BuildCompatUtils;
 
 /**
- * Version of SwitchPreference that can be disabled by a device admin
+ * Version of SwitchPreferenceCompat that can be disabled by a device admin
  * using a user restriction.
  */
-public class RestrictedSwitchPreference extends SwitchPreference {
+public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
     RestrictedPreferenceHelper mHelper;
     AppOpsManager mAppOpsManager;
     boolean mUseAdditionalSummary = false;
@@ -93,8 +92,7 @@
     }
 
     public RestrictedSwitchPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
-                android.R.attr.switchPreferenceStyle));
+        this(context, attrs, androidx.preference.R.attr.switchPreferenceCompatStyle);
     }
 
     public RestrictedSwitchPreference(Context context) {
@@ -113,7 +111,7 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        final View switchView = holder.findViewById(android.R.id.switch_widget);
+        final View switchView = holder.findViewById(androidx.preference.R.id.switchWidget);
         if (switchView != null) {
             final View rootView = switchView.getRootView();
             rootView.setFilterTouchesWhenObscured(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
index 660090d..1900575 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
@@ -19,14 +19,19 @@
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHapClient;
+import android.bluetooth.BluetoothHapPresetInfo;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
 import android.util.Log;
 
@@ -36,6 +41,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * HapClientProfile handles the Bluetooth HAP service client role.
@@ -118,7 +124,52 @@
     }
 
     /**
-     * Get hearing aid devices matching connection states{
+     * Registers a {@link BluetoothHapClient.Callback} that will be invoked during the
+     * operation of this profile.
+     *
+     * Repeated registration of the same <var>callback</var> object after the first call to this
+     * method will result with IllegalArgumentException being thrown, even when the
+     * <var>executor</var> is different. API caller would have to call
+     * {@link #unregisterCallback(BluetoothHapClient.Callback)} with the same callback object
+     * before registering it again.
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link BluetoothHapClient.Callback}
+     * @throws NullPointerException if a null executor, or callback is given, or
+     *  IllegalArgumentException if the same <var>callback</var> is already registered.
+     * @hide
+     */
+    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull BluetoothHapClient.Callback callback) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot register callback.");
+            return;
+        }
+        mService.registerCallback(executor, callback);
+    }
+
+    /**
+     * Unregisters the specified {@link BluetoothHapClient.Callback}.
+     * <p>The same {@link BluetoothHapClient.Callback} object used when calling
+     * {@link #registerCallback(Executor, BluetoothHapClient.Callback)} must be used.
+     *
+     * <p>Callbacks are automatically unregistered when application process goes away
+     *
+     * @param callback user implementation of the {@link BluetoothHapClient.Callback}
+     * @throws NullPointerException when callback is null or IllegalArgumentException when no
+     *  callback is registered
+     * @hide
+     */
+    public void unregisterCallback(@NonNull BluetoothHapClient.Callback callback) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot unregister callback.");
+            return;
+        }
+        mService.unregisterCallback(callback);
+    }
+
+    /**
+     * Gets hearing aid devices matching connection states{
      * {@code BluetoothProfile.STATE_CONNECTED},
      * {@code BluetoothProfile.STATE_CONNECTING},
      * {@code BluetoothProfile.STATE_DISCONNECTING}}
@@ -133,7 +184,7 @@
     }
 
     /**
-     * Get hearing aid devices matching connection states{
+     * Gets hearing aid devices matching connection states{
      * {@code BluetoothProfile.STATE_DISCONNECTED},
      * {@code BluetoothProfile.STATE_CONNECTED},
      * {@code BluetoothProfile.STATE_CONNECTING},
@@ -222,6 +273,270 @@
         return mService.supportsWritablePresets(device);
     }
 
+
+    /**
+     * Gets the group identifier, which can be used in the group related part of the API.
+     *
+     * <p>Users are expected to get group identifier for each of the connected device to discover
+     * the device grouping. This allows them to make an informed decision which devices can be
+     * controlled by single group API call and which require individual device calls.
+     *
+     * <p>Note that some binaural HA devices may not support group operations, therefore are not
+     * considered a valid HAP group. In such case -1 is returned even if such device is a valid Le
+     * Audio Coordinated Set member.
+     *
+     * @param device is the device for which we want to get the hap group identifier
+     * @return valid group identifier or -1
+     * @hide
+     */
+    public int getHapGroup(@NonNull BluetoothDevice device) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot get hap group.");
+            return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+        }
+        return mService.getHapGroup(device);
+    }
+
+    /**
+     * Gets the currently active preset for a HA device.
+     *
+     * @param device is the device for which we want to set the active preset
+     * @return active preset index or {@link BluetoothHapClient#PRESET_INDEX_UNAVAILABLE} if the
+     *         device is not connected.
+     * @hide
+     */
+    public int getActivePresetIndex(@NonNull BluetoothDevice device) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot get active preset index.");
+            return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
+        }
+        return mService.getActivePresetIndex(device);
+    }
+
+    /**
+     * Gets the currently active preset info for a remote device.
+     *
+     * @param device is the device for which we want to get the preset name
+     * @return currently active preset info if selected, null if preset info is not available for
+     *     the remote device
+     * @hide
+     */
+    @Nullable
+    public BluetoothHapPresetInfo getActivePresetInfo(@NonNull BluetoothDevice device) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot get active preset info.");
+            return null;
+        }
+        return mService.getActivePresetInfo(device);
+    }
+
+    /**
+     * Selects the currently active preset for a HA device
+     *
+     * <p>On success,
+     * {@link BluetoothHapClient.Callback#onPresetSelected(BluetoothDevice, int, int)} will be
+     * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST} On failure,
+     * {@link BluetoothHapClient.Callback#onPresetSelectionFailed(BluetoothDevice, int)} will be
+     * called.
+     *
+     * @param device is the device for which we want to set the active preset
+     * @param presetIndex is an index of one of the available presets
+     * @hide
+     */
+    public void selectPreset(@NonNull BluetoothDevice device, int presetIndex) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot select preset.");
+            return;
+        }
+        mService.selectPreset(device, presetIndex);
+    }
+
+
+    /**
+     * Selects the currently active preset for a Hearing Aid device group.
+     *
+     * <p>This group call may replace multiple device calls if those are part of the valid HAS
+     * group. Note that binaural HA devices may or may not support group.
+     *
+     * <p>On success,
+     * {@link BluetoothHapClient.Callback#onPresetSelected(BluetoothDevice, int, int)} will be
+     * called for each device within the group with reason code
+     * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST} On failure,
+     * {@link BluetoothHapClient.Callback#onPresetSelectionForGroupFailed(int, int)} will be
+     * called for the group.
+     *
+     * @param groupId is the device group identifier for which want to set the active preset
+     * @param presetIndex is an index of one of the available presets
+     * @hide
+     */
+    public void selectPresetForGroup(int groupId, int presetIndex) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot select preset for group.");
+            return;
+        }
+        mService.selectPresetForGroup(groupId, presetIndex);
+    }
+
+    /**
+     * Sets the next preset as a currently active preset for a HA device
+     *
+     * <p>Note that the meaning of 'next' is HA device implementation specific and does not
+     * necessarily mean a higher preset index.
+     *
+     * @param device is the device for which we want to set the active preset
+     * @hide
+     */
+    public void switchToNextPreset(@NonNull BluetoothDevice device) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot switch to next preset.");
+            return;
+        }
+        mService.switchToNextPreset(device);
+    }
+
+
+    /**
+     * Sets the next preset as a currently active preset for a HA device group
+     *
+     * <p>Note that the meaning of 'next' is HA device implementation specific and does not
+     * necessarily mean a higher preset index.
+     *
+     * <p>This group call may replace multiple device calls if those are part of the valid HAS
+     * group. Note that binaural HA devices may or may not support group.
+     *
+     * @param groupId is the device group identifier for which want to set the active preset
+     * @hide
+     */
+    public void switchToNextPresetForGroup(int groupId) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot switch to next preset for group.");
+            return;
+        }
+        mService.switchToNextPresetForGroup(groupId);
+    }
+
+    /**
+     * Sets the previous preset as a currently active preset for a HA device.
+     *
+     * <p>Note that the meaning of 'previous' is HA device implementation specific and does not
+     * necessarily mean a lower preset index.
+     *
+     * @param device is the device for which we want to set the active preset
+     * @hide
+     */
+    public void switchToPreviousPreset(@NonNull BluetoothDevice device) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot switch to previous preset.");
+            return;
+        }
+        mService.switchToPreviousPreset(device);
+    }
+
+
+    /**
+     * Sets the next preset as a currently active preset for a HA device group
+     *
+     * <p>Note that the meaning of 'next' is HA device implementation specific and does not
+     * necessarily mean a higher preset index.
+     *
+     * <p>This group call may replace multiple device calls if those are part of the valid HAS
+     * group. Note that binaural HA devices may or may not support group.
+     *
+     * @param groupId is the device group identifier for which want to set the active preset
+     * @hide
+     */
+    public void switchToPreviousPresetForGroup(int groupId) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot switch to previous preset for "
+                    + "group.");
+            return;
+        }
+        mService.switchToPreviousPresetForGroup(groupId);
+    }
+
+    /**
+     * Requests the preset info
+     *
+     * @param device is the device for which we want to get the preset name
+     * @param presetIndex is an index of one of the available presets
+     * @return preset info
+     * @hide
+     */
+    public BluetoothHapPresetInfo getPresetInfo(@NonNull BluetoothDevice device, int presetIndex) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot get preset info.");
+            return null;
+        }
+        return mService.getPresetInfo(device, presetIndex);
+    }
+
+    /**
+     * Gets all preset info for a particular device
+     *
+     * @param device is the device for which we want to get all presets info
+     * @return a list of all known preset info
+     * @hide
+     */
+    @NonNull
+    public List<BluetoothHapPresetInfo> getAllPresetInfo(@NonNull BluetoothDevice device) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot get all preset info.");
+            return new ArrayList<>();
+        }
+        return mService.getAllPresetInfo(device);
+    }
+
+    /**
+     * Sets the preset name for a particular device
+     *
+     * <p>Note that the name length is restricted to 40 characters.
+     *
+     * <p>On success,
+     * {@link BluetoothHapClient.Callback#onPresetInfoChanged(BluetoothDevice, List, int)} with a
+     * new name will be called and reason code
+     * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST} On failure,
+     * {@link BluetoothHapClient.Callback#onSetPresetNameFailed(BluetoothDevice, int)} will be
+     * called.
+     *
+     * @param device is the device for which we want to get the preset name
+     * @param presetIndex is an index of one of the available presets
+     * @param name is a new name for a preset, maximum length is 40 characters
+     * @hide
+     */
+    public void setPresetName(@NonNull BluetoothDevice device, int presetIndex,
+            @NonNull String name) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot set preset name.");
+            return;
+        }
+        mService.setPresetName(device, presetIndex, name);
+    }
+
+    /**
+     * Sets the name for a hearing aid preset.
+     *
+     * <p>Note that the name length is restricted to 40 characters.
+     *
+     * <p>On success,
+     * {@link BluetoothHapClient.Callback#onPresetInfoChanged(BluetoothDevice, List, int)} with a
+     * new name will be called for each device within the group with reason code
+     * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST} On failure,
+     * {@link BluetoothHapClient.Callback#onSetPresetNameForGroupFailed(int, int)} will be invoked
+     *
+     * @param groupId is the device group identifier
+     * @param presetIndex is an index of one of the available presets
+     * @param name is a new name for a preset, maximum length is 40 characters
+     * @hide
+     */
+    public void setPresetNameForGroup(int groupId, int presetIndex, @NonNull String name) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot set preset name for group.");
+            return;
+        }
+        mService.setPresetNameForGroup(groupId, presetIndex, name);
+    }
+
+
     @Override
     public boolean accessProfileEnabled() {
         return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
index 02d76304..f988837 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
@@ -37,7 +37,7 @@
     const val MONITORED_ANIMATION_DURATION_MS = 300L
 
     /**
-     * Detects the jank when click on a SwitchPreference.
+     * Detects the jank when click on a TwoStatePreference.
      *
      * @param recyclerView the recyclerView contains the preference
      * @param preference the clicked preference
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index c9540c7..8bdbee3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.PowerManager;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -202,10 +201,10 @@
     }
 
     private static void setBatterySaverConfirmationAcknowledged(Context context) {
-        Secure.putIntForUser(context.getContentResolver(),
-                Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1, UserHandle.USER_CURRENT);
-        Secure.putIntForUser(context.getContentResolver(),
-                Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, 1, UserHandle.USER_CURRENT);
+        Secure.putInt(context.getContentResolver(),
+                Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
+        Secure.putInt(context.getContentResolver(),
+                Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, 1);
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 2999c83..1501e27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -31,8 +31,8 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
+import android.widget.CompoundButton;
 import android.widget.ImageView;
-import android.widget.Switch;
 import android.widget.Toast;
 
 import androidx.preference.Preference;
@@ -138,7 +138,7 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        final Switch switchWidget = getSwitch();
+        final CompoundButton switchWidget = getSwitch();
         if (switchWidget != null) {
             // Avoid default behavior in {@link PrimarySwitchPreference#onBindViewHolder}.
             switchWidget.setOnClickListener(v -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 673f243..2272654 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -44,45 +44,6 @@
     private static final long ONE_MIN_MILLIS = TimeUnit.MINUTES.toMillis(1);
 
     /**
-     * This method produces the text used in various places throughout the system to describe the
-     * remaining battery life of the phone in a consistent manner.
-     *
-     * @param context
-     * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
-     * @param percentageString An optional percentage of battery remaining string.
-     * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation.
-     * @return a properly formatted and localized string describing how much time remains
-     * before the battery runs out.
-     */
-    public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs,
-            @Nullable String percentageString, boolean basedOnUsage) {
-        if (drainTimeMs > 0) {
-            if (drainTimeMs <= SEVEN_MINUTES_MILLIS) {
-                // show a imminent shutdown warning if less than 7 minutes remain
-                return getShutdownImminentString(context, percentageString);
-            } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) {
-                // show a less than 15 min remaining warning if appropriate
-                CharSequence timeString = StringUtil.formatElapsedTime(context,
-                        FIFTEEN_MINUTES_MILLIS,
-                        false /* withSeconds */, false /* collapseTimeUnit */);
-                return getUnderFifteenString(context, timeString, percentageString);
-            } else if (drainTimeMs >= TWO_DAYS_MILLIS) {
-                // just say more than two day if over 48 hours
-                return getMoreThanTwoDaysString(context, percentageString);
-            } else if (drainTimeMs >= ONE_DAY_MILLIS) {
-                // show remaining days & hours if more than a day
-                return getMoreThanOneDayString(context, drainTimeMs,
-                        percentageString, basedOnUsage);
-            } else {
-                // show the time of day we think you'll run out
-                return getRegularTimeRemainingString(context, drainTimeMs,
-                        percentageString, basedOnUsage);
-            }
-        }
-        return null;
-    }
-
-    /**
      * Method to produce a shortened string describing the remaining battery. Suitable for Quick
      * Settings and other areas where space is constrained.
      *
@@ -128,14 +89,6 @@
         }
     }
 
-    private static String getShutdownImminentString(Context context, String percentageString) {
-        return TextUtils.isEmpty(percentageString)
-                ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
-                : context.getString(
-                        R.string.power_remaining_duration_shutdown_imminent,
-                        percentageString);
-    }
-
     private static String getUnderFifteenString(Context context, CharSequence timeString,
             String percentageString) {
         return TextUtils.isEmpty(percentageString)
@@ -268,4 +221,4 @@
             return time - remainder + multiple;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
index d9cf9f2..debfa49 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
@@ -23,8 +23,8 @@
 
 import android.content.Context;
 import android.view.LayoutInflater;
+import android.widget.CompoundButton;
 import android.widget.LinearLayout;
-import android.widget.Switch;
 
 import androidx.preference.Preference.OnPreferenceChangeListener;
 import androidx.preference.PreferenceViewHolder;
@@ -62,7 +62,7 @@
 
     @Test
     public void setChecked_shouldUpdateButtonCheckedState() {
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         mPreference.onBindViewHolder(mHolder);
 
         mPreference.setChecked(true);
@@ -74,7 +74,7 @@
 
     @Test
     public void setSwitchEnabled_shouldUpdateButtonEnabledState() {
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         mPreference.onBindViewHolder(mHolder);
 
         mPreference.setSwitchEnabled(true);
@@ -86,7 +86,7 @@
 
     @Test
     public void setSwitchEnabled_shouldUpdateButtonEnabledState_beforeViewBound() {
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
 
         mPreference.setSwitchEnabled(false);
         mPreference.onBindViewHolder(mHolder);
@@ -97,7 +97,7 @@
     public void clickWidgetView_shouldToggleButton() {
         assertThat(mWidgetView).isNotNull();
 
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         mPreference.onBindViewHolder(mHolder);
 
         toggle.performClick();
@@ -111,7 +111,7 @@
     public void clickWidgetView_shouldNotToggleButtonIfDisabled() {
         assertThat(mWidgetView).isNotNull();
 
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         mPreference.onBindViewHolder(mHolder);
         toggle.setEnabled(false);
 
@@ -122,7 +122,7 @@
     @Test
     public void clickWidgetView_shouldNotifyPreferenceChanged() {
 
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
 
         final OnPreferenceChangeListener listener = mock(OnPreferenceChangeListener.class);
         mPreference.setOnPreferenceChangeListener(listener);
@@ -139,7 +139,7 @@
 
     @Test
     public void setDisabledByAdmin_hasEnforcedAdmin_shouldDisableButton() {
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         toggle.setEnabled(true);
         mPreference.onBindViewHolder(mHolder);
 
@@ -149,7 +149,7 @@
 
     @Test
     public void setDisabledByAdmin_noEnforcedAdmin_shouldEnableButton() {
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         toggle.setEnabled(false);
         mPreference.onBindViewHolder(mHolder);
 
@@ -159,7 +159,7 @@
 
     @Test
     public void onBindViewHolder_toggleButtonShouldHaveContentDescription() {
-        final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+        final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
         final String label = "TestButton";
         mPreference.setTitle(label);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HapClientProfileTest.java
index 03a792a..7e3263b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HapClientProfileTest.java
@@ -26,10 +26,12 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHapClient;
+import android.bluetooth.BluetoothHapPresetInfo;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
+import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
@@ -47,11 +49,16 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class HapClientProfileTest {
 
+    private static final int TEST_GROUP_ID = 1;
+    private static final int TEST_PRESET_INDEX = 1;
+    private static final String TEST_DEVICE_NAME = "test_device";
+
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
 
@@ -63,6 +70,8 @@
     private BluetoothDevice mBluetoothDevice;
     @Mock
     private BluetoothHapClient mService;
+    @Mock
+    private BluetoothHapPresetInfo mPresetInfo;
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private BluetoothProfile.ServiceListener mServiceListener;
@@ -206,4 +215,275 @@
 
         assertThat(mProfile.getConnectableDevices().size()).isEqualTo(connectableList.size());
     }
+
+    /**
+     * Verify registerCallback() call is correctly delegated to {@link BluetoothHapClient} service.
+     */
+    @Test
+    public void registerCallback_verifyIsCalled() {
+        final Executor executor = (command -> new Thread(command).start());
+        final BluetoothHapClient.Callback callback = new BluetoothHapClient.Callback() {
+            @Override
+            public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex,
+                    int reason) {
+
+            }
+
+            @Override
+            public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
+
+            }
+
+            @Override
+            public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
+
+            }
+
+            @Override
+            public void onPresetInfoChanged(@NonNull BluetoothDevice device,
+                    @NonNull List<BluetoothHapPresetInfo> presetInfoList, int reason) {
+
+            }
+
+            @Override
+            public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
+
+            }
+
+            @Override
+            public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
+
+            }
+        };
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.registerCallback(executor, callback);
+
+        verify(mService).registerCallback(executor, callback);
+    }
+
+    /**
+     * Verify unregisterCallback() call is correctly delegated to {@link BluetoothHapClient}
+     * service.
+     */
+    @Test
+    public void unregisterCallback_verifyIsCalled() {
+        final BluetoothHapClient.Callback callback = new BluetoothHapClient.Callback() {
+            @Override
+            public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex,
+                    int reason) {
+
+            }
+
+            @Override
+            public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
+
+            }
+
+            @Override
+            public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
+
+            }
+
+            @Override
+            public void onPresetInfoChanged(@NonNull BluetoothDevice device,
+                    @NonNull List<BluetoothHapPresetInfo> presetInfoList, int reason) {
+
+            }
+
+            @Override
+            public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
+
+            }
+
+            @Override
+            public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
+
+            }
+        };
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.unregisterCallback(callback);
+
+        verify(mService).unregisterCallback(callback);
+    }
+
+    /**
+     * Verify getHapGroup() call is correctly delegated to {@link BluetoothHapClient} service
+     * and return correct value.
+     */
+    @Test
+    public void getHapGroup_verifyIsCalledAndReturnCorrectValue() {
+        when(mService.getHapGroup(mBluetoothDevice)).thenReturn(TEST_GROUP_ID);
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        final int groupId = mProfile.getHapGroup(mBluetoothDevice);
+
+        verify(mService).getHapGroup(mBluetoothDevice);
+        assertThat(groupId).isEqualTo(TEST_GROUP_ID);
+    }
+
+    /**
+     * Verify getActivePresetIndex() call is correctly delegated to {@link BluetoothHapClient}
+     * service and return correct index.
+     */
+    @Test
+    public void getActivePresetIndex_verifyIsCalledAndReturnCorrectValue() {
+        when(mService.getActivePresetIndex(mBluetoothDevice)).thenReturn(TEST_PRESET_INDEX);
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        final int activeIndex = mProfile.getActivePresetIndex(mBluetoothDevice);
+
+        verify(mService).getActivePresetIndex(mBluetoothDevice);
+        assertThat(activeIndex).isEqualTo(TEST_PRESET_INDEX);
+    }
+
+    /**
+     * Verify getActivePresetInfo() call is correctly delegated to {@link BluetoothHapClient}
+     * service and return correct object.
+     */
+    @Test
+    public void getActivePresetInfo_verifyIsCalledAndReturnCorrectObject() {
+        when(mService.getActivePresetInfo(mBluetoothDevice)).thenReturn(mPresetInfo);
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        final BluetoothHapPresetInfo activeInfo = mProfile.getActivePresetInfo(mBluetoothDevice);
+
+        verify(mService).getActivePresetInfo(mBluetoothDevice);
+        assertThat(activeInfo).isEqualTo(mPresetInfo);
+    }
+
+    /**
+     * Verify selectPreset() call is correctly delegated to {@link BluetoothHapClient} service.
+     */
+    @Test
+    public void selectPreset_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+
+        verify(mService).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+    }
+
+    /**
+     * Verify selectPresetForGroup() call is correctly delegated to {@link BluetoothHapClient}
+     * service.
+     */
+    @Test
+    public void selectPresetForGroup_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.selectPresetForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX);
+
+        verify(mService).selectPresetForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX);
+    }
+
+    /**
+     * Verify switchToNextPreset() call is correctly delegated to {@link BluetoothHapClient}
+     * service.
+     */
+    @Test
+    public void switchToNextPreset_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.switchToNextPreset(mBluetoothDevice);
+
+        verify(mService).switchToNextPreset(mBluetoothDevice);
+    }
+
+    /**
+     * Verify switchToNextPresetForGroup() call is correctly delegated to {@link BluetoothHapClient}
+     * service.
+     */
+    @Test
+    public void switchToNextPresetForGroup_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.switchToNextPresetForGroup(TEST_GROUP_ID);
+
+        verify(mService).switchToNextPresetForGroup(TEST_GROUP_ID);
+    }
+
+    /**
+     * Verify switchToPreviousPreset() call is correctly delegated to {@link BluetoothHapClient}
+     * service.
+     */
+    @Test
+    public void switchToPreviousPreset_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.switchToPreviousPreset(mBluetoothDevice);
+
+        verify(mService).switchToPreviousPreset(mBluetoothDevice);
+    }
+
+    /**
+     * Verify switchToPreviousPresetForGroup() call is correctly delegated to
+     * {@link BluetoothHapClient} service.
+     */
+    @Test
+    public void switchToPreviousPresetForGroup_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.switchToPreviousPresetForGroup(TEST_GROUP_ID);
+
+        verify(mService).switchToPreviousPresetForGroup(TEST_GROUP_ID);
+    }
+
+    /**
+     * Verify getPresetInfo() call is correctly delegated to {@link BluetoothHapClient} service and
+     * return correct object.
+     */
+    @Test
+    public void getPresetInfo_verifyIsCalledAndReturnCorrectObject() {
+        when(mService.getPresetInfo(mBluetoothDevice, TEST_PRESET_INDEX)).thenReturn(mPresetInfo);
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        final BluetoothHapPresetInfo info = mProfile.getPresetInfo(mBluetoothDevice,
+                TEST_PRESET_INDEX);
+
+        verify(mService).getPresetInfo(mBluetoothDevice, TEST_PRESET_INDEX);
+        assertThat(info).isEqualTo(mPresetInfo);
+    }
+
+    /**
+     * Verify getAllPresetInfo() call is correctly delegated to {@link BluetoothHapClient} service
+     * and return correct list.
+     */
+    @Test
+    public void getAllPresetInfo_verifyIsCalledAndReturnCorrectList() {
+        final List<BluetoothHapPresetInfo> testList = Arrays.asList(mPresetInfo, mPresetInfo);
+        when(mService.getAllPresetInfo(mBluetoothDevice)).thenReturn(testList);
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        final List<BluetoothHapPresetInfo> infoList = mProfile.getAllPresetInfo(mBluetoothDevice);
+
+        verify(mService).getAllPresetInfo(mBluetoothDevice);
+        assertThat(infoList.size()).isEqualTo(testList.size());
+    }
+
+    /**
+     * Verify setPresetName() call is correctly delegated to {@link BluetoothHapClient} service.
+     */
+    @Test
+    public void setPresetName_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.setPresetName(mBluetoothDevice, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
+
+        verify(mService).setPresetName(mBluetoothDevice, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
+    }
+
+    /**
+     * Verify setPresetNameForGroup() call is correctly delegated to {@link BluetoothHapClient}
+     * service.
+     */
+    @Test
+    public void setPresetNameForGroup_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
+
+        mProfile.setPresetNameForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
+
+        verify(mService).setPresetNameForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index ae54206..2e7905f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -59,155 +59,6 @@
     }
 
     @Test
-    public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                SEVENTEEN_MIN_MILLIS,
-                TEST_BATTERY_LEVEL_10,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                SEVENTEEN_MIN_MILLIS,
-                TEST_BATTERY_LEVEL_10,
-                false /* basedOnUsage */);
-
-        // We only add special mention for the long string
-        // ex: Will last about 1:15 PM based on your usage (10%)
-        assertThat(info).containsMatch(Pattern.compile(
-                NORMAL_CASE_EXPECTED_PREFIX
-                        + TIME_OF_DAY_REGEX
-                        + ENHANCED_SUFFIX
-                        + PERCENTAGE_REGEX));
-        // shortened string should not have extra text
-        // ex: Will last about 1:15 PM (10%)
-        assertThat(info2).containsMatch(Pattern.compile(
-                NORMAL_CASE_EXPECTED_PREFIX
-                        + TIME_OF_DAY_REGEX
-                        + PERCENTAGE_REGEX));
-    }
-
-    @Test
-    public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_noPercentage() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                SEVENTEEN_MIN_MILLIS,
-                null /* percentageString */,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                SEVENTEEN_MIN_MILLIS,
-                null /* percentageString */,
-                false /* basedOnUsage */);
-
-        // We only have % when it is provided
-        // ex: Will last about 1:15 PM based on your usage
-        assertThat(info).containsMatch(Pattern.compile(
-                NORMAL_CASE_EXPECTED_PREFIX
-                        + TIME_OF_DAY_REGEX
-                        + ENHANCED_SUFFIX
-                        + "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
-        // shortened string should not have extra text
-        // ex: Will last about 1:15 PM
-        assertThat(info2).containsMatch(Pattern.compile(
-                NORMAL_CASE_EXPECTED_PREFIX
-                        + TIME_OF_DAY_REGEX
-                        + "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
-    }
-
-    @Test
-    public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                FIVE_MINUTES_MILLIS,
-                TEST_BATTERY_LEVEL_10 /* percentageString */,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                FIVE_MINUTES_MILLIS,
-                null /* percentageString */,
-                true /* basedOnUsage */);
-
-        // additional battery percentage in this string
-        assertThat(info.contains("may shut down soon (10%)")).isTrue();
-        // shortened string should not have percentage
-        assertThat(info2.contains("may shut down soon")).isTrue();
-    }
-
-    @Test
-    public void testGetBatteryRemainingStringFormatted_betweenSevenAndFifteenMinutes_usesCorrectString() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                TEN_MINUTES_MILLIS,
-                null /* percentageString */,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                TEN_MINUTES_MILLIS,
-                TEST_BATTERY_LEVEL_10 /* percentageString */,
-                true /* basedOnUsage */);
-
-        // shortened string should not have percentage
-        assertThat(info).isEqualTo("Less than 15 min left");
-        // Add percentage to string when provided
-        assertThat(info2).isEqualTo("Less than 15 min left (10%)");
-    }
-
-    @Test
-    public void testGetBatteryRemainingStringFormatted_betweenOneAndTwoDays_usesCorrectString() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                THIRTY_HOURS_MILLIS,
-                null /* percentageString */,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                THIRTY_HOURS_MILLIS,
-                TEST_BATTERY_LEVEL_10 /* percentageString */,
-                false /* basedOnUsage */);
-        String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                THIRTY_HOURS_MILLIS + TEN_MINUTES_MILLIS,
-                null /* percentageString */,
-                false /* basedOnUsage */);
-
-        // We only add special mention for the long string
-        assertThat(info).isEqualTo("About 1 day, 6 hr left based on your usage");
-        // shortened string should not have extra text
-        assertThat(info2).isEqualTo("About 1 day, 6 hr left (10%)");
-        // present 2 time unit at most
-        assertThat(info3).isEqualTo("About 1 day, 6 hr left");
-    }
-
-    @Test
-    public void testGetBatteryRemainingStringFormatted_lessThanOneDay_usesCorrectString() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                TEN_HOURS_MILLIS,
-                null /* percentageString */,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                TEN_HOURS_MILLIS,
-                TEST_BATTERY_LEVEL_10 /* percentageString */,
-                false /* basedOnUsage */);
-        String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                TEN_HOURS_MILLIS + TEN_MINUTES_MILLIS + TEN_SEC_MILLIS,
-                null /* percentageString */,
-                false /* basedOnUsage */);
-
-        // We only add special mention for the long string
-        assertThat(info).isEqualTo("About 10 hr left based on your usage");
-        // shortened string should not have extra text
-        assertThat(info2).isEqualTo("About 10 hr left (10%)");
-        // present 2 time unit at most
-        assertThat(info3).isEqualTo("About 10 hr, 10 min left");
-    }
-
-    @Test
-    public void testGetBatteryRemainingStringFormatted_moreThanTwoDays_usesCorrectString() {
-        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                THREE_DAYS_MILLIS,
-                null /* percentageString */,
-                true /* basedOnUsage */);
-        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
-                THREE_DAYS_MILLIS,
-                TEST_BATTERY_LEVEL_10 /* percentageString */,
-                true /* basedOnUsage */);
-
-        // shortened string should not have percentage
-        assertThat(info).isEqualTo("More than 2 days left");
-        // Add percentage to string when provided
-        assertThat(info2).isEqualTo("More than 2 days left (10%)");
-    }
-
-    @Test
     public void getBatteryTipStringFormatted_moreThanOneDay_usesCorrectString() {
         String info = PowerUtil.getBatteryTipStringFormatted(mContext,
                 THREE_DAYS_MILLIS);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index c6e9c03..603e19f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -220,6 +220,7 @@
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
+        Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
         Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
         Settings.Secure.NOTIFICATION_BUBBLES,
         Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 0727677..5457c35 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -309,6 +309,9 @@
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+                BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
                 new InclusiveIntegerRangeValidator(
                         Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 02ec486..cd35f67 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -45,7 +45,8 @@
 
     private static final boolean DEBUG = false;
 
-    private final Object mLock;
+    // This lock is not the same lock used in SettingsProvider and SettingsState
+    private final Object mLock = new Object();
 
     // Key -> backingStore mapping
     @GuardedBy("mLock")
@@ -74,8 +75,7 @@
 
     private final int mMaxNumBackingStore;
 
-    GenerationRegistry(Object lock, int maxNumUsers) {
-        mLock = lock;
+    GenerationRegistry(int maxNumUsers) {
         // Add some buffer to maxNumUsers to accommodate corner cases when the actual number of
         // users in the system exceeds the limit
         maxNumUsers = maxNumUsers + 2;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f06b31c..7db189b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1850,6 +1850,10 @@
                 SecureSettingsProto.Accessibility
                         .ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+                SecureSettingsProto.Accessibility
+                        .ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
+        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
                 SecureSettingsProto.Accessibility
                         .ACCESSIBILITY_MAGNIFICATION_GESTURE);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4e2fad0..5acc1ca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -40,7 +40,6 @@
 import static com.android.providers.settings.SettingsState.isConfigSettingsKey;
 import static com.android.providers.settings.SettingsState.isGlobalSettingsKey;
 import static com.android.providers.settings.SettingsState.isSecureSettingsKey;
-import static com.android.providers.settings.SettingsState.isSsaidSettingsKey;
 import static com.android.providers.settings.SettingsState.isSystemSettingsKey;
 import static com.android.providers.settings.SettingsState.makeKey;
 
@@ -412,6 +411,9 @@
         SettingsState.cacheSystemPackageNamesAndSystemSignature(getContext());
         synchronized (mLock) {
             mSettingsRegistry.migrateAllLegacySettingsIfNeededLocked();
+            for (UserInfo user : mUserManager.getAliveUsers()) {
+                mSettingsRegistry.ensureSettingsForUserLocked(user.id);
+            }
             mSettingsRegistry.syncSsaidTableOnStartLocked();
         }
         mHandler.post(() -> {
@@ -427,65 +429,53 @@
     public Bundle call(String method, String name, Bundle args) {
         final int requestingUserId = getRequestingUserId(args);
         switch (method) {
-            case Settings.CALL_METHOD_GET_CONFIG: {
+            case Settings.CALL_METHOD_GET_CONFIG -> {
                 Setting setting = getConfigSetting(name);
                 return packageValueForCallResult(SETTINGS_TYPE_CONFIG, name, requestingUserId,
                         setting, isTrackingGeneration(args));
             }
-
-            case Settings.CALL_METHOD_GET_GLOBAL: {
+            case Settings.CALL_METHOD_GET_GLOBAL -> {
                 Setting setting = getGlobalSetting(name);
                 return packageValueForCallResult(SETTINGS_TYPE_GLOBAL, name, requestingUserId,
                         setting, isTrackingGeneration(args));
             }
-
-            case Settings.CALL_METHOD_GET_SECURE: {
+            case Settings.CALL_METHOD_GET_SECURE -> {
                 Setting setting = getSecureSetting(name, requestingUserId);
                 return packageValueForCallResult(SETTINGS_TYPE_SECURE, name, requestingUserId,
                         setting, isTrackingGeneration(args));
             }
-
-            case Settings.CALL_METHOD_GET_SYSTEM: {
+            case Settings.CALL_METHOD_GET_SYSTEM -> {
                 Setting setting = getSystemSetting(name, requestingUserId);
                 return packageValueForCallResult(SETTINGS_TYPE_SYSTEM, name, requestingUserId,
                         setting, isTrackingGeneration(args));
             }
-
-            case Settings.CALL_METHOD_PUT_CONFIG: {
+            case Settings.CALL_METHOD_PUT_CONFIG -> {
                 String value = getSettingValue(args);
                 final boolean makeDefault = getSettingMakeDefault(args);
                 insertConfigSetting(name, value, makeDefault);
-                break;
             }
-
-            case Settings.CALL_METHOD_PUT_GLOBAL: {
+            case Settings.CALL_METHOD_PUT_GLOBAL -> {
                 String value = getSettingValue(args);
                 String tag = getSettingTag(args);
                 final boolean makeDefault = getSettingMakeDefault(args);
                 final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
                 insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false,
                         overrideableByRestore);
-                break;
             }
-
-            case Settings.CALL_METHOD_PUT_SECURE: {
+            case Settings.CALL_METHOD_PUT_SECURE -> {
                 String value = getSettingValue(args);
                 String tag = getSettingTag(args);
                 final boolean makeDefault = getSettingMakeDefault(args);
                 final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
                 insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false,
                         overrideableByRestore);
-                break;
             }
-
-            case Settings.CALL_METHOD_PUT_SYSTEM: {
+            case Settings.CALL_METHOD_PUT_SYSTEM -> {
                 String value = getSettingValue(args);
                 boolean overrideableByRestore = getSettingOverrideableByRestore(args);
                 insertSystemSetting(name, value, requestingUserId, overrideableByRestore);
-                break;
             }
-
-            case Settings.CALL_METHOD_SET_ALL_CONFIG: {
+            case Settings.CALL_METHOD_SET_ALL_CONFIG -> {
                 String prefix = getSettingPrefix(args);
                 Map<String, String> flags = getSettingFlags(args);
                 Bundle result = new Bundle();
@@ -493,120 +483,96 @@
                         setAllConfigSettings(prefix, flags));
                 return result;
             }
-
-            case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG: {
+            case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG -> {
                 final int mode = getSyncDisabledMode(args);
                 setSyncDisabledModeConfig(mode);
-                break;
             }
-
-            case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG: {
+            case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG -> {
                 Bundle result = new Bundle();
                 result.putInt(Settings.KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN,
                         getSyncDisabledModeConfig());
                 return result;
             }
-
-            case Settings.CALL_METHOD_RESET_CONFIG: {
+            case Settings.CALL_METHOD_RESET_CONFIG -> {
                 final int mode = getResetModeEnforcingPermission(args);
                 String prefix = getSettingPrefix(args);
                 resetConfigSetting(mode, prefix);
-                break;
             }
-
-            case Settings.CALL_METHOD_RESET_GLOBAL: {
+            case Settings.CALL_METHOD_RESET_GLOBAL -> {
                 final int mode = getResetModeEnforcingPermission(args);
                 String tag = getSettingTag(args);
                 resetGlobalSetting(requestingUserId, mode, tag);
-                break;
             }
-
-            case Settings.CALL_METHOD_RESET_SECURE: {
+            case Settings.CALL_METHOD_RESET_SECURE -> {
                 final int mode = getResetModeEnforcingPermission(args);
                 String tag = getSettingTag(args);
                 resetSecureSetting(requestingUserId, mode, tag);
-                break;
             }
-
-            case Settings.CALL_METHOD_RESET_SYSTEM: {
+            case Settings.CALL_METHOD_RESET_SYSTEM -> {
                 final int mode = getResetModeEnforcingPermission(args);
                 String tag = getSettingTag(args);
                 resetSystemSetting(requestingUserId, mode, tag);
-                break;
             }
-
-            case Settings.CALL_METHOD_DELETE_CONFIG: {
-                int rows  = deleteConfigSetting(name) ? 1 : 0;
+            case Settings.CALL_METHOD_DELETE_CONFIG -> {
+                int rows = deleteConfigSetting(name) ? 1 : 0;
                 Bundle result = new Bundle();
                 result.putInt(RESULT_ROWS_DELETED, rows);
                 return result;
             }
-
-            case Settings.CALL_METHOD_DELETE_GLOBAL: {
+            case Settings.CALL_METHOD_DELETE_GLOBAL -> {
                 int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
                 Bundle result = new Bundle();
                 result.putInt(RESULT_ROWS_DELETED, rows);
                 return result;
             }
-
-            case Settings.CALL_METHOD_DELETE_SECURE: {
+            case Settings.CALL_METHOD_DELETE_SECURE -> {
                 int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
                 Bundle result = new Bundle();
                 result.putInt(RESULT_ROWS_DELETED, rows);
                 return result;
             }
-
-            case Settings.CALL_METHOD_DELETE_SYSTEM: {
+            case Settings.CALL_METHOD_DELETE_SYSTEM -> {
                 int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
                 Bundle result = new Bundle();
                 result.putInt(RESULT_ROWS_DELETED, rows);
                 return result;
             }
-
-            case Settings.CALL_METHOD_LIST_CONFIG: {
+            case Settings.CALL_METHOD_LIST_CONFIG -> {
                 String prefix = getSettingPrefix(args);
                 Bundle result = packageValuesForCallResult(prefix, getAllConfigFlags(prefix),
                         isTrackingGeneration(args));
                 reportDeviceConfigAccess(prefix);
                 return result;
             }
-
-            case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG: {
+            case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG -> {
                 RemoteCallback callback = args.getParcelable(
                         Settings.CALL_METHOD_MONITOR_CALLBACK_KEY);
                 setMonitorCallback(callback);
-                break;
             }
-
-            case Settings.CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG: {
+            case Settings.CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG -> {
                 clearMonitorCallback();
-                break;
             }
-
-            case Settings.CALL_METHOD_LIST_GLOBAL: {
+            case Settings.CALL_METHOD_LIST_GLOBAL -> {
                 Bundle result = new Bundle();
                 result.putStringArrayList(RESULT_SETTINGS_LIST,
                         buildSettingsList(getAllGlobalSettings(null)));
                 return result;
             }
-
-            case Settings.CALL_METHOD_LIST_SECURE: {
+            case Settings.CALL_METHOD_LIST_SECURE -> {
                 Bundle result = new Bundle();
                 result.putStringArrayList(RESULT_SETTINGS_LIST,
                         buildSettingsList(getAllSecureSettings(requestingUserId, null)));
                 return result;
             }
-
-            case Settings.CALL_METHOD_LIST_SYSTEM: {
+            case Settings.CALL_METHOD_LIST_SYSTEM -> {
                 Bundle result = new Bundle();
                 result.putStringArrayList(RESULT_SETTINGS_LIST,
                         buildSettingsList(getAllSystemSettings(requestingUserId, null)));
                 return result;
             }
-
-            default: {
+            default -> {
                 Slog.w(LOG_TAG, "call() with invalid method: " + method);
-            } break;
+            }
         }
 
         return null;
@@ -638,7 +604,7 @@
         }
 
         switch (args.table) {
-            case TABLE_GLOBAL: {
+            case TABLE_GLOBAL -> {
                 if (args.name != null) {
                     Setting setting = getGlobalSetting(args.name);
                     return packageSettingForQuery(setting, normalizedProjection);
@@ -646,8 +612,7 @@
                     return getAllGlobalSettings(projection);
                 }
             }
-
-            case TABLE_SECURE: {
+            case TABLE_SECURE -> {
                 final int userId = UserHandle.getCallingUserId();
                 if (args.name != null) {
                     Setting setting = getSecureSetting(args.name, userId);
@@ -656,8 +621,7 @@
                     return getAllSecureSettings(userId, projection);
                 }
             }
-
-            case TABLE_SYSTEM: {
+            case TABLE_SYSTEM -> {
                 final int userId = UserHandle.getCallingUserId();
                 if (args.name != null) {
                     Setting setting = getSystemSetting(args.name, userId);
@@ -666,8 +630,7 @@
                     return getAllSystemSettings(userId, projection);
                 }
             }
-
-            default: {
+            default -> {
                 throw new IllegalArgumentException("Invalid Uri path:" + uri);
             }
         }
@@ -708,30 +671,27 @@
         String value = values.getAsString(Settings.Secure.VALUE);
 
         switch (table) {
-            case TABLE_GLOBAL: {
+            case TABLE_GLOBAL -> {
                 if (insertGlobalSetting(name, value, null, false,
                         UserHandle.getCallingUserId(), false,
                         /* overrideableByRestore */ false)) {
-                    return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
+                    return Uri.withAppendedPath(Global.CONTENT_URI, name);
                 }
-            } break;
-
-            case TABLE_SECURE: {
+            }
+            case TABLE_SECURE -> {
                 if (insertSecureSetting(name, value, null, false,
                         UserHandle.getCallingUserId(), false,
                         /* overrideableByRestore */ false)) {
-                    return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
+                    return Uri.withAppendedPath(Secure.CONTENT_URI, name);
                 }
-            } break;
-
-            case TABLE_SYSTEM: {
+            }
+            case TABLE_SYSTEM -> {
                 if (insertSystemSetting(name, value, UserHandle.getCallingUserId(),
                         /* overridableByRestore */ false)) {
                     return Uri.withAppendedPath(Settings.System.CONTENT_URI, name);
                 }
-            } break;
-
-            default: {
+            }
+            default -> {
                 throw new IllegalArgumentException("Bad Uri path:" + uri);
             }
         }
@@ -775,22 +735,19 @@
         }
 
         switch (args.table) {
-            case TABLE_GLOBAL: {
+            case TABLE_GLOBAL -> {
                 final int userId = UserHandle.getCallingUserId();
                 return deleteGlobalSetting(args.name, userId, false) ? 1 : 0;
             }
-
-            case TABLE_SECURE: {
+            case TABLE_SECURE -> {
                 final int userId = UserHandle.getCallingUserId();
                 return deleteSecureSetting(args.name, userId, false) ? 1 : 0;
             }
-
-            case TABLE_SYSTEM: {
+            case TABLE_SYSTEM -> {
                 final int userId = UserHandle.getCallingUserId();
                 return deleteSystemSetting(args.name, userId) ? 1 : 0;
             }
-
-            default: {
+            default -> {
                 throw new IllegalArgumentException("Bad Uri path:" + uri);
             }
         }
@@ -816,24 +773,21 @@
         String value = values.getAsString(Settings.Secure.VALUE);
 
         switch (args.table) {
-            case TABLE_GLOBAL: {
+            case TABLE_GLOBAL -> {
                 final int userId = UserHandle.getCallingUserId();
                 return updateGlobalSetting(args.name, value, null, false,
                         userId, false) ? 1 : 0;
             }
-
-            case TABLE_SECURE: {
+            case TABLE_SECURE -> {
                 final int userId = UserHandle.getCallingUserId();
                 return updateSecureSetting(args.name, value, null, false,
                         userId, false) ? 1 : 0;
             }
-
-            case TABLE_SYSTEM: {
+            case TABLE_SYSTEM -> {
                 final int userId = UserHandle.getCallingUserId();
                 return updateSystemSetting(args.name, value, userId) ? 1 : 0;
             }
-
-            default: {
+            default -> {
                 throw new IllegalArgumentException("Invalid Uri path:" + uri);
             }
         }
@@ -1034,27 +988,38 @@
 
     private void registerBroadcastReceivers() {
         IntentFilter userFilter = new IntentFilter();
+        userFilter.addAction(Intent.ACTION_USER_ADDED);
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         userFilter.addAction(Intent.ACTION_USER_STOPPED);
 
         getContext().registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
+                if (intent.getAction() == null) {
+                    return;
+                }
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_SYSTEM);
+                        UserHandle.USER_NULL);
+                if (userId == UserHandle.USER_NULL) {
+                    return;
+                }
 
                 switch (intent.getAction()) {
-                    case Intent.ACTION_USER_REMOVED: {
+                    case Intent.ACTION_USER_ADDED -> {
+                        synchronized (mLock) {
+                            mSettingsRegistry.ensureSettingsForUserLocked(userId);
+                        }
+                    }
+                    case Intent.ACTION_USER_REMOVED -> {
                         synchronized (mLock) {
                             mSettingsRegistry.removeUserStateLocked(userId, true);
                         }
-                    } break;
-
-                    case Intent.ACTION_USER_STOPPED: {
+                    }
+                    case Intent.ACTION_USER_STOPPED -> {
                         synchronized (mLock) {
                             mSettingsRegistry.removeUserStateLocked(userId, false);
                         }
-                    } break;
+                    }
                 }
             }
         }, userFilter);
@@ -1350,26 +1315,24 @@
         // Perform the mutation.
         synchronized (mLock) {
             switch (operation) {
-                case MUTATION_OPERATION_INSERT: {
+                case MUTATION_OPERATION_INSERT -> {
                     enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
                             callingPackage, false, null,
                             /* overrideableByRestore */ false);
                 }
-
-                case MUTATION_OPERATION_DELETE: {
+                case MUTATION_OPERATION_DELETE -> {
                     enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
                     return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, false, null);
                 }
-
-                case MUTATION_OPERATION_RESET: {
+                case MUTATION_OPERATION_RESET -> {
                     enforceDeviceConfigWritePermission(getContext(),
                             getAllConfigFlags(prefix).keySet());
-                    mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
+                    return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix);
-                } return true;
+                }
             }
         }
 
@@ -1523,7 +1486,7 @@
         enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId);
 
         // If this is a setting that is currently restricted for this user, do not allow
         // unrestricting changes.
@@ -1536,28 +1499,25 @@
         // Perform the mutation.
         synchronized (mLock) {
             switch (operation) {
-                case MUTATION_OPERATION_INSERT: {
+                case MUTATION_OPERATION_INSERT -> {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
                             UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
                             callingPackage, forceNotify,
                             CRITICAL_GLOBAL_SETTINGS, overrideableByRestore);
                 }
-
-                case MUTATION_OPERATION_DELETE: {
+                case MUTATION_OPERATION_DELETE -> {
                     return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
                             UserHandle.USER_SYSTEM, name, forceNotify, CRITICAL_GLOBAL_SETTINGS);
                 }
-
-                case MUTATION_OPERATION_UPDATE: {
+                case MUTATION_OPERATION_UPDATE -> {
                     return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
                             UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
                             callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS);
                 }
-
-                case MUTATION_OPERATION_RESET: {
-                    mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL,
+                case MUTATION_OPERATION_RESET -> {
+                    return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL,
                             UserHandle.USER_SYSTEM, callingPackage, mode, tag);
-                } return true;
+                }
             }
         }
 
@@ -1580,12 +1540,12 @@
         }
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(userId);
 
         // The relevant "calling package" userId will be the owning userId for some
         // profiles, and we can't do the lookup inside our [lock held] loop, so work out
         // up front who the effective "new SSAID" user ID for that settings name will be.
-        final int ssaidUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId,
+        final int ssaidUserId = resolveOwningUserIdForSecureSetting(callingUserId,
                 Settings.Secure.ANDROID_ID);
         final PackageInfo ssaidCallingPkg = getCallingPackageInfo(ssaidUserId);
 
@@ -1600,7 +1560,7 @@
             for (int i = 0; i < nameCount; i++) {
                 String name = names.get(i);
                 // Determine the owning user as some profile settings are cloned from the parent.
-                final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId,
+                final int owningUserId = resolveOwningUserIdForSecureSetting(callingUserId,
                         name);
 
                 if (!isSecureSettingAccessible(name)) {
@@ -1638,13 +1598,13 @@
         }
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId);
 
         // Ensure the caller can access the setting.
         enforceSettingReadable(name, SETTINGS_TYPE_SECURE, UserHandle.getCallingUserId());
 
         // Determine the owning user as some profile settings are cloned from the parent.
-        final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
+        final int owningUserId = resolveOwningUserIdForSecureSetting(callingUserId, name);
 
         if (!isSecureSettingAccessible(name)) {
             // This caller is not permitted to access this setting. Pretend the setting doesn't
@@ -1811,7 +1771,7 @@
         enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId);
 
         // If this is a setting that is currently restricted for this user, do not allow
         // unrestricting changes.
@@ -1820,7 +1780,7 @@
         }
 
         // Determine the owning user as some profile settings are cloned from the parent.
-        final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
+        final int owningUserId = resolveOwningUserIdForSecureSetting(callingUserId, name);
 
         // Only the owning user can change the setting.
         if (owningUserId != callingUserId) {
@@ -1832,28 +1792,25 @@
         // Mutate the value.
         synchronized (mLock) {
             switch (operation) {
-                case MUTATION_OPERATION_INSERT: {
+                case MUTATION_OPERATION_INSERT -> {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
                             owningUserId, name, value, tag, makeDefault,
                             callingPackage, forceNotify, CRITICAL_SECURE_SETTINGS,
                             overrideableByRestore);
                 }
-
-                case MUTATION_OPERATION_DELETE: {
+                case MUTATION_OPERATION_DELETE -> {
                     return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE,
                             owningUserId, name, forceNotify, CRITICAL_SECURE_SETTINGS);
                 }
-
-                case MUTATION_OPERATION_UPDATE: {
+                case MUTATION_OPERATION_UPDATE -> {
                     return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE,
                             owningUserId, name, value, tag, makeDefault,
                             callingPackage, forceNotify, CRITICAL_SECURE_SETTINGS);
                 }
-
-                case MUTATION_OPERATION_RESET: {
-                    mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE,
+                case MUTATION_OPERATION_RESET -> {
+                    return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE,
                             UserHandle.USER_SYSTEM, callingPackage, mode, tag);
-                } return true;
+                }
             }
         }
 
@@ -1866,7 +1823,7 @@
         }
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(userId);
 
         synchronized (mLock) {
             List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId);
@@ -1903,7 +1860,7 @@
         }
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId);
 
         // Ensure the caller can access the setting.
         enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, UserHandle.getCallingUserId());
@@ -1978,7 +1935,7 @@
         }
 
         // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId);
+        final int callingUserId = resolveCallingUserIdEnforcingPermissions(runAsUserId);
 
         if (isSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) {
             Slog.e(LOG_TAG, "UserId: " + callingUserId + " is disallowed to change system "
@@ -2012,37 +1969,30 @@
         // Mutate the value.
         synchronized (mLock) {
             switch (operation) {
-                case MUTATION_OPERATION_INSERT: {
+                case MUTATION_OPERATION_INSERT -> {
                     validateSystemSettingValue(name, value);
                     success = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, value, null, false, callingPackage,
                             false, null, overrideableByRestore);
-                    break;
                 }
-
-                case MUTATION_OPERATION_DELETE: {
+                case MUTATION_OPERATION_DELETE -> {
                     success = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, false, null);
-                    break;
                 }
-
-                case MUTATION_OPERATION_UPDATE: {
+                case MUTATION_OPERATION_UPDATE -> {
                     validateSystemSettingValue(name, value);
                     success = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, value, null, false, callingPackage,
                             false, null);
-                    break;
                 }
-
-                case MUTATION_OPERATION_RESET: {
+                case MUTATION_OPERATION_RESET -> {
                     success = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM,
                             runAsUserId, callingPackage, mode, tag);
-                    break;
                 }
-
-                default:
+                default -> {
                     success = false;
                     Slog.e(LOG_TAG, "Unknown operation code: " + operation);
+                }
             }
         }
 
@@ -2113,8 +2063,8 @@
      * Returns {@code true} if the specified secure setting should be accessible to the caller.
      */
     private boolean isSecureSettingAccessible(String name) {
-        switch (name) {
-            case "bluetooth_address":
+        return switch (name) {
+            case "bluetooth_address" ->
                 // BluetoothManagerService for some reason stores the Android's Bluetooth MAC
                 // address in this secure setting. Secure settings can normally be read by any app,
                 // which thus enables them to bypass the recently introduced restrictions on access
@@ -2122,22 +2072,23 @@
                 // To mitigate this we make this setting available only to callers privileged to see
                 // this device's MAC addresses, same as through public API
                 // BluetoothAdapter.getAddress() (see BluetoothManagerService for details).
-                return getContext().checkCallingOrSelfPermission(
-                        Manifest.permission.LOCAL_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
-            default:
-                return true;
-        }
+                    getContext().checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS)
+                            == PackageManager.PERMISSION_GRANTED;
+            default -> true;
+        };
     }
 
-    private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) {
-        return resolveOwningUserIdLocked(userId, sSecureCloneToManagedSettings, setting);
+    private int resolveOwningUserIdForSecureSetting(int userId, String setting) {
+        // no need to lock because sSecureCloneToManagedSettings is never modified
+        return resolveOwningUserId(userId, sSecureCloneToManagedSettings, setting);
     }
 
+    @GuardedBy("mLock")
     private int resolveOwningUserIdForSystemSettingLocked(int userId, String setting) {
         final int parentId;
         // Resolves dependency if setting has a dependency and the calling user has a parent
         if (sSystemCloneFromParentOnDependency.containsKey(setting)
-                && (parentId = getGroupParentLocked(userId)) != userId) {
+                && (parentId = getGroupParent(userId)) != userId) {
             // The setting has a dependency and the profile has a parent
             String dependency = sSystemCloneFromParentOnDependency.get(setting);
             // Lookup the dependency setting as ourselves, some callers may not have access to it.
@@ -2151,11 +2102,11 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
-        return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting);
+        return resolveOwningUserId(userId, sSystemCloneToManagedSettings, setting);
     }
 
-    private int resolveOwningUserIdLocked(int userId, Set<String> keys, String name) {
-        final int parentId = getGroupParentLocked(userId);
+    private int resolveOwningUserId(int userId, Set<String> keys, String name) {
+        final int parentId = getGroupParent(userId);
         if (parentId != userId && keys.contains(name)) {
             return parentId;
         }
@@ -2174,9 +2125,8 @@
         }
 
         switch (operation) {
-            case MUTATION_OPERATION_INSERT:
-                // Insert updates.
-            case MUTATION_OPERATION_UPDATE: {
+            // Insert updates.
+            case MUTATION_OPERATION_INSERT, MUTATION_OPERATION_UPDATE -> {
                 if (Settings.System.PUBLIC_SETTINGS.contains(name)) {
                     return;
                 }
@@ -2192,9 +2142,8 @@
 
                 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
                         packageInfo.applicationInfo.targetSdkVersion, name);
-            } break;
-
-            case MUTATION_OPERATION_DELETE: {
+            }
+            case MUTATION_OPERATION_DELETE -> {
                 if (Settings.System.PUBLIC_SETTINGS.contains(name)
                         || Settings.System.PRIVATE_SETTINGS.contains(name)) {
                     throw new IllegalArgumentException("You cannot delete system defined"
@@ -2212,34 +2161,26 @@
 
                 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
                         packageInfo.applicationInfo.targetSdkVersion, name);
-            } break;
+            }
         }
     }
 
-    private Set<String> getInstantAppAccessibleSettings(int settingsType) {
-        switch (settingsType) {
-            case SETTINGS_TYPE_GLOBAL:
-                return Settings.Global.INSTANT_APP_SETTINGS;
-            case SETTINGS_TYPE_SECURE:
-                return Settings.Secure.INSTANT_APP_SETTINGS;
-            case SETTINGS_TYPE_SYSTEM:
-                return Settings.System.INSTANT_APP_SETTINGS;
-            default:
-                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
-        }
+    private static Set<String> getInstantAppAccessibleSettings(int settingsType) {
+        return switch (settingsType) {
+            case SETTINGS_TYPE_GLOBAL -> Global.INSTANT_APP_SETTINGS;
+            case SETTINGS_TYPE_SECURE -> Secure.INSTANT_APP_SETTINGS;
+            case SETTINGS_TYPE_SYSTEM -> Settings.System.INSTANT_APP_SETTINGS;
+            default -> throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+        };
     }
 
-    private Set<String> getOverlayInstantAppAccessibleSettings(int settingsType) {
-        switch (settingsType) {
-            case SETTINGS_TYPE_GLOBAL:
-                return OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS;
-            case SETTINGS_TYPE_SYSTEM:
-                return OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS;
-            case SETTINGS_TYPE_SECURE:
-                return OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS;
-            default:
-                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
-        }
+    private static Set<String> getOverlayInstantAppAccessibleSettings(int settingsType) {
+        return switch (settingsType) {
+            case SETTINGS_TYPE_GLOBAL -> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS;
+            case SETTINGS_TYPE_SYSTEM -> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS;
+            case SETTINGS_TYPE_SECURE -> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS;
+            default -> throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+        };
     }
 
     @GuardedBy("mLock")
@@ -2270,7 +2211,7 @@
         switch (settingName) {
             // missing READ_PRIVILEGED_PHONE_STATE permission protection
             // see alternative API {@link SubscriptionManager#getPreferredDataSubscriptionId()
-            case Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION:
+            case Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION -> {
                 // app-compat handling, not break apps targeting on previous SDKs.
                 if (CompatChanges.isChangeEnabled(
                         ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL)) {
@@ -2278,7 +2219,7 @@
                             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
                             "access global settings MULTI_SIM_DATA_CALL_SUBSCRIPTION");
                 }
-                break;
+            }
         }
         if (!ai.isInstantApp()) {
             return;
@@ -2306,23 +2247,22 @@
         final Set<String> readableFields;
         final ArrayMap<String, Integer> readableFieldsWithMaxTargetSdk;
         switch (settingsType) {
-            case SETTINGS_TYPE_GLOBAL:
+            case SETTINGS_TYPE_GLOBAL -> {
                 allFields = sAllGlobalSettings;
                 readableFields = sReadableGlobalSettings;
                 readableFieldsWithMaxTargetSdk = sReadableGlobalSettingsWithMaxTargetSdk;
-                break;
-            case SETTINGS_TYPE_SYSTEM:
+            }
+            case SETTINGS_TYPE_SYSTEM -> {
                 allFields = sAllSystemSettings;
                 readableFields = sReadableSystemSettings;
                 readableFieldsWithMaxTargetSdk = sReadableSystemSettingsWithMaxTargetSdk;
-                break;
-            case SETTINGS_TYPE_SECURE:
+            }
+            case SETTINGS_TYPE_SECURE -> {
                 allFields = sAllSecureSettings;
                 readableFields = sReadableSecureSettings;
                 readableFieldsWithMaxTargetSdk = sReadableSecureSettingsWithMaxTargetSdk;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+            }
+            default -> throw new IllegalArgumentException("Invalid settings type: " + settingsType);
         }
 
         if (allFields.contains(settingName)) {
@@ -2380,7 +2320,7 @@
         throw new IllegalStateException("Calling package doesn't exist");
     }
 
-    private int getGroupParentLocked(int userId) {
+    private int getGroupParent(int userId) {
         // Most frequent use case.
         if (userId == UserHandle.USER_SYSTEM) {
             return userId;
@@ -2480,7 +2420,7 @@
         }
     }
 
-    private static int resolveCallingUserIdEnforcingPermissionsLocked(int requestingUserId) {
+    private static int resolveCallingUserIdEnforcingPermissions(int requestingUserId) {
         if (requestingUserId == UserHandle.getCallingUserId()) {
             return requestingUserId;
         }
@@ -2654,28 +2594,28 @@
     private static int getResetModeEnforcingPermission(Bundle args) {
         final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0;
         switch (mode) {
-            case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
+            case Settings.RESET_MODE_UNTRUSTED_DEFAULTS -> {
                 if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) {
                     throw new SecurityException("Only system, shell/root on a "
                             + "debuggable build can reset to untrusted defaults");
                 }
                 return mode;
             }
-            case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
+            case Settings.RESET_MODE_UNTRUSTED_CHANGES -> {
                 if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) {
                     throw new SecurityException("Only system, shell/root on a "
                             + "debuggable build can reset untrusted changes");
                 }
                 return mode;
             }
-            case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
+            case Settings.RESET_MODE_TRUSTED_DEFAULTS -> {
                 if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) {
                     throw new SecurityException("Only system, shell/root on a "
                             + "debuggable build can reset to trusted defaults");
                 }
                 return mode;
             }
-            case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
+            case Settings.RESET_MODE_PACKAGE_DEFAULTS -> {
                 return mode;
             }
         }
@@ -2736,21 +2676,18 @@
             String column = cursor.getColumnName(i);
 
             switch (column) {
-                case Settings.NameValueTable._ID: {
+                case Settings.NameValueTable._ID -> {
                     values[i] = setting.getId();
-                } break;
-
-                case Settings.NameValueTable.NAME: {
+                }
+                case Settings.NameValueTable.NAME -> {
                     values[i] = setting.getName();
-                } break;
-
-                case Settings.NameValueTable.VALUE: {
+                }
+                case Settings.NameValueTable.VALUE -> {
                     values[i] = setting.getValue();
-                } break;
-
-                case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE: {
+                }
+                case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE -> {
                     values[i] = String.valueOf(setting.isValuePreservedInRestore());
-                } break;
+                }
             }
         }
 
@@ -2762,19 +2699,11 @@
     }
 
     private String resolveCallingPackage() {
-        switch (Binder.getCallingUid()) {
-            case Process.ROOT_UID: {
-                return "root";
-            }
-
-            case Process.SHELL_UID: {
-                return "com.android.shell";
-            }
-
-            default: {
-                return getCallingPackage();
-            }
-        }
+        return switch (Binder.getCallingUid()) {
+            case Process.ROOT_UID -> "root";
+            case Process.SHELL_UID -> "com.android.shell";
+            default -> getCallingPackage();
+        };
     }
 
     private static final class Arguments {
@@ -2796,17 +2725,17 @@
         public Arguments(Uri uri, String where, String[] whereArgs, boolean supportAll) {
             final int segmentSize = uri.getPathSegments().size();
             switch (segmentSize) {
-                case 1: {
+                case 1 -> {
                     if (where != null
                             && (WHERE_PATTERN_WITH_PARAM_NO_BRACKETS.matcher(where).matches()
-                                || WHERE_PATTERN_WITH_PARAM_IN_BRACKETS.matcher(where).matches())
+                            || WHERE_PATTERN_WITH_PARAM_IN_BRACKETS.matcher(where).matches())
                             && whereArgs.length == 1) {
                         name = whereArgs[0];
                         table = computeTableForSetting(uri, name);
                         return;
                     } else if (where != null
                             && (WHERE_PATTERN_NO_PARAM_NO_BRACKETS.matcher(where).matches()
-                                || WHERE_PATTERN_NO_PARAM_IN_BRACKETS.matcher(where).matches())) {
+                            || WHERE_PATTERN_NO_PARAM_IN_BRACKETS.matcher(where).matches())) {
                         final int startIndex = Math.max(where.indexOf("'"),
                                 where.indexOf("\"")) + 1;
                         final int endIndex = Math.max(where.lastIndexOf("'"),
@@ -2819,15 +2748,14 @@
                         table = computeTableForSetting(uri, null);
                         return;
                     }
-                } break;
-
-                case 2: {
+                }
+                case 2 -> {
                     if (where == null && whereArgs == null) {
                         name = uri.getPathSegments().get(1);
                         table = computeTableForSetting(uri, name);
                         return;
                     }
-                } break;
+                }
             }
 
             EventLogTags.writeUnsupportedSettingsQuery(
@@ -2956,10 +2884,11 @@
 
         public SettingsRegistry() {
             mHandler = new MyHandler(getContext().getMainLooper());
-            mGenerationRegistry = new GenerationRegistry(mLock, UserManager.getMaxSupportedUsers());
+            mGenerationRegistry = new GenerationRegistry(UserManager.getMaxSupportedUsers());
             mBackupManager = new BackupManager(getContext());
         }
 
+        @GuardedBy("mLock")
         private void generateUserKeyLocked(int userId) {
             // Generate a random key for each user used for creating a new ssaid.
             final byte[] keyBytes = new byte[32];
@@ -2983,6 +2912,7 @@
             return ByteBuffer.allocate(4).putInt(data.length).array();
         }
 
+        @GuardedBy("mLock")
         public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) {
             // Read the user's key from the ssaid table.
             Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
@@ -3044,6 +2974,7 @@
             return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid);
         }
 
+        @GuardedBy("mLock")
         private void syncSsaidTableOnStartLocked() {
             // Verify that each user's packages and ssaid's are in sync.
             for (UserInfo user : mUserManager.getAliveUsers()) {
@@ -3078,15 +3009,17 @@
             }
         }
 
+        @GuardedBy("mLock")
         public List<String> getSettingsNamesLocked(int type, int userId) {
             final int key = makeKey(type, userId);
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState == null) {
                 return new ArrayList<>();
             }
             return settingsState.getSettingNamesLocked();
         }
 
+        @GuardedBy("mLock")
         public SparseBooleanArray getKnownUsersLocked() {
             SparseBooleanArray users = new SparseBooleanArray();
             for (int i = mSettingsStates.size()-1; i >= 0; i--) {
@@ -3095,17 +3028,19 @@
             return users;
         }
 
+        @GuardedBy("mLock")
         @Nullable
         public SettingsState getSettingsLocked(int type, int userId) {
             final int key = makeKey(type, userId);
-            return peekSettingsStateLocked(key);
+            return mSettingsStates.get(key);
         }
 
-        public boolean ensureSettingsForUserLocked(int userId) {
+        @GuardedBy("mLock")
+        public void ensureSettingsForUserLocked(int userId) {
             // First make sure this user actually exists.
             if (mUserManager.getUserInfo(userId) == null) {
                 Slog.wtf(LOG_TAG, "Requested user " + userId + " does not exist");
-                return false;
+                return;
             }
 
             // Migrate the setting for this user if needed.
@@ -3143,9 +3078,9 @@
             // Upgrade the settings to the latest version.
             UpgradeController upgrader = new UpgradeController(userId);
             upgrader.upgradeIfNeededLocked();
-            return true;
         }
 
+        @GuardedBy("mLock")
         private void ensureSettingsStateLocked(int key) {
             if (mSettingsStates.get(key) == null) {
                 final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key));
@@ -3155,6 +3090,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         public void removeUserStateLocked(int userId, boolean permanently) {
             // We always keep the global settings in memory.
 
@@ -3166,12 +3102,7 @@
                     mSettingsStates.remove(systemKey);
                     systemSettingsState.destroyLocked(null);
                 } else {
-                    systemSettingsState.destroyLocked(new Runnable() {
-                        @Override
-                        public void run() {
-                            mSettingsStates.remove(systemKey);
-                        }
-                    });
+                    systemSettingsState.destroyLocked(() -> mSettingsStates.remove(systemKey));
                 }
             }
 
@@ -3183,12 +3114,7 @@
                     mSettingsStates.remove(secureKey);
                     secureSettingsState.destroyLocked(null);
                 } else {
-                    secureSettingsState.destroyLocked(new Runnable() {
-                        @Override
-                        public void run() {
-                            mSettingsStates.remove(secureKey);
-                        }
-                    });
+                    secureSettingsState.destroyLocked(() -> mSettingsStates.remove(secureKey));
                 }
             }
 
@@ -3200,12 +3126,7 @@
                     mSettingsStates.remove(ssaidKey);
                     ssaidSettingsState.destroyLocked(null);
                 } else {
-                    ssaidSettingsState.destroyLocked(new Runnable() {
-                        @Override
-                        public void run() {
-                            mSettingsStates.remove(ssaidKey);
-                        }
-                    });
+                    ssaidSettingsState.destroyLocked(() -> mSettingsStates.remove(ssaidKey));
                 }
             }
 
@@ -3213,6 +3134,7 @@
             mGenerationRegistry.onUserRemoved(userId);
         }
 
+        @GuardedBy("mLock")
         public boolean insertSettingLocked(int type, int userId, String name, String value,
                 String tag, boolean makeDefault, String packageName, boolean forceNotify,
                 Set<String> criticalSettings, boolean overrideableByRestore) {
@@ -3220,6 +3142,7 @@
                     packageName, forceNotify, criticalSettings, overrideableByRestore);
         }
 
+        @GuardedBy("mLock")
         public boolean insertSettingLocked(int type, int userId, String name, String value,
                 String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
                 boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) {
@@ -3232,7 +3155,7 @@
 
             boolean success = false;
             boolean wasUnsetNonPredefinedSetting = false;
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState != null) {
                 if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
                     wasUnsetNonPredefinedSetting = true;
@@ -3264,9 +3187,10 @@
          * Set Config Settings using consumed keyValues, returns true if the keyValues can be set,
          * false otherwise.
          */
+        @GuardedBy("mLock")
         public boolean setConfigSettingsLocked(int key, String prefix,
                 Map<String, String> keyValues, String packageName) {
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState != null) {
                 if (settingsState.isNewConfigBannedLocked(prefix, keyValues)) {
                     return false;
@@ -3283,12 +3207,13 @@
             return true;
         }
 
+        @GuardedBy("mLock")
         public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
                 Set<String> criticalSettings) {
             final int key = makeKey(type, userId);
 
             boolean success = false;
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState != null) {
                 success = settingsState.deleteSettingLocked(name);
             }
@@ -3306,13 +3231,14 @@
             return success;
         }
 
+        @GuardedBy("mLock")
         public boolean updateSettingLocked(int type, int userId, String name, String value,
                 String tag, boolean makeDefault, String packageName, boolean forceNotify,
                 Set<String> criticalSettings) {
             final int key = makeKey(type, userId);
 
             boolean success = false;
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState != null) {
                 success = settingsState.updateSettingLocked(name, value, tag,
                         makeDefault, packageName);
@@ -3331,10 +3257,11 @@
             return success;
         }
 
+        @GuardedBy("mLock")
         public Setting getSettingLocked(int type, int userId, String name) {
             final int key = makeKey(type, userId);
 
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState == null) {
                 return null;
             }
@@ -3352,16 +3279,18 @@
             return Global.SECURE_FRP_MODE.equals(setting.getName());
         }
 
+        @GuardedBy("mLock")
         public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag) {
             return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
                     null);
         }
 
+        @GuardedBy("mLock")
         public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag, @Nullable String prefix) {
             final int key = makeKey(type, userId);
-            SettingsState settingsState = peekSettingsStateLocked(key);
+            SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState == null) {
                 return false;
             }
@@ -3369,7 +3298,7 @@
             boolean success = false;
             banConfigurationIfNecessary(type, prefix, settingsState);
             switch (mode) {
-                case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
+                case Settings.RESET_MODE_PACKAGE_DEFAULTS -> {
                     for (String name : settingsState.getSettingNamesLocked()) {
                         boolean someSettingChanged = false;
                         Setting setting = settingsState.getSettingLocked(name);
@@ -3389,9 +3318,8 @@
                             success = true;
                         }
                     }
-                } break;
-
-                case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
+                }
+                case Settings.RESET_MODE_UNTRUSTED_DEFAULTS -> {
                     for (String name : settingsState.getSettingNamesLocked()) {
                         boolean someSettingChanged = false;
                         Setting setting = settingsState.getSettingLocked(name);
@@ -3411,9 +3339,8 @@
                             success = true;
                         }
                     }
-                } break;
-
-                case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
+                }
+                case Settings.RESET_MODE_UNTRUSTED_CHANGES -> {
                     for (String name : settingsState.getSettingNamesLocked()) {
                         boolean someSettingChanged = false;
                         Setting setting = settingsState.getSettingLocked(name);
@@ -3439,9 +3366,8 @@
                             success = true;
                         }
                     }
-                } break;
-
-                case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
+                }
+                case Settings.RESET_MODE_TRUSTED_DEFAULTS -> {
                     for (String name : settingsState.getSettingNamesLocked()) {
                         Setting setting = settingsState.getSettingLocked(name);
                         boolean someSettingChanged = false;
@@ -3464,11 +3390,12 @@
                             success = true;
                         }
                     }
-                } break;
+                }
             }
             return success;
         }
 
+        @GuardedBy("mLock")
         public void removeSettingsForPackageLocked(String packageName, int userId) {
             // Global and secure settings are signature protected. Apps signed
             // by the platform certificate are generally not uninstalled  and
@@ -3482,6 +3409,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         public void onUidRemovedLocked(int uid) {
             final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID,
                     UserHandle.getUserId(uid));
@@ -3490,19 +3418,7 @@
             }
         }
 
-        @Nullable
-        private SettingsState peekSettingsStateLocked(int key) {
-            SettingsState settingsState = mSettingsStates.get(key);
-            if (settingsState != null) {
-                return settingsState;
-            }
-
-            if (!ensureSettingsForUserLocked(getUserIdFromKey(key))) {
-                return null;
-            }
-            return mSettingsStates.get(key);
-        }
-
+        @GuardedBy("mLock")
         private void migrateAllLegacySettingsIfNeededLocked() {
             final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
             File globalFile = getSettingsFile(key);
@@ -3538,6 +3454,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         private void migrateLegacySettingsForUserIfNeededLocked(int userId) {
             // Every user has secure settings and if no file we need to migrate.
             final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
@@ -3552,6 +3469,7 @@
             migrateLegacySettingsForUserLocked(dbHelper, database, userId);
         }
 
+        @GuardedBy("mLock")
         private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,
                 SQLiteDatabase database, int userId) {
             // Move over the system settings.
@@ -3596,6 +3514,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         private void migrateLegacySettingsLocked(SettingsState settingsState,
                 SQLiteDatabase database, String table) {
             SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
@@ -3630,7 +3549,7 @@
             }
         }
 
-        @GuardedBy("secureSettings.mLock")
+        @GuardedBy("mLock")
         private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) {
             Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID);
 
@@ -3706,6 +3625,7 @@
                     name, type, changeType);
         }
 
+        @GuardedBy("mLock")
         private void notifyForConfigSettingsChangeLocked(int key, String prefix,
                 List<String> changedSettings) {
 
@@ -3787,30 +3707,18 @@
             }
         }
 
-        private File getSettingsFile(int key) {
-            if (isConfigSettingsKey(key)) {
-                final int userId = getUserIdFromKey(key);
-                return new File(Environment.getUserSystemDirectory(userId),
-                        SETTINGS_FILE_CONFIG);
-            } else if (isGlobalSettingsKey(key)) {
-                final int userId = getUserIdFromKey(key);
-                return new File(Environment.getUserSystemDirectory(userId),
-                        SETTINGS_FILE_GLOBAL);
-            } else if (isSystemSettingsKey(key)) {
-                final int userId = getUserIdFromKey(key);
-                return new File(Environment.getUserSystemDirectory(userId),
-                        SETTINGS_FILE_SYSTEM);
-            } else if (isSecureSettingsKey(key)) {
-                final int userId = getUserIdFromKey(key);
-                return new File(Environment.getUserSystemDirectory(userId),
-                        SETTINGS_FILE_SECURE);
-            } else if (isSsaidSettingsKey(key)) {
-                final int userId = getUserIdFromKey(key);
-                return new File(Environment.getUserSystemDirectory(userId),
-                        SETTINGS_FILE_SSAID);
-            } else {
-                throw new IllegalArgumentException("Invalid settings key:" + key);
-            }
+        private static File getSettingsFile(int key) {
+            final int userId = getUserIdFromKey(key);
+            final int type = getTypeFromKey(key);
+            final File userSystemDirectory = Environment.getUserSystemDirectory(userId);
+            return switch (type) {
+                case SETTINGS_TYPE_CONFIG -> new File(userSystemDirectory, SETTINGS_FILE_CONFIG);
+                case SETTINGS_TYPE_GLOBAL -> new File(userSystemDirectory, SETTINGS_FILE_GLOBAL);
+                case SETTINGS_TYPE_SYSTEM -> new File(userSystemDirectory, SETTINGS_FILE_SYSTEM);
+                case SETTINGS_TYPE_SECURE -> new File(userSystemDirectory, SETTINGS_FILE_SECURE);
+                case SETTINGS_TYPE_SSAID -> new File(userSystemDirectory, SETTINGS_FILE_SSAID);
+                default -> throw new IllegalArgumentException("Invalid settings key:" + key);
+            };
         }
 
         private Uri getNotificationUriFor(int key, String name) {
@@ -3833,14 +3741,11 @@
 
         private int getMaxBytesPerPackageForType(int type) {
             switch (type) {
-                case SETTINGS_TYPE_CONFIG:
-                case SETTINGS_TYPE_GLOBAL:
-                case SETTINGS_TYPE_SECURE:
-                case SETTINGS_TYPE_SSAID: {
+                case SETTINGS_TYPE_CONFIG, SETTINGS_TYPE_GLOBAL, SETTINGS_TYPE_SECURE,
+                        SETTINGS_TYPE_SSAID -> {
                     return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED;
                 }
-
-                default: {
+                default -> {
                     return SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED;
                 }
             }
@@ -3857,7 +3762,7 @@
             @Override
             public void handleMessage(Message msg) {
                 switch (msg.what) {
-                    case MSG_NOTIFY_URI_CHANGED: {
+                    case MSG_NOTIFY_URI_CHANGED -> {
                         final int userId = msg.arg1;
                         Uri uri = (Uri) msg.obj;
                         try {
@@ -3868,12 +3773,11 @@
                         if (DEBUG) {
                             Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri);
                         }
-                    } break;
-
-                    case MSG_NOTIFY_DATA_CHANGED: {
+                    }
+                    case MSG_NOTIFY_DATA_CHANGED -> {
                         mBackupManager.dataChanged();
                         scheduleWriteFallbackFilesJob();
-                    } break;
+                    }
                 }
             }
         }
@@ -3887,6 +3791,7 @@
                 mUserId = userId;
             }
 
+            @GuardedBy("mLock")
             public void upgradeIfNeededLocked() {
                 // The version of all settings for a user is the same (all users have secure).
                 SettingsState secureSettings = getSettingsLocked(
@@ -3944,18 +3849,22 @@
                 systemSettings.setVersionLocked(newVersion);
             }
 
+            @GuardedBy("mLock")
             private SettingsState getGlobalSettingsLocked() {
                 return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
             }
 
+            @GuardedBy("mLock")
             private SettingsState getSecureSettingsLocked(int userId) {
                 return getSettingsLocked(SETTINGS_TYPE_SECURE, userId);
             }
 
+            @GuardedBy("mLock")
             private SettingsState getSsaidSettingsLocked(int userId) {
                 return getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
             }
 
+            @GuardedBy("mLock")
             private SettingsState getSystemSettingsLocked(int userId) {
                 return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId);
             }
@@ -5399,7 +5308,7 @@
                     // next version step.
                     // If this is a new profile, check if a secure setting exists for the
                     // owner of the profile and use that value for the work profile.
-                    int owningId = resolveOwningUserIdForSecureSettingLocked(userId,
+                    int owningId = resolveOwningUserIdForSecureSetting(userId,
                             NOTIFICATION_BUBBLES);
                     Setting previous = getGlobalSettingsLocked()
                             .getSettingLocked("notification_bubbles");
@@ -6068,18 +5977,22 @@
                 return currentVersion;
             }
 
+            @GuardedBy("mLock")
             private void initGlobalSettingsDefaultValLocked(String key, boolean val) {
                 initGlobalSettingsDefaultValLocked(key, val ? "1" : "0");
             }
 
+            @GuardedBy("mLock")
             private void initGlobalSettingsDefaultValLocked(String key, int val) {
                 initGlobalSettingsDefaultValLocked(key, String.valueOf(val));
             }
 
+            @GuardedBy("mLock")
             private void initGlobalSettingsDefaultValLocked(String key, long val) {
                 initGlobalSettingsDefaultValLocked(key, String.valueOf(val));
             }
 
+            @GuardedBy("mLock")
             private void initGlobalSettingsDefaultValLocked(String key, String val) {
                 final SettingsState globalSettings = getGlobalSettingsLocked();
                 Setting currentSetting = globalSettings.getSettingLocked(key);
@@ -6198,6 +6111,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         private void ensureLegacyDefaultValueAndSystemSetUpdatedLocked(SettingsState settings,
                 int userId) {
             List<String> names = settings.getSettingNamesLocked();
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 7bca944..9dacade 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -904,6 +904,7 @@
                         Settings.Secure.EXTRA_AUTOMATIC_POWER_SAVE_MODE,
                         Settings.Secure.GAME_DASHBOARD_ALWAYS_ON,
                         Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
+                        Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT,
                         Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING,
                         Settings.Secure.LOCATION_COARSE_ACCURACY_M,
                         Settings.Secure.LOCATION_SHOW_SYSTEM_OPS,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 12865f4..8029785 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -36,7 +36,7 @@
 public class GenerationRegistryTest {
     @Test
     public void testGenerationsWithRegularSetting() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
+        final GenerationRegistry generationRegistry = new GenerationRegistry(2);
         final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
@@ -93,7 +93,7 @@
 
     @Test
     public void testGenerationsWithConfigSetting() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
+        final GenerationRegistry generationRegistry = new GenerationRegistry(1);
         final String prefix = "test_namespace/";
         final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
 
@@ -110,7 +110,7 @@
 
     @Test
     public void testMaxNumBackingStores() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
+        final GenerationRegistry generationRegistry = new GenerationRegistry(2);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
         for (int i = 0; i < generationRegistry.getMaxNumBackingStores(); i++) {
@@ -133,7 +133,7 @@
 
     @Test
     public void testMaxSizeBackingStore() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
+        final GenerationRegistry generationRegistry = new GenerationRegistry(1);
         final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
@@ -153,7 +153,7 @@
 
     @Test
     public void testUnsetSettings() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
+        final GenerationRegistry generationRegistry = new GenerationRegistry(1);
         final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
@@ -172,7 +172,7 @@
 
     @Test
     public void testGlobalSettings() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
+        final GenerationRegistry generationRegistry = new GenerationRegistry(2);
         final int globalKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, 0);
         final String testGlobalSetting = "test_global_setting";
         final Bundle b = new Bundle();
@@ -190,11 +190,11 @@
 
     @Test
     public void testNumberOfBackingStores() {
-        GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 0);
+        GenerationRegistry generationRegistry = new GenerationRegistry(0);
         // Test that the capacity of the backing stores is always valid
         assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
                 GenerationRegistry.MIN_NUM_BACKING_STORE);
-        generationRegistry = new GenerationRegistry(new Object(), 100);
+        generationRegistry = new GenerationRegistry(100);
         // Test that the capacity of the backing stores is always valid
         assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
                 GenerationRegistry.MAX_NUM_BACKING_STORE);
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
index 2b33057..2984cd3 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
@@ -17,10 +17,12 @@
 package com.android.providers.settings;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assume.assumeTrue;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.Process;
@@ -42,6 +44,8 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
 
 @LargeTest
 public class InstallNonMarketAppsDeprecationTest extends BaseSettingsProviderTest {
@@ -54,35 +58,42 @@
     private boolean mSystemSetUserRestriction;
     private List<Integer> mUsersAddedByTest;
 
-    private String waitTillValueChanges(String errorMessage, String oldValue) {
+    private static String waitTillValueChanges(String errorMessage, @Nullable String oldValue,
+                                        Supplier<String> action) {
         boolean interrupted = false;
         final long startTime = SystemClock.uptimeMillis();
-        String newValue = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
-        while (newValue.equals(oldValue) && SystemClock.uptimeMillis() <= (startTime
+        String newValue = action.get();
+        while (Objects.equals(newValue, oldValue) && SystemClock.uptimeMillis() <= (startTime
                 + USER_RESTRICTION_CHANGE_TIMEOUT)) {
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException exc) {
                 interrupted = true;
             }
-            newValue = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
+            newValue = action.get();
         }
         if (interrupted) {
             Thread.currentThread().interrupt();
         }
-        assertFalse(errorMessage, oldValue.equals(newValue));
+        assertNotEquals(errorMessage, newValue, oldValue);
         return newValue;
     }
 
-    private String getSecureSettingForUserViaShell(int userId) throws IOException {
+    private String getSecureSettingForUserViaShell(int userId) {
         StringBuilder sb = new StringBuilder("settings get --user ");
         sb.append(userId + " secure ");
         sb.append(Settings.Secure.INSTALL_NON_MARKET_APPS);
         BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(
                 InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
                         sb.toString()).getFileDescriptor())));
-        String line = reader.readLine();
-        return line.trim();
+        String line;
+        try {
+            line = reader.readLine();
+        } catch (IOException e) {
+            return null;
+        }
+        final String result = line.trim();
+        return (result.isEmpty() || result.equals("null")) ? null : result;
     }
 
     @Override
@@ -121,7 +132,10 @@
 
         UserInfo newUser = mUm.createUser("TEST_USER", 0);
         mUsersAddedByTest.add(newUser.id);
-        String value = getSecureSettingForUserViaShell(newUser.id);
+        // wait till SettingsProvider reacts to the USER_ADDED event
+        String value = waitTillValueChanges(
+                "install_non_market_apps should be set for the new user", null,
+                () -> getSecureSettingForUserViaShell(newUser.id));
         assertEquals("install_non_market_apps should be 1 for a new user", value, "1");
     }
 
@@ -140,13 +154,15 @@
         mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, !mHasUserRestriction);
         value = waitTillValueChanges(
                 "Changing user restriction did not change the value of install_non_market_apps",
-                value);
+                value,
+                () -> getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS));
         assertTrue("Invalid value", value.equals("1") || value.equals("0"));
 
         mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, mHasUserRestriction);
         value = waitTillValueChanges(
                 "Changing user restriction did not change the value of install_non_market_apps",
-                value);
+                value,
+                () -> getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS));
         assertTrue("Invalid value", value.equals("1") || value.equals("0"));
     }
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 11ae9c3..10d04d3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -860,6 +860,10 @@
     <!-- Permission required for CTS test - CtsWallpaperTestCases -->
     <uses-permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER" />
 
+    <!-- Permissions required for CTS test - CtsVoiceInteractionTestCases -->
+    <uses-permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT" />
+    <uses-permission android:name="android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA" />
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e6a82e8..17cc9f8 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -485,6 +485,8 @@
         "motion_tool_lib",
         "androidx.core_core-animation-testing-nodeps",
         "androidx.compose.ui_ui",
+        "flag-junit",
+        "platform-test-annotations",
     ],
 }
 
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 8f329b3..34545cf 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -36,6 +36,7 @@
 hwwang@google.com
 hyunyoungs@google.com
 ikateryna@google.com
+iyz@google.com
 jaggies@google.com
 jamesoleary@google.com
 jbolinger@google.com
@@ -54,10 +55,12 @@
 kozynski@google.com
 kprevas@google.com
 lusilva@google.com
+liuyining@google.com
 lynhan@google.com
 madym@google.com
 mankoff@google.com
 mateuszc@google.com
+matiashe@google.com
 mgalhardo@google.com
 michaelmikhil@google.com
 michschn@google.com
@@ -94,6 +97,7 @@
 tsuji@google.com
 twickham@google.com
 vadimt@google.com
+valiiftime@google.com
 vanjan@google.com
 victortulias@google.com
 winsonc@google.com
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index bcf1535..08ecf09b 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -8,3 +8,10 @@
     description: "Adjusts bounds to allow the floating menu to render on top of navigation bars."
     bug: "283768342"
 }
+
+flag {
+    name: "floating_menu_ime_displacement_animation"
+    namespace: "accessibility"
+    description: "Adds an animation for when the FAB is displaced by an IME becoming visible."
+    bug: "281150010"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 0567528..c26d5f5 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -23,6 +23,14 @@
 }
 
 flag {
+    name: "notifications_icon_container_refactor"
+    namespace: "systemui"
+    description: "Enables the refactored version of the notification icon container in StatusBar, "
+        "AOD, and the notification shelf. Should not bring any behavioral changes."
+    bug: "278765923"
+}
+
+flag {
     name: "notification_lifetime_extension_refactor"
     namespace: "systemui"
     description: "Enables moving notification lifetime extension management from SystemUI to "
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
index df87d19d..91fe33c 100644
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
@@ -30,6 +30,8 @@
          *
          * Currently treats the first supported size as the size to be rendered in, ignoring other
          * supported sizes.
+         *
+         * Cards are ordered by priority, from highest to lowest.
          */
         fun distributeCardsIntoColumns(
             cards: List<CommunalGridLayoutCard>,
@@ -37,7 +39,8 @@
             val result = ArrayList<ArrayList<CommunalGridLayoutCardInfo>>()
 
             var capacityOfLastColumn = 0
-            for (card in cards) {
+            val sorted = cards.sortedByDescending { it.priority }
+            for (card in sorted) {
                 val cardSize = card.supportedSizes.first()
                 if (capacityOfLastColumn >= cardSize.value) {
                     // Card fits in last column
diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
index c1974ca..50b7c5f 100644
--- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
+++ b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
@@ -41,7 +41,7 @@
                 ),
             )
 
-        assertDistribution(cards, expected)
+        assertDistributionBySize(cards, expected)
     }
 
     @Test
@@ -71,10 +71,30 @@
                 ),
             )
 
-        assertDistribution(cards, expected)
+        assertDistributionBySize(cards, expected)
     }
 
-    private fun assertDistribution(
+    @Test
+    fun distribution_sortByPriority() {
+        val cards =
+            listOf(
+                generateCard(priority = 2),
+                generateCard(priority = 7),
+                generateCard(priority = 10),
+                generateCard(priority = 1),
+                generateCard(priority = 5),
+            )
+        val expected =
+            listOf(
+                listOf(10, 7),
+                listOf(5, 2),
+                listOf(1),
+            )
+
+        assertDistributionByPriority(cards, expected)
+    }
+
+    private fun assertDistributionBySize(
         cards: List<CommunalGridLayoutCard>,
         expected: List<List<CommunalGridLayoutCard.Size>>,
     ) {
@@ -87,6 +107,19 @@
         }
     }
 
+    private fun assertDistributionByPriority(
+        cards: List<CommunalGridLayoutCard>,
+        expected: List<List<Int>>,
+    ) {
+        val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards)
+
+        for (c in expected.indices) {
+            for (r in expected[c].indices) {
+                assertThat(result[c][r].card.priority).isEqualTo(expected[c][r])
+            }
+        }
+    }
+
     private fun generateCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard {
         return object : CommunalGridLayoutCard() {
             override val supportedSizes = listOf(size)
@@ -97,4 +130,16 @@
             }
         }
     }
+
+    private fun generateCard(priority: Int): CommunalGridLayoutCard {
+        return object : CommunalGridLayoutCard() {
+            override val supportedSizes = listOf(Size.HALF)
+            override val priority = priority
+
+            @Composable
+            override fun Content(modifier: Modifier, size: SizeF) {
+                Card(modifier = modifier, content = {})
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 17c74ba..0e7694e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -315,7 +315,8 @@
                 }
             }
 
-            override fun shouldAnimateExit(): Boolean = isComposed.value
+            override fun shouldAnimateExit(): Boolean =
+                isComposed.value && composeViewRoot.isAttachedToWindow && composeViewRoot.isShown
 
             override fun onExitAnimationCancelled() {
                 isDialogShowing.value = false
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 689a0a2..eb71490 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -46,7 +46,6 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
-import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.layout
@@ -69,7 +68,6 @@
 import com.android.compose.modifiers.background
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.theme.colorAttr
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
@@ -78,6 +76,7 @@
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.res.R
 import kotlinx.coroutines.launch
 
 /** The Quick Settings footer actions row. */
@@ -89,8 +88,7 @@
 ) {
     val context = LocalContext.current
 
-    // Collect visibility and alphas as soon as we are composed, even when not visible.
-    val isVisible by viewModel.isVisible.collectAsState()
+    // Collect alphas as soon as we are composed, even when not visible.
     val alpha by viewModel.alpha.collectAsState()
     val backgroundAlpha = viewModel.backgroundAlpha.collectAsState()
 
@@ -142,11 +140,6 @@
         modifier
             .fillMaxWidth()
             .graphicsLayer { this.alpha = alpha }
-            .drawWithContent {
-                if (isVisible) {
-                    drawContent()
-                }
-            }
             .then(backgroundModifier)
             .padding(
                 top = dimensionResource(R.dimen.qs_footer_actions_top_padding),
@@ -328,7 +321,7 @@
         shape = CircleShape,
         color = colorAttr(R.attr.underSurface),
         contentColor = LocalAndroidColorScheme.current.onSurfaceVariant,
-        borderStroke = BorderStroke(1.dp, colorAttr(R.attr.onShadeActive)),
+        borderStroke = BorderStroke(1.dp, colorAttr(R.attr.shadeInactive)),
         modifier = modifier.padding(horizontal = 4.dp),
         onClick = onClick,
     ) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index d70a248..2dc53ab 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -157,6 +157,8 @@
      */
     private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() }
 
+    internal var gestureWithPriority: Any? = null
+
     internal fun onDragStarted() {
         if (isDrivingTransition) {
             // This [transition] was already driving the animation: simply take over it.
@@ -525,15 +527,21 @@
     private val gestureHandler: SceneGestureHandler,
 ) : DraggableHandler {
     override suspend fun onDragStarted(coroutineScope: CoroutineScope, startedPosition: Offset) {
+        gestureHandler.gestureWithPriority = this
         gestureHandler.onDragStarted()
     }
 
     override fun onDelta(pixels: Float) {
-        gestureHandler.onDrag(delta = pixels)
+        if (gestureHandler.gestureWithPriority == this) {
+            gestureHandler.onDrag(delta = pixels)
+        }
     }
 
     override suspend fun onDragStopped(coroutineScope: CoroutineScope, velocity: Float) {
-        gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true)
+        if (gestureHandler.gestureWithPriority == this) {
+            gestureHandler.gestureWithPriority = null
+            gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true)
+        }
     }
 }
 
@@ -615,10 +623,15 @@
             },
             canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key },
             onStart = {
+                gestureHandler.gestureWithPriority = this
                 priorityScene = nextScene
                 gestureHandler.onDragStarted()
             },
             onScroll = { offsetAvailable ->
+                if (gestureHandler.gestureWithPriority != this) {
+                    return@PriorityNestedScrollConnection Offset.Zero
+                }
+
                 val amount = offsetAvailable.toAmount()
 
                 // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
@@ -628,6 +641,10 @@
                 amount.toOffset()
             },
             onStop = { velocityAvailable ->
+                if (gestureHandler.gestureWithPriority != this) {
+                    return@PriorityNestedScrollConnection Velocity.Zero
+                }
+
                 priorityScene = null
 
                 gestureHandler.onDragStopped(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index 9b9e70d..6791a85 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -312,4 +312,52 @@
         advanceUntilIdle()
         assertScene(currentScene = SceneA, isIdle = true)
     }
+
+    @Test
+    fun beforeDraggableStart_drag_shouldBeIgnored() = runGestureTest {
+        draggable.onDelta(deltaInPixels10)
+        assertScene(currentScene = SceneA, isIdle = true)
+    }
+    @Test
+    fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest {
+        draggable.onDragStopped(coroutineScope, velocityThreshold)
+        assertScene(currentScene = SceneA, isIdle = true)
+    }
+
+    @Test
+    fun beforeNestedScrollStart_stop_shouldBeIgnored() = runGestureTest {
+        nestedScroll.onPreFling(Velocity(0f, velocityThreshold))
+        assertScene(currentScene = SceneA, isIdle = true)
+    }
+
+    @Test
+    fun startNestedScrollWhileDragging() = runGestureTest {
+        draggable.onDragStarted(coroutineScope, Offset.Zero)
+        assertScene(currentScene = SceneA, isIdle = false)
+        val transition = transitionState as Transition
+
+        draggable.onDelta(deltaInPixels10)
+        assertThat(transition.progress).isEqualTo(0.1f)
+
+        // now we can intercept the scroll events
+        nestedScrollEvents(available = offsetY10)
+        assertThat(transition.progress).isEqualTo(0.2f)
+
+        // this should be ignored, we are scrolling now!
+        draggable.onDragStopped(coroutineScope, velocityThreshold)
+        assertScene(currentScene = SceneA, isIdle = false)
+
+        nestedScrollEvents(available = offsetY10)
+        assertThat(transition.progress).isEqualTo(0.3f)
+
+        nestedScrollEvents(available = offsetY10)
+        assertThat(transition.progress).isEqualTo(0.4f)
+
+        nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold))
+        assertScene(currentScene = SceneC, isIdle = false)
+
+        // wait for the stop animation
+        advanceUntilIdle()
+        assertScene(currentScene = SceneC, isIdle = true)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 07ffd11..1620088 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -1,15 +1,17 @@
 /*
  * 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
+ * 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.
+ * 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.systemui.statusbar.phone;
@@ -19,8 +21,6 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -29,9 +29,9 @@
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -43,12 +43,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class SystemUIDialogTest extends SysuiTestCase {
@@ -76,12 +77,13 @@
 
         dialog.show();
         verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverCaptor.capture(),
-                intentFilterCaptor.capture(), eq(null), any());
+                intentFilterCaptor.capture(), ArgumentMatchers.eq(null), ArgumentMatchers.any());
         assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
         assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
 
         dialog.dismiss();
-        verify(mBroadcastDispatcher).unregisterReceiver(eq(broadcastReceiverCaptor.getValue()));
+        verify(mBroadcastDispatcher).unregisterReceiver(
+                ArgumentMatchers.eq(broadcastReceiverCaptor.getValue()));
     }
 
 
@@ -90,11 +92,12 @@
         final SystemUIDialog dialog = new SystemUIDialog(mContext, 0, false);
 
         dialog.show();
-        verify(mBroadcastDispatcher, never()).registerReceiver(any(), any(), eq(null), any());
+        verify(mBroadcastDispatcher, never()).registerReceiver(ArgumentMatchers.any(),
+                ArgumentMatchers.any(), ArgumentMatchers.eq(null), ArgumentMatchers.any());
         assertTrue(dialog.isShowing());
 
         dialog.dismiss();
-        verify(mBroadcastDispatcher, never()).unregisterReceiver(any());
+        verify(mBroadcastDispatcher, never()).unregisterReceiver(ArgumentMatchers.any());
         assertFalse(dialog.isShowing());
     }
 
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 2eb1bb5..a6a8122 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans stadig"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laaiproses word geoptimeer om battery te beskerm"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kwessie met laaibykomstigheid"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk Kieslys om te ontsluit."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk is gesluit"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Geen SIM nie"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Voeg ’n SIM by."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Die SIM is weg of nie leesbaar nie. Voeg ’n SIM by."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Onbruikbare SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Jou SIM is permanent gedeaktiveer.\n Kontak jou draadlose diensverskaffer vir ’n ander SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is gesluit."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-gesluit."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ontsluit tans SIM …"</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 5fd946b..fb84414 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ባትሪን ለመጠበቅ ኃይል መሙላት ተብቷል"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ተለዋዋጭን ኃይል በመሙላት ላይ ችግር"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ለመክፈት ምናሌ ተጫን።"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"አውታረ መረብ ተቆልፏል"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ምንም SIM የለም"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ሲም ያክሉ።"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ሲሙ ጠፍቷል ወይም አይነበብም። ሲም ያክሉ።"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ጥቅም ላይ የማይውል ሲም።"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ሲምዎ በቋሚነት ቦዝኗል።\n ለሌላ ሲም የእርስዎን አገልግሎት ሰጪ ያግኙ።"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ሲም ተቆልፏል።"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ሲም በPUK የተቆለፈ ነው።"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ሲምን በመክፈት ላይ…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index b6479f4..fb33092 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تم تحسين الشحن لحماية البطارية"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"‫<xliff:g id="PERCENTAGE">%s</xliff:g> • مشكلة متعلّقة بجهاز الشحن الملحق"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"‏لا تتوفر شريحة SIM."</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"‏يجب إضافة شريحة SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"‏شريحة SIM مفقودة أو غير قابلة للقراءة. يجب إضافة شريحة SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"‏شريحة SIM غير قابلة للاستخدام."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"‏تم إيقاف شريحة SIM نهائيًا.\n عليك التواصل مع مقدم خدمة اللاسلكي للحصول على شريحة SIM أخرى."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"‏شريحة SIM مُقفَلة."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‏شريحة SIM مُقفَلة برمز PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"‏جارٍ إلغاء قفل شريحة SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index a41a704..a123bb7 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেটাৰী সুৰক্ষিত কৰিবলৈ চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চাৰ্জিঙৰ আনুষংগিক সামগ্ৰীত সমস্যা হৈছে"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক কৰিবলৈ মেনু টিপক।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটৱর্ক লক কৰা অৱস্থাত আছে"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"কোনো ছিম নাই"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"এখন ছিম যোগ দিয়ক।"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ছিম নাই অথবা সেইখন পঢ়িব নোৱাৰি। এখন ছিম যোগ দিয়ক।"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ব্যৱহাৰ কৰিব নোৱৰা ছিম।"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"আপোনাৰ ছিমখন স্থায়ীভাৱে নিষ্ক্ৰিয় কৰা হৈছে।\n অন্য এখন ছিমৰ বাবে আপোনাৰ ৱায়াৰলেছ সেৱা প্ৰদানকাৰীৰ সৈতে যোগাযোগ কৰক।"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ছিমখন লক হৈ আছে।"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ছিমখন PUKৰ দ্বাৰা লক হৈ আছে।"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ছিম আনলক কৰি থকা হৈছে…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index ed969c7..b133b30a 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş enerji yığır"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareyanı qorumaq üçün şarj optimallaşdırılıb"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj aksesuarı ilə bağlı problem"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Şəbəkə kilidlidir"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM yoxdur"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM əlavə edin."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM kart yoxdur və ya oxuna bilinmir. SIM əlavə edin."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"İstifadəyə yararsız SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM kartınız həmişəlik deaktiv edilib.\n Başqa SIM kart üçün simsiz xidmət provayderinizə müraciət edin."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM kilidlənib."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM kart PUK ilə kilidlənib."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM kiliddən çıxarılır…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index b0a6471..9a91962 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je optimizovano da bi se zaštitila baterija"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem sa dodatnim priborom za punjenje"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otključali."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM nedostaje ili ne može da se pročita. Dodajte SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Neupotrebljiv SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM je trajno deaktiviran.\n Obratite se dobavljaču usluge bežične telefonije da biste dobili drugi SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM je zaključan."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM je zaključan PUK-om."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Otključava se SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 11cc77d..5e46b715 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе павольная зарадка"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • У мэтах зберажэння акумулятара зарадка аптымізавана"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Праблема з зараднай прыладай"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Націсніце кнопку \"Меню\", каб разблакіраваць."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сетка заблакіравана"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Няма SIM-карты"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Дадайце SIM-карту."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-карта адсутнічае ці не чытаецца. Дадайце SIM-карту."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Непрыдатная для выкарыстання SIM-карта."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Ваша SIM-карта адключана назаўсёды.\n Звяжыцеся з аператарам бесправадной сувязі, каб атрымаць іншую SIM-карту."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-карта заблакіравана."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-карта заблакіравана PUK-кодам."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Разблакіраванне SIM-карты…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index c554a27..ab931ed 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зареждането е оптимизирано с цел запазване на батерията"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблем със зарядното устройство"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натиснете „Меню“, за да отключите."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Няма SIM карта"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Добавете SIM карта."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM картата липсва или е нечетлива. Добавете SIM карта."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Неизползваема SIM карта."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM картата ви е деактивирана за постоянно.\nЗа да получите друга, се свържете с доставчика си на безжична услуга."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM картата е заключена."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM картата е заключена с PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картата се отключва…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 67b4e4b..e25de93 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ব্যাটারি ভাল রাখতে চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জিং অ্যাক্সেসরিতে সমস্যা রয়েছে"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক করতে মেনুতে টিপুন।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটওয়ার্ক লক করা আছে"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"কোনও সিম নেই"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"সিম যোগ করুন।"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"সিম নেই অথবা সেটি রিড করা যাচ্ছে না। সিম যোগ করুন।"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ব্যবহারযোগ্য নয় এমন সিম।"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"আপনার সিম স্থায়ীভাবে বন্ধ করে দেওয়া হয়েছে।\n অন্য একটি সিমের জন্য আপনার ওয়্যারলেস পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"সিম লক করা হয়েছে।"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"সিম PUK লক করা হয়েছে।"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"সিম আনলক করা হচ্ছে…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 4c519c8..cd7aaeb 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo punjenje"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je optimizirano radi zaštite baterije"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem s opremom za punjenje"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite meni da otključate."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM nedostaje ili se ne može čitati. Dodajte SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Neupotrebljiv SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM je trajno deaktiviran.\n Kontaktirajte pružaoca bežičnih usluga za drugi SIM"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM je zaključan."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM je zaključan PUK-om."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Otključavanje SIM-a…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 3bd6508..bf8a592 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Càrrega optimitzada per protegir la bateria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema relacionat amb l\'accessori de càrrega"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prem Menú per desbloquejar."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hi ha cap SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Afegeix una SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Falta la SIM o no es pot llegir. Afegeix una SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"La SIM no es pot utilitzar."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"La SIM s\'ha desactivat permanentment.\n Contacta amb el proveïdor de serveis sense fil per obtenir-ne una altra."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La SIM està bloquejada."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La SIM està bloquejada pel PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"S\'està desbloquejant la targeta SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 573638b..bedafd8 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pomalé nabíjení"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimalizované nabíjení za účelem ochrany baterie"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problém s nabíjecím příslušenstvím"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Klávesy odemknete stisknutím tlačítka nabídky."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Síť je blokována"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Žádná SIM karta"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Přidejte SIM kartu."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM karta chybí nebo je nečitelná. Přidejte SIM kartu."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM kartu nelze použít."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM karta byla natrvalo deaktivována.\n Požádejte svého poskytovatele bezdrátových služeb o další SIM kartu."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karta je zablokována."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karta je blokována pomocí kódu PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Odblokování SIM karty…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index c7c863b..93f505e 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladning er optimeret for at beskytte batteriet"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem med opladertilbehør"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tryk på menuen for at låse op."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netværket er låst"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Intet SIM-kort"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Tilføj et SIM-kort."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kortet mangler eller kan ikke læses. Tilføj et SIM-kort."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Deaktiveret SIM-kort."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Dit SIM-kort er permanent deaktiveret.\n Kontakt din tjenesteudbyder for at få et nyt SIM-kort."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortet er låst."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortet er låst med PUK-koden."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-kortet låses op…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 5c5f264..01e166e 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimiertes Laden zur Akkuschonung"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem mit dem Ladezubehör"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Keine SIM-Karte"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lege eine SIM-Karte ein."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-Karte fehlt oder ist nicht lesbar. Lege eine SIM-Karte ein."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-Karte ist nicht nutzbar."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Deine SIM-Karte wurde dauerhaft deaktiviert.\n Wende dich an deinen Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-Karte ist gesperrt."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-Karte ist mit einem PUK gesperrt."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-Karte wird entsperrt…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 3a01da5..9769242 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Αργή φόρτιση"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Η φόρτιση βελτιστοποιήθηκε για την προστασία της μπαταρίας"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Πρόβλημα αξεσουάρ φόρτισης"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Κλειδωμένο δίκτυο"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Δεν υπάρχει SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Προσθέστε μια SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Η SIM λείπει ή δεν είναι δυνατή η ανάγνωσή της. Προσθέστε μια SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Η SIM δεν μπορεί να χρησιμοποιηθεί."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Η SIM απενεργοποιήθηκε οριστικά.\n Επικοινωνήστε με τον πάροχο υπηρεσιών ασύρματου δικτύου για μια νέα SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Η SIM είναι κλειδωμένη."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Η SIM έχει κλειδωθεί με κωδικό PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ξεκλείδωμα SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index a4b13487..087ab3a 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimised to protect battery"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 480bcbb..7297cf9 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimized to protect battery"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index a4b13487..087ab3a 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimised to protect battery"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index a4b13487..087ab3a 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimised to protect battery"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index b8e89f4..ead8bce 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging slowly‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging optimized to protect battery‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Issue with charging accessory‎‏‎‎‏‎"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎Press Menu to unlock.‎‏‎‎‏‎"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎Network locked‎‏‎‎‏‎"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎No SIM‎‏‎‎‏‎"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎Add a SIM.‎‏‎‎‏‎"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‎The SIM is missing or not readable. Add a SIM.‎‏‎‎‏‎"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎Unusable SIM.‎‏‎‎‏‎"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎Your SIM has been permanently deactivated.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ Contact your wireless service provider for another SIM.‎‏‎‎‏‎"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎SIM is locked.‎‏‎‎‏‎"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎SIM is PUK-locked.‎‏‎‎‏‎"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎Unlocking SIM…‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index debbeb1..5b82c44 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga optimizada para proteger la batería"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema con el accesorio de carga"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Presiona Menú para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hay ninguna tarjeta SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Introduce una tarjeta SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Falta la tarjeta SIM o no se puede leer. Introduce una tarjeta SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Tarjeta SIM inutilizable."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Tu tarjeta SIM se desactivó permanentemente.\n Ponte en contacto con tu proveedor de servicios inalámbricos para obtener otra tarjeta SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La tarjeta SIM está bloqueada."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La tarjeta SIM está bloqueada con el código PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando tarjeta SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 0ea98a8..cf7f3d2 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga optimizada para proteger la batería"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema con el accesorio de carga"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pulsa el menú para desbloquear la pantalla."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hay ninguna SIM."</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Añade una SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Falta la SIM o no se puede leer. Añade una SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"No se puede usar la SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Tu SIM se ha desactivado de forma permanente.\n Para obtener otra SIM, ponte en contacto con tu proveedor de servicios inalámbricos."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La SIM está bloqueada."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La SIM está bloqueada con el código PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 722a022..6335ca8 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine on aku kaitsmiseks optimeeritud"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • probleem laadimistarvikuga"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Vajutage avamiseks menüüklahvi."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Võrk on lukus"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM-i pole"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lisage SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM puudub või pole loetav. Lisage SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-i ei saa kasutada."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Teie SIM on jäädavalt inaktiveeritud.\n Teise SIM-i saamiseks võtke ühendust oma traadita side teenusepakkujaga."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM on lukustatud."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM on PUK-koodiga lukustatud."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-i avamine …"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index d329369..b47c58a 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria ez kaltetzeko, kargatzeko modua optimizatu da"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Arazo bat dago kargatzeko osagarriarekin"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ez dago SIMik"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Gehitu SIM bat."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIMa falta da, edo ezin da irakurri. Gehitu SIM bat."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ezin da erabili SIMa."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Betiko desaktibatu da SIMa.\n Jarri operadorearekin harremanetan beste SIM bat eskuratzeko."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIMa blokeatuta dago."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIMa PUKaren bidez desblokeatu behar da."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMa desblokeatzen…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index ae3f04a..f274f5f 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آهسته‌آهسته شارژ می‌شود"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • برای محافظت از باتری، شارژ بهینه می‌شود"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • در شارژ وسیله جانبی مشکلی وجود دارد"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"برای باز کردن قفل روی «منو» فشار دهید."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"شبکه قفل شد"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"سیم‌کارتی وجود ندارد"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"سیم‌کارت اضافه کنید."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"سیم‌کارت موجود نیست یا قابل‌خواندن نیست. سیم‌کارت اضافه کنید."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"سیم‌کارت قابل‌استفاده نیست."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"سیم‌کارت شما برای همیشه غیرفعال شده است.\n برای دریافت سیم‌کارتی دیگر، با رساننده خدمات بی‌سیم خود تماس بگیرید."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"سیم‌کارت قفل است."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‏سیم‌کارت با کد PUK قفل شده است."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"درحال باز کردن قفل سیم‌کارت…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 050df99..dd9ce2e4 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lataus optimoitu akun suojaamiseksi"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ongelma laturin kanssa"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Poista lukitus painamalla Valikkoa."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Verkko lukittu"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ei SIM-korttia"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lisää SIM-kortti."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-korttia ei löydy tai ei voi lukea. Lisää SIM-kortti."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-korttia ei voi käyttää."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Sim-kortti on poistettu käytöstä pysyvästi.\n Ota yhteyttä langattoman palvelun tarjoajaan ja pyydä uusi SIM-kortti."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortti on lukittu."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortti on lukittu PUK-koodilla."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-kortin lukitusta avataan…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index fa1a191..742f56e 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge optimisée pour protéger la pile"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problème concernant l\'accessoire de recharge"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune carte SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ajouter une carte SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"La carte SIM est manquante ou illisible. Ajouter une carte SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"La carte SIM est inutilisable."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Votre carte SIM a été désactivée de manière permanente.\n Communiquez avec votre fournisseur de services sans fil pour obtenir une autre carte SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La carte SIM est verrouillée."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La carte SIM est verrouillée par clé PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Déverrouillage de la carte SIM en cours…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index d687a1d..92d24c4 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge optimisée pour protéger la batterie"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problème de recharge de l\'accessoire"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur \"Menu\" pour déverrouiller le clavier."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ajoutez une SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"La SIM est absente ou illisible. Ajoutez une SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilisable."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Votre SIM a été désactivée définitivement.\n Contactez votre opérateur de téléphonie mobile pour en obtenir une autre."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM verrouillée."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM verrouillée par clé PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Déblocage de la SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 68f22cb..4837de2 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga optimizada para protexer a batería"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema co accesorio de carga"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Non hai ningunha SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Engade unha SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"A SIM falta ou non se pode ler. Engade unha."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"A SIM non se pode usar."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"A SIM desactivouse permanentemente.\n Ponte en contacto co teu fornecedor de servizos sen fíos para conseguir outra."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"A SIM está bloqueada."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"A SIM está bloqueada mediante PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 99c9883..7f8c6d8 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • બૅટરીની સુરક્ષા કરવા માટે, ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ ઍક્સેસરીમાં સમસ્યા"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવર્ક લૉક થયું"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"કોઈ સિમ કાર્ડ નથી"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"સિમ કાર્ડ ઉમેરો."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"સિમ કાર્ડ ખૂટે છે અથવા વાંચી શકાય એવું નથી. સિમ કાર્ડ ઉમેરો."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ઉપયોગમાં ન લઈ શકાતું સિમ કાર્ડ."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"તમારું સિમ કાર્ડ કાયમ માટે નિષ્ક્રિય કરવામાં આવ્યું છે.\n બીજા સિમ કાર્ડ માટે તમારા વાયરલેસ સેવા પ્રદાતાનો સંપર્ક કરો."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"સિમ કાર્ડ લૉક કરેલું છે."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"સિમ કાર્ડ PUK-લૉક કરેલું છે."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"સિમ કાર્ડ અનલૉક કરી રહ્યાં છીએ…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 9d32f04..18d63ab 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • धीरे चार्ज हो रहा है"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बैटरी को नुकसान से बचाने के लिए, चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्जर ऐक्सेसरी से जुड़ी समस्या"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"लॉक खोलने के लिए मेन्यू दबाएं."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक किया हुआ है"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"कोई सिम नहीं है"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"कोई सिम जोड़ें."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"सिम मौजूद नहीं है या उसे ऐक्सेस नहीं किया जा सकता. कोई सिम जोड़ें."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"सिम को हमेशा के लिए बंद कर दिया गया है."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"आपका सिम हमेशा के लिए बंद कर दिया गया है.\n दूसरा सिम पाने के लिए, वायरलेस सेवा देने वाली कंपनी से संपर्क करें."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"सिम लॉक है."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"सिम में PUK लॉक लगा है."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"सिम अनलॉक हो रहा है…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index b4224bf..0206faf 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • sporo punjenje"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje se optimizira radi zaštite baterije"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem s priborom za punjenje"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Izbornik da biste otključali."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM nedostaje ili nije čitljiv. Dodajte SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM je neupotrebljiv."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Vaš je SIM trajno deaktiviran.\n Obratite se svom davatelju bežičnih usluga da biste dobili drugi SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM je zaključan."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM je zaključan PUK-om."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Otključavanje SIM-a…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index bc712c7..8575e10 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lassú töltés"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimalizált töltés az akkumulátor védelme érdekében"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Probléma van a töltőtartozékkal"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"A feloldáshoz nyomja meg a Menü gombot."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Hálózat zárolva"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nincs SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adjon hozzá egy SIM-et."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"A SIM hiányzik vagy nem olvasható. Adjon hozzá egy SIM-et."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nem használható SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM véglegesen deaktiválva.\n Forduljon a vezeték nélküli szolgáltatójához másik SIM beszerzése érdekében."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Zárolt SIM."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"A SIM le van zárva PUK-kóddal."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM zárolásának feloldása…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 4d7bbbe..a7c3aba 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Մարտկոցը պաշտպանելու համար լիցքավորումն օպտիմալացվել է"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորիչի հետ կապված խնդիր"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ապակողպելու համար սեղմեք Ընտրացանկը:"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM քարտ չկա"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ավելացրեք SIM քարտ։"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM քարտը բացակայում է կամ ընթեռնելի չէ։ Ավելացրեք SIM քարտ։"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Անվավեր SIM քարտ։"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Ձեր SIM քարտն ընդմիշտ ապակտիվացվել է։\n Նոր SIM քարտ ձեռք բերելու համար կապվեք ձեր բջջային օպերատորի հետ։"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM քարտը կողպված է։"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM քարտը կողպված է PUK կոդով։"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM քարտն ապակողպվում է…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index aa766e9..f9a840f 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dioptimalkan untuk melindungi baterai"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Masalah dengan aksesori pengisian daya"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Tidak ada SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Tambahkan SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM tidak ada atau tidak dapat dibaca. Tambahkan SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM tidak dapat digunakan."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM Anda telah dinonaktifkan secara permanen.\n Hubungi penyedia layanan nirkabel Anda untuk mendapatkan SIM lain."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM dikunci."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM dikunci PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Membuka kunci SIM …"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 99f1779..b7147c2 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hleðsla fínstillt til að vernda rafhlöðuna"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Vandamál með hleðslubúnað"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ýttu á valmyndarhnappinn til að taka úr lás."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ekkert SIM-kort"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Bæta við SIM-korti."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kort vantar eða er ekki læsilegt. Bæta við SIM-korti."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ónothæft SIM-kort."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-kortið þitt var gert varanlega óvirkt.\n Hafðu samband við símafyrirtækið þitt til að fá nýtt SIM-kort."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kort er læst."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kort er læst með PUK-númeri."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Opnar SIM-kort…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index cc0a164..9e1b187 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica ottimizzata per proteggere la batteria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema relativo all\'accessorio di ricarica"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Premi Menu per sbloccare."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nessuna SIM presente"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Aggiungi una SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM mancante o non leggibile. Aggiungi una SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilizzabile."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"La SIM è stata disattivata definitivamente.\n Contatta il tuo fornitore di servizi wireless per richiedere un\'altra SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La SIM è bloccata."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La SIM è bloccata tramite PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Sblocco della SIM in corso…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 00c717c..16316ce 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה איטית"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • הטעינה עברה אופטימיזציה כדי להגן על הסוללה"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • יש בעיה עם אביזר הטעינה"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"יש ללחוץ על \'תפריט\' כדי לבטל את הנעילה."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת נעולה"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"‏אין כרטיס SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"‏הוספת כרטיס SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"‏כרטיס ה-SIM חסר או שלא ניתן לקרוא אותו. הוספת כרטיס SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"‏לא ניתן להשתמש בכרטיס ה-SIM הזה."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"‏כרטיס ה-SIM שלך הושבת באופן סופי.\n עליך לפנות לספק השירות האלחוטי שלך לקבלת כרטיס SIM אחר."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"‏כרטיס ה-SIM נעול."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‏כרטיס ה-SIM נעול באמצעות PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"‏מתבצע ביטול נעילה של כרטיס ה-SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 1d59a63..6e8f423 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電中"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • バッテリーを保護するために、充電が最適化されています"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電用アクセサリに関する問題"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"メニューからロックを解除できます。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ネットワークがロックされました"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM がありません"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM を追加してください。"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM が見つからないか読み取れません。SIM を追加してください。"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM が使用できません。"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM が完全に無効になっています。\n ワイヤレス サービス プロバイダにお問い合わせのうえ、新しい SIM を入手してください。"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM がロックされています。"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM が PUK でロックされました。"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM ロックを解除しています…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 5bd6b2e..a31243d 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ნელა იტენება"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • დატენვა ოპტიმიზირებულია ბატარეის დასაცავად"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • დამტენი დამხმარე მოწყობილობის პრობლემა"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"განსაბლოკად დააჭირეთ მენიუს."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ქსელი ჩაკეტილია"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM არ არის"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM-ის დამატება."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM აკლია ან არ იკითხება. SIM-ის დამატება."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"გამოუყენებელი SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"თქვენი SIM სამუდამოდ გამორთულია.\n დაუკავშირდით თქვენს უკაბელო სერვისის პროვაიდერს სხვა SIM ბარათისთვის."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-ბარათი ჩაკეტილია."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM დაბლოკილია PUK-ით."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-ის განბლოკვა…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 83d270d..6a77783 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяны қорғау үшін зарядтау оңтайландырылды"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядтау құрылғысына қатысты мәселе туындады."</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ашу үшін \"Мәзір\" пернесін басыңыз."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM картасы жоқ."</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM картасын қосыңыз."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM картасы жоқ немесе оқылмай тұр. SIM картасын қосыңыз."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM картасын пайдалану мүмкін емес."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM картаңыз біржола өшірілді.\n Сымсыз байланыс провайдеріне хабарласып, басқа SIM картасын алыңыз."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM картасы құлыпталған."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM картасы PUK кодымен құлыпталды."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картасының құлпы ашылып жатыр…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 5306cb1..cda9520 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយឺត"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បានបង្កើនប្រសិទ្ធភាពនៃការសាក ដើម្បីការពារថ្ម"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បញ្ហាពាក់ព័ន្ធនឹងគ្រឿងសាកថ្ម"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ចុចម៉ឺនុយ ​ដើម្បី​ដោះ​សោ។"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញ​ជាប់​សោ"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"គ្មានស៊ីមទេ"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"បញ្ចូល​ស៊ីម។"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"បាត់ស៊ីម ឬមិនអាចអានស៊ីមបាន។ បញ្ចូល​ស៊ីម។"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ស៊ីមមិនអាចប្រើបាន។"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ស៊ីមរបស់អ្នកត្រូវបានបិទដំណើរការជាអចិន្ត្រៃយ៍។\n ទាក់ទងទៅក្រុមហ៊ុនផ្ដល់សេវាឥតខ្សែរបស់អ្នក ដើម្បីទទួលបានស៊ីមមួយទៀត។"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ស៊ីម​ត្រូវបាន​ចាក់សោ។"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ស៊ីមត្រូវបានចាក់សោដោយ PUK។"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"កំពុងដោះសោស៊ីម…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index d609a23..e24005a 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬ್ಯಾಟರಿಯನ್ನು ರಕ್ಷಿಸಲು ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜಿಂಗ್ ಪರಿಕರ ಕುರಿತು ಸಮಸ್ಯೆ ಇದೆ"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್‌ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM ಇಲ್ಲ"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM ಅನ್ನು ಸೇರಿಸಿ."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM ಕಾಣೆಯಾಗಿದೆ ಅಥವಾ ರೀಡ್ ಆಗುತ್ತಿಲ್ಲ. SIM ಅನ್ನು ಸೇರಿಸಿ."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ನಿಷ್ಪ್ರಯೋಜಕವಾಗಿದೆ."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ನಿಮ್ಮ SIM ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ.\n ಬೇರೊಂದು SIM ಗಾಗಿ ನಿಮ್ಮ ವೈರ್‌ಲೆಸ್ ಸೇವಾ ಪೂರೈಕೆದಾರರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM ಲಾಕ್ ಆಗಿದೆ."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM PUK ಲಾಕ್ ಆಗಿದೆ."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM ಅನ್ನು ಅನ್‌ಲಾಕ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 0e09fad..7378cc78 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 저속 충전 중"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 배터리 보호를 위해 충전 최적화됨"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 액세서리 문제"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"잠금 해제하려면 메뉴를 누르세요."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"네트워크 잠김"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM 없음"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM을 추가하세요."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM이 없거나 SIM을 읽을 수 없습니다. SIM을 추가하세요."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM을 사용할 수 없습니다."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM이 영구적으로 비활성화되었습니다.\n 다른 SIM을 사용하려면 무선 서비스 제공업체에 문의하시기 바랍니다."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM이 잠김 상태입니다."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM이 PUK 잠김 상태입니다."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM 잠금 해제 중…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 1e03c03..88f0b97 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жай кубатталууда"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяны коргоо үчүн кубаттоо процесси оптималдаштырылды"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубаттоочу шайманда көйгөй бар"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Кулпуну ачуу үчүн Менюну басыңыз."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Тармак кулпуланган"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM карта жок"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM карта кошуңуз."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM карта жок же окулбайт. SIM карта кошуңуз."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Жараксыз SIM карта."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM картаңыз биротоло өчүрүлдү.\n Башка SIM карта алуу үчүн зымсыз кызмат көрсөтүүчүгө кайрылыңыз."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM карта кулпуланган."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM карта PUK менен кулпуланган."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картанын кулпусу ачылууда…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 0059d7f..00a382a 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບຊ້າ"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ການສາກຖືກປັບໃຫ້ເໝາະສົມເພື່ອປົກປ້ອງແບັດເຕີຣີ"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ບັນຫາກັບອຸປະກອນເສີມໃນການສາກ"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ເຄືອຂ່າຍຖືກລັອກ"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ບໍ່ມີຊິມ"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ເພີ່ມຊິມ."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ບໍ່ມີຊິມ ຫຼື ອ່ານຊິມບໍ່ໄດ້. ເພີ່ມຊິມ."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ຊິມໃຊ້ບໍ່ໄດ້."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ຊິມຂອງທ່ານຖືກປິດໃຊ້ຢ່າງຖາວອນແລ້ວ.\n ຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການໂທລະສັບໄຮ້ສາຍຂອງທ່ານເພື່ອຂໍຊິມໃໝ່."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ຊິມຖືກລັອກຢູ່."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ຊິມຖືກລັອກດ້ວຍ PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ກຳລັງປົດລັອກຊິມ…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 01e2f88..31c4107 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lėtai įkraunama"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Įkrovimas optimizuotas siekiant apsaugoti akumuliatorių"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Su įkrovimo priedu susijusi problema"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Paspauskite meniu, jei norite atrakinti."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tinklas užrakintas"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nėra SIM kortelės"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Įdėkite SIM kortelę."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Trūksta SIM kortelės arba ji neskaitoma. Įdėkite SIM kortelę."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nenaudojama SIM kortelė."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Jūsų SIM kortelė visam laikui išjungta.\n Susisiekite su belaidžio ryšio paslaugos teikėju, kad gautumėte naują SIM kortelę."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM kortelė užrakinta."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM kortelė užrakinta PUK kodu."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Atrakinama SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 2133694..ecf2233 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lēnā uzlāde"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Uzlāde optimizēta, lai saudzētu akumulatoru"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problēma ar uzlādes ierīci"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lai atbloķētu, nospiediet izvēlnes ikonu."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tīkls ir bloķēts."</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nav SIM kartes"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Pievienojiet SIM karti."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Nav SIM kartes, vai arī to nevar nolasīt. Pievienojiet SIM karti."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM karte nav izmantojama."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Jūsu SIM karte ir neatgriezeniski deaktivizēta.\n Sazinieties ar savu bezvadu pakalpojumu sniedzēju, lai iegūtu citu SIM karti."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karte ir bloķēta."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karte ir bloķēta ar PUK kodu."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Notiek SIM kartes atbloķēšana…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 2771c7f..3f089b9 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Полнењето е оптимизирано за да се заштити батеријата"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблем со додатокот за полнење"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притиснете „Мени“ за отклучување."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заклучена"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Нема SIM-картичка"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Додајте SIM-картичка."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Нема SIM-картичка или не може да се прочита. Додајте SIM-картичка."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-картичката е неупотреблива."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Вашата SIM-картичка е трајно деактивирана.\n Контактирајте со давателот на услуги за безжична мрежа за друга SIM-картичка."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-картичката е заклучена."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-картичката е заклучена со PUK-код."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Се отклучува SIM-картичката…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index 02ee66f..be1ea89 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ബാറ്ററി പരിരക്ഷിക്കാൻ ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജിംഗ് ആക്സസറിയുമായി ബന്ധപ്പെട്ട പ്രശ്നം"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"നെറ്റ്‌വർക്ക് ലോക്കുചെയ്‌തു"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"സിം ഇല്ല"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"സിം ചേർക്കുക."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"സിം കാണുന്നില്ല അല്ലെങ്കിൽ റീഡ് ചെയ്യാനായില്ല. സിം ചേർക്കുക."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ഉപയോഗശൂന്യമായ സിം."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"നിങ്ങളുടെ സിം ശാശ്വതമായി നിഷ്ക്രിയമാക്കി.\n മറ്റൊരു സിമ്മിന് നിങ്ങളുടെ വയർലെസ് സേവന ദാതാവിനെ ബന്ധപ്പെടുക."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"സിം ലോക്ക് ചെയ്തു."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"സിം PUK ലോക്ക് ചെയ്തു."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"സിം അൺലോക്ക് ചെയ്യുന്നു…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 2b9f81e..54fdecd 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейг хамгаалахын тулд цэнэглэх явцыг оновчилсон"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэх хэрэгсэлд асуудал гарлаа"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Түгжээг тайлах бол цэсийг дарна уу."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM байхгүй"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM нэмнэ үү."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM дутуу эсвэл үүнийг унших боломжгүй байна. SIM нэмнэ үү."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ашиглах боломжгүй SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Таны SIM-г бүрмөсөн идэвхгүй болгосон байна.\n Өөр SIM авах бол утасгүй үйлчилгээ үзүүлэгчтэйгээ холбогдоно уу."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-г түгжсэн байна."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-г PUK-р түгжсэн байна."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-н түгжээг тайлж байна…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 7aa7bdd..eff4c7a 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बॅटरीचे संरक्षण करण्यासाठी चार्जिंग ऑप्टिमाइझ केले आहे"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्जिंगच्या ॲक्सेसरीसंबंधित समस्या"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलॉक करण्यासाठी मेनू प्रेस करा."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक केले"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"सिम नाही"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"सिम जोडा."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"सिम गहाळ झाले आहे किंवा ते रीड करू शकत नाही. सिम जोडा."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"वापरण्यायोग्य नसलेले सिम."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"तुमचे सिम कायमचे डीॲक्टिव्हेट केले गेले आहे.\n दुसऱ्या सिमसाठी तुमच्या वायरलेस सेवा पुरवठादाराशी संपर्क साधा."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"सिम लॉक केलेले आहे."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"सिम PUK लॉक केलेले आहे."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"सिम अनलॉक करत आहे…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index bdfa4a7..d9eb4ca 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan dioptimumkan untuk melindungi bateri"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Isu berkaitan aksesori pengecasan"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Tiada SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Tambah SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM tiada atau tidak boleh dibaca. Tambah SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM tidak boleh digunakan."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM anda telah dinyahaktifkan secara kekal.\n Hubungi penyedia perkhidmatan wayarles anda untuk mendapatkan SIM lain."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM dikunci."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM dikunci PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Membuka kunci SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 576250b..afbce26 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည်"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ဘက်ထရီကာကွယ်ရန် အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းပစ္စည်းတွင် ပြဿနာရှိသည်"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"မီနူးကို နှိပ်၍ လော့ခ်ဖွင့်ပါ။"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ကွန်ရက်ကို လော့ခ်ချထားသည်"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ဆင်းမ်ကတ် မရှိပါ"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ဆင်းမ်ကတ်ထည့်ပါ။"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ဆင်းမ်မရှိပါ (သို့) သုံး၍မရပါ။ ဆင်းမ်ကတ်ထည့်ပါ။"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ဆင်းမ်ကတ်ကို သုံး၍မရပါ။"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"သင်၏ဆင်းမ်ကတ်ကို အပြီးပိတ်လိုက်သည်။\n ဆင်းမ်ကတ်နောက်တစ်ခု ရယူရန် သင်၏ ကြိုးမဲ့ဝန်ဆောင်မှုပေးသူထံ ဆက်သွယ်ပါ။"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ဆင်းမ်ကတ်ကို လော့ခ်ချထားသည်။"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ဆင်းမ်ကတ်၏ ပင်နံပါတ်ပြန်ဖွင့်သည့် ကုဒ်ကို လော့ခ်ချထားသည်။"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ဆင်းမ်ကတ် ဖွင့်နေသည်…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 455d086..3098e87 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader sakte"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladingen er optimalisert for å beskytte batteriet"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem med ladetilbehøret"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Trykk på menyknappen for å låse opp."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nettverket er låst"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ingen SIM-kort"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Legg til et SIM-kort."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kortet mangler eller kan ikke leses. Legg til et SIM-kort."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-kortet kan ikke brukes."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-kortet er deaktivert permanent.\n Kontakt leverandøren av trådløstjenesten for å få et nytt SIM-kort."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortet er låst."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortet er låst med PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Låser opp SIM-kortet …"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index f0094a3..45b8819 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मन्द गतिमा चार्ज गरिँदै"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ब्याट्री जोगाउन चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गर्ने एक्सेसरीमा कुनै समस्या आयो"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लक भएको छ"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM कार्ड हालिएको छैन"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM कार्ड हाल्नुहोस्।"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM कार्ड हालिएको छैन वा रिड गर्न मिल्दैन। SIM कार्ड हाल्नुहोस्।"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"यो SIM कार्ड प्रयोग गर्न मिल्दैन।"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"तपाईंको SIM कार्ड सदाका लागि डिएक्टिभेट गरिएको छ।\n आफ्नो वायरलेस सेवा प्रदायकलाई सम्पर्क गरी अर्को SIM कार्ड प्राप्त गर्नुहोस्।"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM कार्ड लक गरिएको छ।"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM कार्ड PUK प्रयोग गरी लक गरिएको छ।"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM कार्ड अनलक गरिँदै छ…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 1ba4a81..af24d40 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Langzaam opladen"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen geoptimaliseerd om de batterij te beschermen"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Probleem met oplaadaccessoire"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk op Menu om te ontgrendelen."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk vergrendeld"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Geen simkaart"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Voeg een simkaart toe."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"De simkaart ontbreekt of kan niet worden gelezen. Voeg een simkaart toe."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Onbruikbare simkaart."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Je simkaart is permanent gedeactiveerd.\n Neem contact op met je mobiele serviceprovider voor een nieuwe simkaart."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Simkaart is vergrendeld."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Simkaart is vergrendeld met pukcode."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Simkaart ontgrendelen…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index b31c9c0..8cae987 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜିଂ ଆକସେସୋରୀ ସହ ସମସ୍ୟା"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ନେଟୱର୍କକୁ ଲକ୍‌ କରାଯାଇଛି"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"କୌଣସି SIM ନାହିଁ"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ଏକ SIM ଯୋଗ କରନ୍ତୁ।"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM ଉପଲବ୍ଧ ନାହିଁ କିମ୍ବା ପଢ଼ିପାରିବା ଯୋଗ୍ୟ ନୁହେଁ। ଏକ SIM ଯୋଗ କରନ୍ତୁ।"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ବ୍ୟବହାର ଅଯୋଗ୍ୟ ଥିବା SIM।"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ଆପଣଙ୍କ SIMକୁ ସ୍ଥାୟୀ ଭାବରେ ନିଷ୍କ୍ରିୟ କରାଯାଇଛି।\n ଅନ୍ୟ ଏକ SIM ପାଇଁ ଆପଣଙ୍କ ୱେୟାରଲେସ ସେବା ପ୍ରଦାନକାରୀଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIMକୁ ଲକ କରାଯାଇଛି।"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIMକୁ PUK-ଲକ କରାଯାଇଛି।"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMକୁ ଅନଲକ କରାଯାଉଛି…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 209b63f..18959c8 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਕਰਨ ਵਾਲੀ ਐਕਸੈਸਰੀ ਸੰਬੰਧੀ ਸਮੱਸਿਆ"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ  ਲਾਕ  ਕੀਤਾ ਗਿਆ"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ਕੋਈ ਸਿਮ ਨਹੀਂ ਹੈ"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ਸਿਮ ਸ਼ਾਮਲ ਕਰੋ।"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ਸਿਮ ਮੌਜੂਦ ਨਹੀਂ ਹੈ ਜਾਂ ਪੜ੍ਹਨਯੋਗ ਨਹੀਂ ਹੈ। ਸਿਮ ਸ਼ਾਮਲ ਕਰੋ।"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ਬੇਕਾਰ ਸਿਮ।"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ਤੁਹਾਡੇ ਸਿਮ ਨੂੰ ਪੱਕੇ ਤੌਰ \'ਤੇ ਅਕਿਰਿਆਸ਼ੀਲ ਕੀਤਾ ਗਿਆ ਹੈ।\n ਦੂਜੇ ਸਿਮ ਲਈ ਆਪਣੇ ਵਾਇਰਲੈੱਸ ਸੇਵਾ ਪ੍ਰਦਾਨਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ਸਿਮ ਲਾਕ ਹੈ।"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ਸਿਮ PUK-ਲਾਕ ਹੈ।"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ਸਿਮ ਨੂੰ ਅਣਲਾਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 7ec988e..bd00ba9 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie zoptymalizowane w celu ochrony baterii"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem z akcesoriami do ładowania"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Naciśnij Menu, aby odblokować."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Brak karty SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodaj kartę SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Brak karty SIM lub nie można jej odczytać. Dodaj kartę SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nie można użyć karty SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Karta SIM została trwale wyłączona.\n Skontaktuj się z dostawcą usług bezprzewodowych, aby uzyskać inną kartę SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Karta SIM jest zablokowana."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Karta SIM została zablokowana kodem PUK"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Odblokowuję kartę SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 78a8091..54e270f 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento otimizado para proteger a bateria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema com o acessório de carregamento"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem chip"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adicione um chip."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"O chip não foi inserido ou não pode ser lido. Adicione um chip."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Chip inutilizável."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Seu chip foi desativado permanentemente.\n Entre em contato com seu provedor de serviços sem fio para receber outro chip."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"O chip está bloqueado."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"O chip está bloqueado pela PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando chip…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 5549b36..2e37bde 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar lentamente…"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento otimizado para proteger a bateria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema com o acessório de carregamento"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prima Menu para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adicione um SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"O SIM está em falta ou não é legível. Adicione um SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilizável."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"O SIM foi desativado permanentemente.\n Contacte o seu fornecedor de serviços de rede sem fios para obter outro SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"O SIM está bloqueado."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"O SIM está bloqueado com o PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"A desbloquear SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 78a8091..54e270f 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento otimizado para proteger a bateria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema com o acessório de carregamento"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem chip"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adicione um chip."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"O chip não foi inserido ou não pode ser lido. Adicione um chip."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Chip inutilizável."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Seu chip foi desativado permanentemente.\n Entre em contato com seu provedor de serviços sem fio para receber outro chip."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"O chip está bloqueado."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"O chip está bloqueado pela PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando chip…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index df28b8d..ead09209 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Încărcarea este optimizată pentru a proteja bateria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problemă legată de accesoriul de încărcare"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Apasă pe Meniu pentru a debloca."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rețea blocată"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Niciun card SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adaugă un card SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Cardul SIM lipsește sau nu poate fi citit. Adaugă un card SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Cardul SIM nu se poate folosi."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Cardul tău SIM a fost dezactivat definitiv.\n Contactează furnizorul de servicii wireless pentru a obține un alt card SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Cardul SIM este blocat."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Cardul SIM este blocat prin cod PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Se deblochează cardul SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index 62249a1..595fba5 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"Идет медленная зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядка оптимизирована для защиты батареи"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблема с зарядным устройством"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Для разблокировки нажмите \"Меню\"."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сеть заблокирована"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM-карта отсутствует"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Добавьте SIM-карту."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-карта отсутствует или не распознана. Добавьте SIM-карту."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-карту невозможно использовать."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-карта была окончательно деактивирована.\n Чтобы получить новую, обратитесь к своему оператору мобильной связи."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-карта заблокирована."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-карта заблокирована с помощью PUK-кода."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Разблокировка SIM-карты…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 17ced75..b6a7422 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණ උපාංගයේ ගැටලුව"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු හැරීමට මෙනුව ඔබන්න."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM නැත"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM එකක් එක් කරන්න."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM අස්ථානගතයි හෝ කියවිය නොහැක. SIM එකක් එක් කරන්න."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"භාවිත කළ නොහැකි SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ඔබේ SIM ස්ථිරවම අක්‍රිය කර ඇත.\n වෙනත් SIM පතක් සඳහා ඔබේ රැහැන් රහිත සේවා සපයන්නා අමතන්න."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM අගුළු දමා ඇත."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM PUK-අගුළු දමා ඇත."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM අගුළු අරිමින්…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index ef08a6c..5e34a94 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa pomaly"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjanie je optimalizované, aby sa chránila batéria"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problém s nabíjacím príslušenstvom"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Odomknete stlačením tlačidla ponuky."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieť je zablokovaná"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Žiadna SIM karta"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Pridajte SIM kartu."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM karta chýba alebo sa nedá čítať. Pridajte SIM kartu."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nepoužiteľná SIM karta."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Vaša SIM karta bola natrvalo deaktivovaná.\n Požiadajte svojho poskytovateľa bezdrôtových služieb o ďalšiu SIM kartu."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karta je uzamknutá."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karta je uzamknutá kódom PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM karta sa odomyká…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index a42989c..3508f3b 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Polnjenje je optimizirano zaradi zaščite baterije"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • težava s pripomočkom za polnjenje"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Če želite odkleniti, pritisnite meni."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ni kartice SIM."</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte kartico SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Ni kartice SIM ali je ni mogoče prebrati. Dodajte kartico SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kartica SIM je neuporabna."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Vaša kartica SIM je bila trajno deaktivirana.\n Za drugo kartico SIM se obrnite na ponudnika brezžičnih storitev."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Kartica SIM je zaklenjena."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Kartica SIM je zaklenjena s kodo PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Odklepanje kartice SIM …"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index ce53b7e..8d71b0f 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet ngadalë"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Karikimi u optimizua për të mbrojtur baterinë"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem me aksesorin e karikimit"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Shtyp \"Meny\" për të shkyçur."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rrjeti është i kyçur"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nuk ka kartë SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Shto një kartë SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Karta SIM mungon ose është e palexueshme. Shto një kartë SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kartë SIM e papërdorshme."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Karta jote SIM është çaktivizuar përgjithmonë.\n Kontakto me ofruesin e shërbimit wireless për një tjetër kartë SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Karta SIM është e kyçur."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Karta SIM është e kyçur me PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Karta SIM po shkyçet…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 437018d..4093952 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуњење је оптимизовано да би се заштитила батерија"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблем са додатним прибором за пуњење"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притисните Мени да бисте откључали."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Нема SIM-а"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Додајте SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM недостаје или не може да се прочита. Додајте SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Неупотребљив SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM је трајно деактивиран.\n Обратите се добављачу услуге бежичне телефоније да бисте добили други SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM је закључан."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM је закључан PUK-ом."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Откључава се SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index b4b1996..5b01f39 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas långsamt"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddningen har optimerats för att skydda batteriet"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ett problem uppstod med att ladda tillbehöret"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lås upp genom att trycka på Meny."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nätverk låst"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Inget SIM-kort"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lägg till ett SIM-kort."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kort saknas eller går inte att läsa. Lägg till ett SIM-kort."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-kortet går inte att använda."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Ditt SIM-kort har inaktiverats permanent.\n Kontakta din operatör och be om ett nytt SIM-kort."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortet är låst."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortet har låsts med PUK-kod."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-kortet låses upp …"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 8ca9046..72f1fc3 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hali ya kuchaji imeboreshwa ili kulinda betri"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kifuasi cha kuchaji kina hitilafu"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Bonyeza Menyu ili kufungua."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mtandao umefungwa"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Hakuna SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Weka SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM haipo au haiwezi kusomwa. Weka SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM haiwezi kutumika."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM kadi yako imefungwa kabisa.\n wasiliana na mtoa huduma wako wa pasi waya ili upate SIM nyingine."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM imefungwa."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM imefungwa kwa PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Inafungua SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 7671194..20eb8ef 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதுவாகச் சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • பேட்டரியைப் பாதுகாக்க சார்ஜிங் மேம்படுத்தப்பட்டுள்ளது"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜரில் சிக்கல் உள்ளது"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"அன்லாக் செய்ய மெனுவை அழுத்தவும்."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"நெட்வொர்க் பூட்டப்பட்டது"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"சிம் இல்லை"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"சிம்மைச் சேருங்கள்."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"சிம் இல்லை அல்லது படிக்கக்கூடியதாக இல்லை. சிம்மைச் சேருங்கள்."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"பயன்படுத்த முடியாத சிம்."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"உங்கள் சிம் நிரந்தரமாக முடக்கப்பட்டுள்ளது.\n மற்றொரு சிம்மிற்கான உங்கள் வயர்லெஸ் சேவை வழங்குநரைத் தொடர்புகொள்ளுங்கள்."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"சிம் லாக் செய்யப்பட்டுள்ளது."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"சிம் PUK-லாக் செய்யப்பட்டுள்ளது."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"சிம்மை அன்லாக் செய்கிறது…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 623b589..d496944 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • బ్యాటరీని రక్షించడానికి ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జింగ్ యాక్సెసరీతో సమస్య ఉంది"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్‌లాక్ చేయడానికి మెనూను నొక్కండి."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్‌వర్క్ లాక్ చేయబడింది"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM లేదు"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIMను జోడించండి."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM మిస్ అయ్యింది లేదా ఆమోదయోగ్యం కాదు. SIMను జోడించండి."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"వినియోగించలేని SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"మీ SIM శాశ్వతంగా డీయాక్టివేట్ చేయబడింది.\n మరో SIMను పొందడం కోసం మీ వైర్‌లెస్ సర్వీస్ ప్రొవైడర్‌ను సంప్రదించండి."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM లాక్ చేయబడింది."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM PUK లాక్ చేయబడింది."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMను అన్‌లాక్ చేస్తోంది…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index c244107..605d077 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างช้าๆ"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ปรับการชาร์จให้เหมาะสมเพื่อถนอมแบตเตอรี่"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ปัญหาเกี่ยวกับอุปกรณ์เสริมสำหรับการชาร์จ"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"กด \"เมนู\" เพื่อปลดล็อก"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"เครือข่ายถูกล็อก"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ไม่มี SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"โปรดใส่ SIM"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ไม่มี SIM หรืออ่านไม่ได้ โปรดใส่ SIM"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ใช้งานไม่ได้"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ปิดใช้งาน SIM อย่างถาวรแล้ว\n ติดต่อผู้ให้บริการไร้สายของคุณเพื่อรับ SIM ใหม่"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM ถูกล็อก"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM ถูกล็อกด้วย PUK"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"กำลังปลดล็อก SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index cd8f810..040ec9e 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabagal na nagcha-charge"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Naka-optimize ang pag-charge para protektahan ang baterya"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Isyu sa pag-charge ng accessory"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pindutin ang Menu upang i-unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Naka-lock ang network"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Walang SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Magdagdag ng SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Wala o hindi nababasa ang SIM. Magdagdag ng SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Hindi magagamit na SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Permanenteng na-deactivate ang iyong SIM.\n Makipag-ugnayan sa iyong service provider ng wireless para sa isa pang SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Naka-lock ang SIM."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Naka-PUK lock ang SIM."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ina-unlock ang SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index ddeba67..750ba11 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş şarj oluyor"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj işlemi pili korumak üzere optimize edildi"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj aksesuarı ile ilgili sorun"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmak için Menü\'ye basın."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ağ kilitli"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM yok"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM ekleyin."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM yok veya okunamıyor. SIM ekleyin."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kullanılamayan SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM\'iniz kalıcı olarak devre dışı bırakıldı.\n Başka bir SIM için kablosuz servis sağlayıcınızla iletişime geçin."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM kilitli."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM\'in PUK kilidi devrede."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM\'in kilidi açılıyor…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index f06d17d..169ea1f 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання оптимізовано, щоб захистити акумулятор"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблема із зарядним пристроєм"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натисніть меню, щоб розблокувати."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Немає SIM-карти"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Додайте SIM-карту."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-карта відсутня або недоступна для читання. Додайте SIM-карту."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Непридатна SIM-карта."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-карту деактивовано назавжди.\n Щоб отримати іншу, зверніться до свого постачальника послуг бездротового зв’язку."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-карту заблоковано."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-карту заблоковано PUK-кодом."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Розблокування SIM-карти…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 8adbaca..d7f7b65 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آہستہ چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • بیٹری کی حفاظت کے لیے چارجنگ کو بہتر بنایا گیا"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارجنگ ایکسیسری کے ساتھ مسئلہ"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"غیر مقفل کرنے کیلئے مینیو دبائیں۔"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"نیٹ ورک مقفل ہو گیا"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"‏کوئی SIM نہیں ہے"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"‏ایک SIM شامل کریں۔"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"‏SIM غائب ہے یا پڑھنے لائق نہیں ہے۔ ایک SIM شامل کریں۔"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"‏ناقابل استعمال SIM۔"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"‏آپ کے SIM کو مستقل طور پر غیر فعال کر دیا گیا ہے۔\n کسی دوسرے SIM کیلئے اپنے وائرلیس سروس فراہم کنندہ سے رابطہ کریں۔"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"‏SIM مقفل ہے۔"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‏آپ کا SIM ‏PUK مقفل ہے۔"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"‏SIM کو غیر مقفل کیا جا رہا ہے…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 96dfa05..40dbaf3 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareyani himoyalash uchun quvvatlash optimallashtirildi"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvatlash aksessuari bilan muammo"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Qulfdan chiqarish uchun Menyu tugmasini bosing."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM kartasiz"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM karta qoʻshish."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM karta topilmadi yoki oʻqilmadi. SIM karta qoʻshish."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ishlamaydigan SIM."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM karta butunlay faolsizlantirildi.\n Boshqa SIM karta olish uchun simsiz aloqa operatoriga murojaat qiling."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karta qulflandi."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karta PUK kod bilan qulflangan."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM karta qulfdan chiqarilmoqda…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 41b5a33..d5a33d3 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quá trình sạc được tối ưu hoá để bảo vệ pin"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Có vấn đề với phụ kiện sạc"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn vào Menu để mở khóa."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Không có SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Hãy thêm SIM."</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Không tìm thấy hoặc không đọc được SIM. Hãy thêm SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM không sử dụng được."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM của bạn đã bị vô hiệu hoá vĩnh viễn.\n Hãy liên hệ với nhà cung cấp dịch vụ viễn thông không dây của bạn để yêu cầu cấp SIM khác."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM này đang bị khoá."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM này đang bị khoá PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Đang mở khoá SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index 4c65832..6de9ff9 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在慢速充电"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 为保护电池,充电方式已优化"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充电配件有问题"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按“菜单”即可解锁。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"网络已锁定"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"没有 SIM 卡"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"请插入 SIM 卡。"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM 卡缺失或无法读取。请插入 SIM 卡。"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM 卡无法使用。"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"您的 SIM 卡已被永久停用。\n请与您的无线服务提供商联系,以便重新获取一张 SIM 卡。"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM 卡已被锁定。"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM 卡已用 PUK 码锁定。"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"正在解锁 SIM 卡…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index dad6f31..11966ca 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 為保護電池,系統已優化充電"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電配件發生問題"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按下 [選單] 即可解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"沒有 SIM 卡"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"請新增 SIM 卡。"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"找不到 SIM 卡或 SIM 卡無法讀取,請新增 SIM 卡。"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM 卡無法使用。"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM 卡已永久停用。\n請向無線服務供應商索取其他 SIM 卡。"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM 卡已鎖定。"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM 卡已使用 PUK 鎖定。"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"正在解鎖 SIM 卡…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 88b7e43..e4f868a 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 為保護電池,充電效能已最佳化"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電配件有問題"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按選單鍵解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"沒有 SIM 卡"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"請新增 SIM 卡。"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"找不到 SIM 卡或 SIM 卡無法讀取,請新增 SIM 卡。"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM 卡無法使用。"</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM 卡已永久停用。\n 請向無線服務供應商索取其他 SIM 卡。"</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM 卡已鎖定。"</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM 卡已使用 PUK 碼鎖定。"</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"正在解鎖 SIM 卡…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index c5e99ab..4fadc2e 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -35,13 +35,9 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kancane"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ukushaja kuthuthukisiwe ukuze kuvikelwe ibhethri"</string>
     <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • • Inkinga ngesisekeli sokushaja"</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Chofoza Menyu ukuvula."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Inethiwekhi ivaliwe"</string>
     <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ayikho i-SIM"</string>
-    <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"engeza i-SIM"</string>
-    <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"I-SIM ayitholakali noma ayifundeki. engeza i-SIM"</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"I-SIM engasebenziseki."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"I-SIM yakho iyekiswe ukusebenza unomphela.\n Xhumana nomhlinzeki wakho wesevisi ngokungenazintambo ukuze uthole enye i-SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"I-SIM ikhiyiwe."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"I-SIM ikhiyiwe nge-PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ivula i-SIM…"</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 28b5870..565ed10 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -67,23 +67,13 @@
     <!-- When the lock screen is showing and the phone plugged in with incompatible charger. -->
     <string name="keyguard_plugged_in_incompatible_charger"><xliff:g id="percentage">%s</xliff:g> • Issue with charging accessory</string>
 
-    <!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock.  This is shown in small font at the bottom. -->
-    <string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string>
-
     <!-- SIM messages --><skip />
     <!-- When the user inserts a sim card from an unsupported network, it becomes network locked -->
     <string name="keyguard_network_locked_message">Network locked</string>
     <!-- Shown when there is no SIM. -->
     <string name="keyguard_missing_sim_message_short">No SIM</string>
-    <!-- Shown to ask the user to add a SIM. -->
-    <string name="keyguard_missing_sim_instructions">Add a SIM.</string>
-    <!-- Shown to ask the user to add a SIM when sim is missing or not readable. -->
-    <string name="keyguard_missing_sim_instructions_long">The SIM is missing or not readable. Add a SIM.</string>
     <!-- Shown when SIM is permanently disabled. -->
     <string name="keyguard_permanent_disabled_sim_message_short">Unusable SIM.</string>
-    <!-- Shown to inform the user to SIM is permanently deactivated. -->
-    <string name="keyguard_permanent_disabled_sim_instructions">Your SIM has been permanently deactivated.\n
-    Contact your wireless service provider for another SIM.</string>
     <!-- Shown to tell the user that their SIM is locked and they must unlock it. -->
     <string name="keyguard_sim_locked_message">SIM is locked.</string>
     <!-- When the user enters a wrong sim pin too many times, it becomes PUK locked (Pin Unlock Kode) -->
diff --git a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
index 015e9f9..d1b8a06 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
@@ -14,8 +14,9 @@
   ~ limitations under the License
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <item android:state_selected="true"
-          android:color="?android:attr/colorAccent" />
+          android:color="?androidprv:attr/materialColorOnSurfaceVariant" />
     <item android:color="@color/notification_guts_priority_button_bg_stroke_color" />
 </selector>
diff --git a/packages/SystemUI/res/color/notification_guts_priority_contents.xml b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
index 42f0189..cc8c25a 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_contents.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
@@ -14,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <item android:state_selected="true"
-          android:color="?android:attr/colorAccent" />
+          android:color="?androidprv:attr/materialColorOnSurfaceVariant" />
     <item android:color="@color/notification_guts_priority_button_content_color" />
 </selector>
diff --git a/packages/SystemUI/res/color/remote_input_hint.xml b/packages/SystemUI/res/color/remote_input_hint.xml
index 7fe58db..0d90ee6 100644
--- a/packages/SystemUI/res/color/remote_input_hint.xml
+++ b/packages/SystemUI/res/color/remote_input_hint.xml
@@ -14,6 +14,7 @@
   ~ limitations under the License.
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="?android:attr/textColorPrimary" android:alpha=".6" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="?androidprv:attr/materialColorOnSurfaceVariant" android:alpha=".6" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml
index 4dcd3dd..0acc66b 100644
--- a/packages/SystemUI/res/color/remote_input_send.xml
+++ b/packages/SystemUI/res/color/remote_input_send.xml
@@ -15,7 +15,8 @@
   ~ limitations under the License
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="?android:attr/colorAccent" android:alpha=".3" />
-    <item android:color="?android:attr/colorAccent" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_enabled="false" android:color="?androidprv:attr/materialColorPrimary" android:alpha=".3" />
+    <item android:color="?androidprv:attr/materialColorPrimary" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
index 13bb1d7..bf2c198 100644
--- a/packages/SystemUI/res/color/remote_input_text.xml
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -15,7 +15,8 @@
   ~ limitations under the License
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="?android:attr/textColorPrimary" android:alpha=".6" />
-    <item android:color="?android:attr/textColorPrimary" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_enabled="false" android:color="?androidprv:attr/materialColorOnSurfaceVariant" android:alpha=".6" />
+    <item android:color="?androidprv:attr/materialColorOnSurfaceVariant" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index e626675..b959737 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -26,7 +26,7 @@
                 <padding
                     android:left="20dp"
                     android:right="20dp" />
-                <solid android:color="?androidprv:attr/colorSurface" />
+                <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
             </shape>
         </inset>
     </item>
diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml
index bd9394b..84e2231 100644
--- a/packages/SystemUI/res/drawable/notification_guts_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml
@@ -17,7 +17,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
     <!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners -->
     <corners android:radius="1dp" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 3eaa618..355e75d 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -20,7 +20,7 @@
         android:color="?android:attr/colorControlHighlight">
     <item>
         <shape>
-            <solid android:color="?androidprv:attr/colorSurface" />
+            <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
         </shape>
     </item>
     <item>
diff --git a/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml b/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
index 535b354..45d1a53 100644
--- a/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
+++ b/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
@@ -14,12 +14,13 @@
   ~ limitations under the License.
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
 
-    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceDim" />
     <stroke
         android:width="@dimen/remote_input_view_text_stroke"
-        android:color="?android:attr/colorAccent"/>
+        android:color="?androidprv:attr/materialColorPrimary"/>
     <padding
         android:bottom="0dp"
         android:left="12dp"
diff --git a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
index 8b34080..06bed00 100644
--- a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
+++ b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
@@ -15,11 +15,12 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:width="40dp"
     android:height="40dp"
     android:viewportWidth="40"
     android:viewportHeight="40">
     <path
-        android:fillColor="@color/notification_section_clear_all_btn_color"
+        android:fillColor="?androidprv:attr/materialColorOnSurfaceVariant"
         android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7267L16.2734 24.6667L20 20.94L23.7267 24.6667L24.6667 23.7267L20.94 20L24.6667 16.2733Z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index bddcb6a..cf301c9 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -79,7 +79,4 @@
             android:tint="@color/accent_tint_color_selector"
             android:soundEffectsEnabled="false" />
     </LinearLayout>
-
-    <include layout="@layout/volume_dnd_icon"/>
-
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 3b70dc0..5ce2601 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -70,12 +70,6 @@
                     android:tint="?android:attr/textColorPrimary"
                     android:layout_gravity="center"
                     android:soundEffectsEnabled="false" />
-
-                <include layout="@layout/volume_dnd_icon"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginRight="@dimen/volume_dialog_stream_padding"
-                    android:layout_marginTop="6dp"/>
             </FrameLayout>
 
             <LinearLayout
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 6d77943..08eccbb 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -44,10 +44,9 @@
         android:paddingTop="10dp"
         android:maxLines="1"
         android:ellipsize="end"
-        app:layout_constraintWidth_percent="0.7"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
-        app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+        app:layout_constraintEnd_toStartOf="@+id/guideline"
         app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary"
         android:gravity="center_vertical"
         android:textSize="14sp" />
@@ -60,34 +59,35 @@
         android:paddingBottom="10dp"
         android:maxLines="1"
         android:ellipsize="end"
-        app:layout_constraintWidth_percent="0.7"
         app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
         app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
-        app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+        app:layout_constraintEnd_toStartOf="@+id/guideline"
         app:layout_constraintBottom_toBottomOf="parent"
         android:gravity="center_vertical" />
 
+    <androidx.constraintlayout.widget.Guideline
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/guideline"
+        app:layout_constraintGuide_percent="0.8"
+        android:orientation="vertical"/>
+
     <View
         android:id="@+id/gear_icon"
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
-        app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
-        app:layout_constraintEnd_toEndOf="@+id/gear_icon_image"
+        app:layout_constraintStart_toEndOf="@+id/guideline"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
 
     <ImageView
         android:id="@+id/gear_icon_image"
-        android:src="@drawable/ic_settings_24dp"
-        android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
         android:layout_width="0dp"
         android:layout_height="24dp"
-        app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        android:src="@drawable/ic_settings_24dp"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintWidth_percent="0.3"
-        android:gravity="center_vertical"
-        android:paddingStart="10dp" />
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 5d986e0..c11a18b 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -51,147 +51,160 @@
         android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/bluetooth_toggle_title"
+        app:layout_constraintBottom_toTopOf="@+id/scroll_view"
         app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_title" />
 
-    <TextView
-        android:id="@+id/bluetooth_toggle_title"
-        style="@style/BluetoothTileDialog.Device"
-        android:layout_width="0dp"
-        android:layout_height="64dp"
-        android:maxLines="1"
-        android:ellipsize="end"
-        android:gravity="center_vertical"
-        android:layout_marginTop="4dp"
-        android:text="@string/turn_on_bluetooth"
-        android:clickable="false"
-        android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
-        android:textSize="16sp"
-        app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" />
-
-    <Switch
-        android:id="@+id/bluetooth_toggle"
-        style="@style/BluetoothTileDialog.Device"
-        android:layout_width="0dp"
-        android:layout_height="48dp"
-        android:gravity="center|center_vertical"
-        android:paddingEnd="24dp"
-        android:layout_marginTop="10dp"
-        android:contentDescription="@string/turn_on_bluetooth"
-        android:switchMinWidth="@dimen/settingslib_switch_track_width"
-        android:theme="@style/MainSwitch.Settingslib"
-        android:thumb="@drawable/settingslib_thumb_selector"
-        android:track="@drawable/settingslib_track_selector"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title"
-        app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" />
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/device_list"
+    <androidx.core.widget.NestedScrollView
+        android:id="@+id/scroll_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="20dp"
-        android:nestedScrollingEnabled="false"
-        android:overScrollMode="never"
-        android:scrollbars="vertical"
+        app:layout_constrainedHeight="true"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle"
-        app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
-
-    <androidx.constraintlayout.widget.Group
-        android:id="@+id/see_all_layout_group"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        app:constraint_referenced_ids="ic_arrow,see_all_text" />
-
-    <ImageView
-        android:id="@+id/ic_arrow"
-        android:layout_marginStart="36dp"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:importantForAccessibility="no"
-        android:gravity="center_vertical"
-        android:src="@drawable/ic_arrow_forward"
-        app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/see_all_text"
-        app:layout_constraintTop_toBottomOf="@id/device_list" />
-
-    <TextView
-        android:id="@+id/see_all_text"
-        style="@style/BluetoothTileDialog.Device"
-        android:layout_width="0dp"
-        android:layout_height="64dp"
-        android:maxLines="1"
-        android:ellipsize="end"
-        android:gravity="center_vertical"
-        android:layout_marginStart="0dp"
-        android:paddingStart="20dp"
-        android:text="@string/see_all_bluetooth_devices"
-        android:textSize="14sp"
-        android:textAppearance="@style/TextAppearance.Dialog.Title"
-        app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
-        app:layout_constraintStart_toEndOf="@+id/ic_arrow"
-        app:layout_constraintTop_toBottomOf="@id/device_list"
-        app:layout_constraintEnd_toEndOf="parent" />
-
-    <androidx.constraintlayout.widget.Group
-        android:id="@+id/pair_new_device_layout_group"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        app:constraint_referenced_ids="ic_add,pair_new_device_text" />
-
-    <ImageView
-        android:id="@+id/ic_add"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_marginStart="36dp"
-        android:gravity="center_vertical"
-        android:importantForAccessibility="no"
-        android:src="@drawable/ic_add"
-        app:layout_constraintBottom_toTopOf="@id/done_button"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
-        app:layout_constraintTop_toBottomOf="@id/see_all_text"
-        android:tint="?android:attr/textColorPrimary" />
-
-    <TextView
-        android:id="@+id/pair_new_device_text"
-        style="@style/BluetoothTileDialog.Device"
-        android:layout_width="0dp"
-        android:layout_height="64dp"
-        android:maxLines="1"
-        android:ellipsize="end"
-        android:gravity="center_vertical"
-        android:layout_marginStart="0dp"
-        android:paddingStart="20dp"
-        android:text="@string/pair_new_bluetooth_devices"
-        android:textSize="14sp"
-        android:textAppearance="@style/TextAppearance.Dialog.Title"
-        app:layout_constraintBottom_toTopOf="@id/done_button"
-        app:layout_constraintStart_toEndOf="@+id/ic_add"
-        app:layout_constraintTop_toBottomOf="@id/see_all_text"
-        app:layout_constraintEnd_toEndOf="parent" />
-
-    <Button
-        android:id="@+id/done_button"
-        style="@style/Widget.Dialog.Button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/dialog_bottom_padding"
-        android:layout_marginEnd="@dimen/dialog_side_padding"
-        android:layout_marginStart="@dimen/dialog_side_padding"
-        android:clickable="true"
-        android:ellipsize="end"
-        android:focusable="true"
-        android:maxLines="1"
-        android:text="@string/inline_done_button"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/pair_new_device_text" />
+        app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/scroll_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" >
+
+            <TextView
+                android:id="@+id/bluetooth_toggle_title"
+                android:layout_width="0dp"
+                android:layout_height="64dp"
+                android:maxLines="1"
+                android:ellipsize="end"
+                android:gravity="start|center_vertical"
+                android:paddingEnd="0dp"
+                android:paddingStart="36dp"
+                android:text="@string/turn_on_bluetooth"
+                android:clickable="false"
+                android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+                android:textSize="16sp"
+                app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintBottom_toTopOf="@+id/device_list"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <Switch
+                android:id="@+id/bluetooth_toggle"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:gravity="start|center_vertical"
+                android:paddingEnd="40dp"
+                android:contentDescription="@string/turn_on_bluetooth"
+                android:switchMinWidth="@dimen/settingslib_switch_track_width"
+                android:theme="@style/MainSwitch.Settingslib"
+                android:thumb="@drawable/settingslib_thumb_selector"
+                android:track="@drawable/settingslib_track_selector"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title"
+                app:layout_constraintBottom_toTopOf="@+id/device_list"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/device_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle"
+                app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
+
+            <androidx.constraintlayout.widget.Group
+                android:id="@+id/see_all_layout_group"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                app:constraint_referenced_ids="ic_arrow,see_all_text" />
+
+            <ImageView
+                android:id="@+id/ic_arrow"
+                android:layout_marginStart="36dp"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:importantForAccessibility="no"
+                android:gravity="center_vertical"
+                android:src="@drawable/ic_arrow_forward"
+                app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toStartOf="@id/see_all_text"
+                app:layout_constraintTop_toBottomOf="@id/device_list" />
+
+            <TextView
+                android:id="@+id/see_all_text"
+                style="@style/BluetoothTileDialog.Device"
+                android:layout_width="0dp"
+                android:layout_height="64dp"
+                android:maxLines="1"
+                android:ellipsize="end"
+                android:gravity="center_vertical"
+                android:layout_marginStart="0dp"
+                android:paddingStart="20dp"
+                android:text="@string/see_all_bluetooth_devices"
+                android:textSize="14sp"
+                android:textAppearance="@style/TextAppearance.Dialog.Title"
+                app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
+                app:layout_constraintStart_toEndOf="@+id/ic_arrow"
+                app:layout_constraintTop_toBottomOf="@id/device_list"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+            <androidx.constraintlayout.widget.Group
+                android:id="@+id/pair_new_device_layout_group"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                app:constraint_referenced_ids="ic_add,pair_new_device_text" />
+
+            <ImageView
+                android:id="@+id/ic_add"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_marginStart="36dp"
+                android:gravity="center_vertical"
+                android:importantForAccessibility="no"
+                android:src="@drawable/ic_add"
+                app:layout_constraintBottom_toTopOf="@id/done_button"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
+                app:layout_constraintTop_toBottomOf="@id/see_all_text"
+                android:tint="?android:attr/textColorPrimary" />
+
+            <TextView
+                android:id="@+id/pair_new_device_text"
+                style="@style/BluetoothTileDialog.Device"
+                android:layout_width="0dp"
+                android:layout_height="64dp"
+                android:maxLines="1"
+                android:ellipsize="end"
+                android:gravity="center_vertical"
+                android:layout_marginStart="0dp"
+                android:paddingStart="20dp"
+                android:text="@string/pair_new_bluetooth_devices"
+                android:textSize="14sp"
+                android:textAppearance="@style/TextAppearance.Dialog.Title"
+                app:layout_constraintStart_toEndOf="@+id/ic_add"
+                app:layout_constraintTop_toBottomOf="@id/see_all_text"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+            <Button
+                android:id="@+id/done_button"
+                style="@style/Widget.Dialog.Button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/dialog_bottom_padding"
+                android:layout_marginEnd="@dimen/dialog_side_padding"
+                android:layout_marginStart="@dimen/dialog_side_padding"
+                android:clickable="true"
+                android:ellipsize="end"
+                android:focusable="true"
+                android:maxLines="1"
+                android:text="@string/inline_done_button"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/pair_new_device_text"
+                app:layout_constraintBottom_toBottomOf="parent" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </androidx.core.widget.NestedScrollView>
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 8241070..ad9a775 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -115,8 +115,7 @@
             android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
             android:src="@drawable/dream_overlay_assistant_attention_indicator"
             android:visibility="gone"
-            android:contentDescription=
-                "@string/dream_overlay_status_bar_assistant_attention_indicator" />
+            android:contentDescription="@string/assistant_attention_content_description" />
 
     </LinearLayout>
 </com.android.systemui.dreams.DreamOverlayStatusBarView>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index c70f8e2..68c8dd9 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -16,6 +16,7 @@
 
 <FrameLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         android:id="@+id/half_shelf_dialog"
         android:orientation="vertical"
         android:layout_width="wrap_content"
@@ -65,7 +66,7 @@
                     android:gravity="center_vertical|start"
                     android:ellipsize="end"
                     android:maxLines="2"
-                    android:textColor="?android:attr/textColorPrimary"
+                    android:textColor="?androidprv:attr/materialColorOnSurface"
                     android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
                     android:textSize="16sp"
                 />
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 190f999..9ef342c 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -16,6 +16,7 @@
 
 <com.android.systemui.statusbar.notification.row.ChannelRow
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/half_shelf_row"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -59,7 +60,7 @@
                 android:ellipsize="end"
                 android:maxLines="1"
                 android:fontFamily="@*android:string/config_headlineFontFamily"
-                android:textColor="?android:attr/textColorPrimary"
+                android:textColor="?androidprv:attr/materialColorOnSurface"
                 android:textSize="16sp"
             />
 
@@ -74,7 +75,7 @@
                 android:maxLines="1"
                 android:layout_below="@id/channel_name"
                 android:fontFamily="@*android:string/config_bodyFontFamily"
-                android:textColor="?android:attr/textColorSecondary"
+                android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
                 android:textSize="14sp"
             />
         </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index eb74306..13e24a9 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -17,7 +17,8 @@
 
 <com.android.systemui.statusbar.AlphaOptimizedView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/notification_more_divider"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_divider_height"
-    android:background="@color/notification_divider_color" />
+    android:background="?androidprv:attr/materialColorOnSurfaceVariant" />
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 4f6e88c..3a752c8 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -17,6 +17,7 @@
 
 <com.android.systemui.statusbar.notification.row.NotificationConversationInfo
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/notification_guts"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -173,7 +174,7 @@
             android:contentDescription="@string/notification_more_settings"
             android:background="@drawable/ripple_drawable_20dp"
             android:src="@drawable/ic_settings"
-            android:tint="?android:attr/colorAccent"
+            android:tint="?androidprv:attr/materialColorPrimary"
             android:layout_alignParentEnd="true" />
 
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 852db1b..19a3f2f 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -17,6 +17,7 @@
 
 <com.android.systemui.statusbar.notification.row.NotificationInfo
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/notification_guts"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -102,7 +103,7 @@
             android:contentDescription="@string/notification_app_settings"
             android:src="@drawable/ic_info"
             android:layout_toStartOf="@id/info"
-            android:tint="@color/notification_guts_link_icon_tint"/>
+            android:tint="?androidprv:attr/materialColorPrimary"/>
         <ImageButton
             android:id="@+id/info"
             android:layout_width="@dimen/notification_importance_toggle_size"
@@ -111,7 +112,7 @@
             android:contentDescription="@string/notification_more_settings"
             android:background="@drawable/ripple_drawable_20dp"
             android:src="@drawable/ic_settings"
-            android:tint="?android:attr/colorAccent"
+            android:tint="?androidprv:attr/materialColorPrimary"
             android:layout_alignParentEnd="true" />
 
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index 8b53680..6e541a7 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -23,7 +23,7 @@
     android:orientation="vertical"
     android:paddingTop="2dp"
     android:paddingBottom="2dp"
-    android:background="?androidprv:attr/colorSurface"
+    android:background="?androidprv:attr/materialColorSurfaceContainerHigh"
     android:theme="@style/Theme.SystemUI">
 
     <RelativeLayout
@@ -38,7 +38,7 @@
             android:layout_alignParentStart="true"
             android:layout_centerVertical="true"
             android:paddingStart="@*android:dimen/notification_content_margin_end"
-            android:textColor="?android:attr/textColorPrimary"
+            android:textColor="?androidprv:attr/materialColorOnSurface"
             android:paddingEnd="4dp"/>
 
         <ImageView
diff --git a/packages/SystemUI/res/layout/notification_snooze_option.xml b/packages/SystemUI/res/layout/notification_snooze_option.xml
index d42cc02..fa6f965 100644
--- a/packages/SystemUI/res/layout/notification_snooze_option.xml
+++ b/packages/SystemUI/res/layout/notification_snooze_option.xml
@@ -16,10 +16,11 @@
 -->
 <TextView
         xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         android:layout_width="match_parent"
         android:layout_height="@*android:dimen/notification_headerless_min_height"
         android:layout_marginStart="@*android:dimen/notification_content_margin_end"
         android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
         android:gravity="center_vertical"
         android:textSize="14sp"
-        android:textColor="?android:attr/textColorSecondary"/>
\ No newline at end of file
+        android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index c1bac31..72424a13 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -41,7 +41,7 @@
             android:layout_height="wrap_content"
             >
             <com.android.systemui.statusbar.notification.row.FooterViewButton
-                style="@style/TextAppearance.NotificationSectionHeaderButton"
+                style="@style/TextAppearance.NotificationFooterButton"
                 android:id="@+id/manage_text"
                 android:layout_width="wrap_content"
                 android:layout_height="48dp"
@@ -54,12 +54,11 @@
                 app:layout_constraintEnd_toStartOf="@id/dismiss_text"
                 android:background="@drawable/notif_footer_btn_background"
                 android:focusable="true"
-                android:textColor="@color/notif_pill_text"
                 android:contentDescription="@string/manage_notifications_history_text"
                 android:text="@string/manage_notifications_history_text"
                 />
             <com.android.systemui.statusbar.notification.row.FooterViewButton
-                style="@style/TextAppearance.NotificationSectionHeaderButton"
+                style="@style/TextAppearance.NotificationFooterButton"
                 android:id="@+id/dismiss_text"
                 android:layout_width="wrap_content"
                 android:layout_height="48dp"
@@ -70,7 +69,6 @@
                 app:layout_constraintStart_toEndOf="@id/manage_text"
                 android:background="@drawable/notif_footer_btn_background"
                 android:focusable="true"
-                android:textColor="@color/notif_pill_text"
                 android:contentDescription="@string/accessibility_clear_all"
                 android:text="@string/clear_all_notifications_text"
                 />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index c4d8d55..53abe87 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -40,7 +40,7 @@
             android:layout_weight="1">
 
             <TextView
-                style="@style/TextAppearance.NotificationSectionHeaderButton"
+                style="@style/TextAppearance.NotificationSectionHeaderLabel"
                 android:id="@+id/header_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 6a192d4..39a1f1f 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -69,12 +69,6 @@
                     android:tint="?android:attr/textColorPrimary"
                     android:layout_gravity="center"
                     android:soundEffectsEnabled="false" />
-
-                <include layout="@layout/volume_dnd_icon"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content"
-                         android:layout_marginRight="@dimen/volume_dialog_stream_padding"
-                         android:layout_marginTop="6dp"/>
             </FrameLayout>
 
             <LinearLayout
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index c9256ae..f35de05 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -62,7 +62,6 @@
                 android:background="@null"
                 android:layoutDirection="ltr"
                 android:rotation="270" />
-            <include layout="@layout/volume_dnd_icon"/>
         </FrameLayout>
 
         <com.android.keyguard.AlphaOptimizedImageButton
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 99311e3..d9385c7 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -33,7 +33,7 @@
     <!-- The color of the text inside a notification -->
     <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
 
-    <color name="notif_pill_text">@color/material_dynamic_neutral95</color>
+    <color name="notif_pill_text">@android:color/system_on_surface_dark</color>
 
     <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
     <color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e942258..0620355 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -115,7 +115,7 @@
     <!-- Chosen so fill over background matches single tone -->
     <color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
 
-    <color name="notif_pill_text">@color/material_dynamic_neutral10</color>
+    <color name="notif_pill_text">@android:color/system_on_surface_light</color>
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 05f4334..74d435d 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -248,4 +248,14 @@
 
     <!-- Tag set on the Compose implementation of the QS footer actions. -->
     <item type="id" name="tag_compose_qs_footer_actions" />
+
+    <!--
+    Ids for the device entry icon.
+        device_entry_icon_view: parent view of both device_entry_icon and device_entry_icon_bg
+        device_entry_icon_fg: fp/lock/unlock icon
+        device_entry_icon_bg: background protection behind the icon
+    -->
+    <item type="id" name="device_entry_icon_view" />
+    <item type="id" name="device_entry_icon_fg" />
+    <item type="id" name="device_entry_icon_bg" />
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9e2ebf6..4c520444 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -466,7 +466,7 @@
     <string name="accessibility_bluetooth_device_icon">Bluetooth device icon</string>
 
     <!-- Content description of the bluetooth device settings gear icon. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_bluetooth_device_settings_gear">Bluetooth device settings gear</string>
+    <string name="accessibility_bluetooth_device_settings_gear">Click to configure device detail</string>
 
     <!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_battery_unknown">Battery percentage unknown.</string>
@@ -629,7 +629,7 @@
     <!-- QuickSettings: Bluetooth detail panel, text when there are no items [CHAR LIMIT=NONE] -->
     <string name="quick_settings_bluetooth_detail_empty_text">No paired devices available</string>
     <!-- QuickSettings: Bluetooth dialog subtitle [CHAR LIMIT=NONE]-->
-    <string name="quick_settings_bluetooth_tile_subtitle">Tap a device to connect</string>
+    <string name="quick_settings_bluetooth_tile_subtitle">Tap to connect or disconnect a device </string>
     <!-- QuickSettings: Bluetooth dialog pair new devices [CHAR LIMIT=NONE]-->
     <string name="pair_new_bluetooth_devices">Pair new device</string>
     <!-- QuickSettings: Bluetooth dialog see all devices [CHAR LIMIT=NONE]-->
@@ -3022,8 +3022,6 @@
     <string name="dream_overlay_status_bar_mic_off">Mic is off</string>
     <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
     <string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
-    <!-- Content description for the assistant attention indicator [CHAR LIMIT=NONE] -->
-    <string name="dream_overlay_status_bar_assistant_attention_indicator">Assistant is listening</string>
     <!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
     <string name="dream_overlay_status_bar_notification_indicator">{count, plural,
     =1 {# notification}
@@ -3209,7 +3207,7 @@
     <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
 
     <!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
-    <string name="assistant_attention_content_description">Assistant attention on</string>
+    <string name="assistant_attention_content_description">User presence is detected</string>
 
     <!--- Content of toast triggered when the notes app entry point is triggered without setting a default notes app. [CHAR LIMIT=NONE] -->
     <string name="set_default_notes_app_toast_content">Set default notes app in Settings</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 084cb88..7ce530f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -597,34 +597,34 @@
     <style name="TextAppearance.NotificationImportanceChannel">
         <item name="android:textSize">@dimen/notification_importance_channel_text</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
-        <item name="android:textColor">@color/notification_guts_header_text_color</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
         <item name="android:textSize">@dimen/notification_importance_channel_text</item>
     </style>
 
     <style name="TextAppearance.NotificationImportanceChannelGroup">
         <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">@color/notification_guts_header_text_color</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
         <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
     </style>
 
     <style name="TextAppearance.NotificationImportanceApp">
         <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">@color/notification_guts_sub_text_color</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
         <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
     </style>
 
     <style name="TextAppearance.NotificationImportanceHeader">
         <item name="android:textSize">@dimen/notification_importance_header_text</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">@color/notification_guts_header_text_color</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
     </style>
 
     <style name="TextAppearance.NotificationImportanceDetail">
         <item name="android:textSize">@dimen/notification_importance_description_text</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
-        <item name="android:textColor">@color/notification_guts_sub_text_color</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
         <item name="android:gravity">center</item>
     </style>
 
@@ -636,9 +636,27 @@
     </style>
 
     <style
+        name="TextAppearance.NotificationSectionHeaderLabel"
+        parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:minWidth">0dp</item>
+    </style>
+
+    <style
         name="TextAppearance.NotificationSectionHeaderButton"
         parent="@android:style/Widget.DeviceDefault.Button.Borderless">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:minWidth">0dp</item>
+    </style>
+
+    <style
+        name="TextAppearance.NotificationFooterButton"
+        parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
         <item name="android:textAllCaps">false</item>
         <item name="android:textSize">14sp</item>
         <item name="android:minWidth">0dp</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index b186018..3757274 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -318,9 +318,8 @@
 
         keyguardUpdateMonitor?.let {
             val anyFaceEnrolled = it.isFaceEnrolled
-            val anyFingerprintEnrolled =
-                    it.getCachedIsUnlockWithFingerprintPossible(
-                            selectedUserInteractor.getSelectedUserId())
+            val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible(
+                    selectedUserInteractor.getSelectedUserId())
             val udfpsEnrolled = it.isUdfpsEnrolled
 
             if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
@@ -374,9 +373,8 @@
             pw.println("   shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
                     "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
             pw.println("   faceEnrolled=${it.isFaceEnrolled}")
-            pw.println("   fpEnrolled=${
-                    it.getCachedIsUnlockWithFingerprintPossible(
-                            selectedUserInteractor.getSelectedUserId())}")
+            pw.println("   fpUnlockPossible=${
+                it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}")
             pw.println("   udfpsEnrolled=${it.isUdfpsEnrolled}")
         } ?: pw.println("   keyguardUpdateMonitor is uninitialized")
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index 873c3d9..1cfa816 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -39,10 +39,10 @@
 
 import com.android.keyguard.logging.CarrierTextManagerLogger;
 import com.android.settingslib.WirelessUtils;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository;
 import com.android.systemui.telephony.TelephonyListenerManager;
 
@@ -612,36 +612,6 @@
         return list;
     }
 
-    private CharSequence getCarrierHelpTextForSimState(int simState,
-            String plmn, String spn) {
-        int carrierHelpTextId = 0;
-        CarrierTextManager.StatusMode status = getStatusForIccState(simState);
-        switch (status) {
-            case NetworkLocked:
-                carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
-                break;
-
-            case SimMissing:
-                carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
-                break;
-
-            case SimPermDisabled:
-                carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
-                break;
-
-            case SimMissingLocked:
-                carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
-                break;
-
-            case Normal:
-            case SimLocked:
-            case SimPukLocked:
-                break;
-        }
-
-        return mContext.getText(carrierHelpTextId);
-    }
-
     /** Injectable Buildeer for {@#link CarrierTextManager}. */
     public static class Builder {
         private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 3c8301f..7bf3e8f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -38,10 +38,10 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
+import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -50,14 +50,22 @@
 import com.android.systemui.log.dagger.KeyguardClockLog;
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.shared.regionsampling.RegionSampler;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.settings.SecureSettings;
@@ -68,6 +76,8 @@
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.DisposableHandle;
+
 /**
  * Injectable controller for {@link KeyguardClockSwitch}.
  */
@@ -84,6 +94,12 @@
     private final DumpManager mDumpManager;
     private final ClockEventController mClockEventController;
     private final LogBuffer mLogBuffer;
+    private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
+    private final ConfigurationState mConfigurationState;
+    private final ConfigurationController mConfigurationController;
+    private final DozeParameters mDozeParameters;
+    private final ScreenOffAnimationController mScreenOffAnimationController;
+    private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
     private FrameLayout mSmallClockFrame; // top aligned clock
     private FrameLayout mLargeClockFrame; // centered clock
 
@@ -107,10 +123,13 @@
     private boolean mShownOnSecondaryDisplay = false;
     private boolean mOnlyClock = false;
     private boolean mIsActiveDreamLockscreenHosted = false;
-    private FeatureFlags mFeatureFlags;
+    private final FeatureFlagsClassic mFeatureFlags;
     private KeyguardInteractor mKeyguardInteractor;
     private final DelayableExecutor mUiExecutor;
     private boolean mCanShowDoubleLineClock = true;
+    private DisposableHandle mAodIconsBindJob;
+    @Nullable private NotificationIconContainer mAodIconContainer;
+
     @VisibleForTesting
     final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
             (Boolean isLockscreenHosted) -> {
@@ -151,26 +170,38 @@
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
             LockscreenSmartspaceController smartspaceController,
+            ConfigurationController configurationController,
+            ScreenOffAnimationController screenOffAnimationController,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             SecureSettings secureSettings,
             @Main DelayableExecutor uiExecutor,
             DumpManager dumpManager,
             ClockEventController clockEventController,
             @KeyguardClockLog LogBuffer logBuffer,
+            NotificationIconContainerAlwaysOnDisplayViewModel aodIconsViewModel,
+            ConfigurationState configurationState,
+            DozeParameters dozeParameters,
+            AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
             KeyguardInteractor keyguardInteractor,
-            FeatureFlags featureFlags) {
+            FeatureFlagsClassic featureFlags) {
         super(keyguardClockSwitch);
         mStatusBarStateController = statusBarStateController;
         mClockRegistry = clockRegistry;
         mKeyguardSliceViewController = keyguardSliceViewController;
         mNotificationIconAreaController = notificationIconAreaController;
         mSmartspaceController = smartspaceController;
+        mConfigurationController = configurationController;
+        mScreenOffAnimationController = screenOffAnimationController;
         mSecureSettings = secureSettings;
         mUiExecutor = uiExecutor;
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
         mDumpManager = dumpManager;
         mClockEventController = clockEventController;
         mLogBuffer = logBuffer;
+        mAodIconsViewModel = aodIconsViewModel;
+        mConfigurationState = configurationState;
+        mDozeParameters = dozeParameters;
+        mAodIconViewStore = aodIconViewStore;
         mView.setLogBuffer(mLogBuffer);
         mFeatureFlags = featureFlags;
         mKeyguardInteractor = keyguardInteractor;
@@ -316,6 +347,8 @@
     int getNotificationIconAreaHeight() {
         if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
             return 0;
+        } else if (NotificationIconContainerRefactor.isEnabled()) {
+            return mAodIconContainer != null ? mAodIconContainer.getHeight() : 0;
         } else {
             return mNotificationIconAreaController.getHeight();
         }
@@ -533,7 +566,27 @@
             NotificationIconContainer nic = (NotificationIconContainer)
                     mView.findViewById(
                             com.android.systemui.res.R.id.left_aligned_notification_icon_container);
-            mNotificationIconAreaController.setupAodIcons(nic);
+            if (NotificationIconContainerRefactor.isEnabled()) {
+                if (mAodIconsBindJob != null) {
+                    mAodIconsBindJob.dispose();
+                }
+                if (nic != null) {
+                    nic.setOnLockScreen(true);
+                    mAodIconsBindJob = NotificationIconContainerViewBinder.bind(
+                        nic,
+                        mAodIconsViewModel,
+                        mConfigurationState,
+                        mConfigurationController,
+                        mDozeParameters,
+                        mFeatureFlags,
+                        mScreenOffAnimationController,
+                        mAodIconViewStore
+                    );
+                    mAodIconContainer = nic;
+                }
+            } else {
+                mNotificationIconAreaController.setupAodIcons(nic);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 2e21255..3d8aaaf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -65,15 +65,23 @@
     private boolean mPaused;
 
     private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> {
-        // Check if this was the result of hitting the enter key
+        // Check if this was the result of hitting the IME done action
         final boolean isSoftImeEvent = event == null
                 && (actionId == EditorInfo.IME_NULL
                 || actionId == EditorInfo.IME_ACTION_DONE
                 || actionId == EditorInfo.IME_ACTION_NEXT);
-        final boolean isKeyboardEnterKey = event != null
-                && KeyEvent.isConfirmKey(event.getKeyCode())
-                && event.getAction() == KeyEvent.ACTION_DOWN;
-        if (isSoftImeEvent || isKeyboardEnterKey) {
+        if (isSoftImeEvent) {
+            verifyPasswordAndUnlock();
+            return true;
+        }
+        return false;
+    };
+
+    private final View.OnKeyListener mKeyListener = (v, keyCode, keyEvent) -> {
+        final boolean isKeyboardEnterKey = keyEvent != null
+                && KeyEvent.isConfirmKey(keyCode)
+                && keyEvent.getAction() == KeyEvent.ACTION_UP;
+        if (isKeyboardEnterKey) {
             verifyPasswordAndUnlock();
             return true;
         }
@@ -140,15 +148,16 @@
                 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
 
         mView.onDevicePostureChanged(mPostureController.getDevicePosture());
+
         mPostureController.addCallback(mPostureCallback);
 
         // Set selected property on so the view can send accessibility events.
         mPasswordEntry.setSelected(true);
         mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
+        mPasswordEntry.setOnKeyListener(mKeyListener);
         mPasswordEntry.addTextChangedListener(mTextWatcher);
         // Poke the wakelock any time the text is selected or modified
         mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
-
         mSwitchImeButton.setOnClickListener(v -> {
             mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
             // Do not show auxiliary subtypes in password lock screen.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 681aa70..175544d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -89,10 +89,7 @@
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (KeyEvent.isConfirmKey(keyCode)) {
-            mOkButton.performClick();
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_DEL) {
+        if (keyCode == KeyEvent.KEYCODE_DEL) {
             mDeleteButton.performClick();
             return true;
         }
@@ -110,6 +107,15 @@
     }
 
     @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            mOkButton.performClick();
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
     protected int getPromptReasonStringRes(int reason) {
         switch (reason) {
             case PROMPT_REASON_RESTART:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index b7d1171..376933d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -41,6 +41,9 @@
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
             return mView.onKeyDown(keyCode, event);
         }
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            return mView.onKeyUp(keyCode, event);
+        }
         return false;
     };
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 7101ed5..1b6112f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -755,7 +755,7 @@
         }
         mView.onResume(
                 mSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId()),
-                mKeyguardStateController.isFaceAuthEnabled());
+                mKeyguardStateController.isFaceEnrolled());
     }
 
     /** Sets an initial message that would override the default message */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 13f9d3e..05fb5fa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -246,7 +246,7 @@
 
     private boolean checkPuk() {
         // make sure the puk is at least 8 digits long.
-        if (mPasswordEntry.getText().length() == 8) {
+        if (mPasswordEntry.getText().length() >= 8) {
             mPukText = mPasswordEntry.getText();
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 7d6240b..f19a9ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -441,7 +441,6 @@
     private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
     private boolean mIsDreaming;
     private boolean mLogoutEnabled;
-    private boolean mIsFaceEnrolled;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private int mPostureState = DEVICE_POSTURE_UNKNOWN;
     private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
@@ -2083,7 +2082,6 @@
     private boolean mFingerprintLockedOut;
     private boolean mFingerprintLockedOutPermanent;
     private boolean mFaceLockedOutPermanent;
-    private final HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>();
 
     /**
      * When we receive a {@link android.content.Intent#ACTION_SIM_STATE_CHANGED} broadcast,
@@ -2701,16 +2699,6 @@
         }
     }
 
-    private void updateFaceEnrolled(int userId) {
-        final Boolean isFaceEnrolled = isFaceSupported()
-                && mBiometricEnabledForUser.get(userId)
-                && mAuthController.isFaceAuthEnrolled(userId);
-        if (mIsFaceEnrolled != isFaceEnrolled) {
-            mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
-        }
-        mIsFaceEnrolled = isFaceEnrolled;
-    }
-
     private boolean isFaceSupported() {
         return mFaceManager != null && !mFaceSensorProperties.isEmpty();
     }
@@ -2750,10 +2738,17 @@
     }
 
     /**
+     * @return true if there's at least one face enrolled for the given user.
+     */
+    public boolean isFaceEnrolled(int userId) {
+        return mAuthController.isFaceAuthEnrolled(userId);
+    }
+
+    /**
      * @return true if there's at least one face enrolled
      */
     public boolean isFaceEnrolled() {
-        return mIsFaceEnrolled;
+        return isFaceEnrolled(mSelectedUserInteractor.getSelectedUserId());
     }
 
     private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
@@ -3442,49 +3437,22 @@
     }
 
     @SuppressLint("MissingPermission")
-    @VisibleForTesting
-    boolean isUnlockWithFingerprintPossible(int userId) {
-        // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
-        boolean newFpEnrolled = isFingerprintSupported()
-                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
-        Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
-        if (oldFpEnrolled != newFpEnrolled) {
-            mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled);
-        }
-        mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled);
-        return mIsUnlockWithFingerprintPossible.get(userId);
-    }
-
-    /**
-     * Cached value for whether fingerprint is enrolled and possible to use for authentication.
-     * Note: checking fingerprint enrollment directly with the AuthController requires an IPC.
-     */
-    public boolean getCachedIsUnlockWithFingerprintPossible(int userId) {
-        return mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
+    public boolean isUnlockWithFingerprintPossible(int userId) {
+        return isFingerprintSupported()
+                && !isFingerprintDisabled(userId) && mAuthController.isFingerprintEnrolled(userId);
     }
 
     /**
      * @deprecated This is being migrated to use modern architecture.
      */
+    @VisibleForTesting
     @Deprecated
-    private boolean isUnlockWithFacePossible(int userId) {
+    public boolean isUnlockWithFacePossible(int userId) {
         if (isFaceAuthInteractorEnabled()) {
             return getFaceAuthInteractor() != null
                     && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled();
         }
-        return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
-    }
-
-    /**
-     * If face hardware is available, user has enrolled and enabled auth via setting.
-     *
-     * @deprecated This is being migrated to use modern architecture.
-     */
-    @Deprecated
-    public boolean isFaceAuthEnabledForUser(int userId) {
-        // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
-        updateFaceEnrolled(userId);
-        return mIsFaceEnrolled;
+        return isFaceSupported() && isFaceEnrolled(userId) && !isFaceDisabled(userId);
     }
 
     private void notifyAboutEnrollmentChange(@BiometricAuthenticator.Modality int modality) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index fa07072..5bf8d63 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -660,19 +660,6 @@
         )
     }
 
-    fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            {
-                int1 = userId
-                bool1 = oldValue
-                bool2 = newValue
-            },
-            { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
-        )
-    }
-
     fun logTrustUsuallyManagedUpdated(
         userId: Int,
         oldValue: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 584357b..0bd4859 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1461,8 +1461,7 @@
         mDragView.setColorFilter(filter);
     }
 
-    @VisibleForTesting
-    void setBounceEffectDuration(int duration) {
+    private void setBounceEffectDuration(int duration) {
         mBounceEffectDuration = duration;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 105de16c..cd8bef1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.systemui.Flags;
 
 import java.util.HashMap;
 
@@ -91,16 +92,48 @@
     }
 
     void moveToPosition(PointF position) {
-        moveToPositionX(position.x);
-        moveToPositionY(position.y);
+        moveToPosition(position, /* animateMovement = */ false);
+    }
+
+    /* Moves position without updating underlying percentage position. Can be animated. */
+    void moveToPosition(PointF position, boolean animateMovement) {
+        if (Flags.floatingMenuImeDisplacementAnimation()) {
+            moveToPositionX(position.x, animateMovement);
+            moveToPositionY(position.y, animateMovement);
+        } else {
+            moveToPositionX(position.x, /* animateMovement = */ false);
+            moveToPositionY(position.y, /* animateMovement = */ false);
+        }
     }
 
     void moveToPositionX(float positionX) {
-        DynamicAnimation.TRANSLATION_X.setValue(mMenuView, positionX);
+        moveToPositionX(positionX, /* animateMovement = */ false);
     }
 
-    private void moveToPositionY(float positionY) {
-        DynamicAnimation.TRANSLATION_Y.setValue(mMenuView, positionY);
+    void moveToPositionX(float positionX, boolean animateMovement) {
+        if (animateMovement && Flags.floatingMenuImeDisplacementAnimation()) {
+            springMenuWith(DynamicAnimation.TRANSLATION_X,
+                    createSpringForce(),
+                    /* velocity = */ 0,
+                    positionX, /* writeToPosition = */ false);
+        } else {
+            DynamicAnimation.TRANSLATION_X.setValue(mMenuView, positionX);
+        }
+    }
+
+    void moveToPositionY(float positionY) {
+        moveToPositionY(positionY, /* animateMovement = */ false);
+    }
+
+    void moveToPositionY(float positionY, boolean animateMovement) {
+        if (animateMovement && Flags.floatingMenuImeDisplacementAnimation()) {
+            springMenuWith(DynamicAnimation.TRANSLATION_Y,
+                    createSpringForce(),
+                    /* velocity = */ 0,
+                    positionY, /* writeToPosition = */ false);
+        } else {
+            DynamicAnimation.TRANSLATION_Y.setValue(mMenuView, positionY);
+        }
     }
 
     void moveToPositionYIfNeeded(float positionY) {
@@ -151,7 +184,7 @@
     void moveAndPersistPosition(PointF position) {
         moveToPosition(position);
         mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
-        constrainPositionAndUpdate(position);
+        constrainPositionAndUpdate(position, /* writeToPosition = */ true);
     }
 
     void removeMenu() {
@@ -180,17 +213,13 @@
         flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X,
                 startXVelocity,
                 FLING_FRICTION_SCALAR,
-                new SpringForce()
-                        .setStiffness(SPRING_STIFFNESS)
-                        .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO),
+                createSpringForce(),
                 finalPositionX);
 
         flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
                 velocityY,
                 FLING_FRICTION_SCALAR,
-                new SpringForce()
-                        .setStiffness(SPRING_STIFFNESS)
-                        .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO),
+                createSpringForce(),
                 /* finalPosition= */ null);
     }
 
@@ -226,7 +255,8 @@
                     final float endPosition = finalPosition != null
                             ? finalPosition
                             : Math.max(min, Math.min(max, endValue));
-                    springMenuWith(property, spring, endVelocity, endPosition);
+                    springMenuWith(property, spring, endVelocity, endPosition,
+                            /* writeToPosition = */ true);
                 });
 
         cancelAnimation(property);
@@ -242,7 +272,7 @@
 
     @VisibleForTesting
     void springMenuWith(DynamicAnimation.ViewProperty property, SpringForce spring,
-            float velocity, float finalPosition) {
+            float velocity, float finalPosition, boolean writeToPosition) {
         final MenuPositionProperty menuPositionProperty = new MenuPositionProperty(property);
         final SpringAnimation springAnimation =
                 new SpringAnimation(mMenuView, menuPositionProperty)
@@ -257,7 +287,7 @@
                                             DynamicAnimation::isRunning);
                             if (!areAnimationsRunning) {
                                 onSpringAnimationsEnd(new PointF(mMenuView.getTranslationX(),
-                                        mMenuView.getTranslationY()));
+                                        mMenuView.getTranslationY()), writeToPosition);
                             }
                         })
                         .setStartVelocity(velocity);
@@ -281,7 +311,8 @@
         if (currentXTranslation < draggableBounds.left
                 || currentXTranslation > draggableBounds.right) {
             constrainPositionAndUpdate(
-                    new PointF(mMenuView.getTranslationX(), mMenuView.getTranslationY()));
+                    new PointF(mMenuView.getTranslationX(), mMenuView.getTranslationY()),
+                    /* writeToPosition = */ true);
             moveToEdgeAndHide();
             return true;
         }
@@ -298,15 +329,19 @@
         return mMenuView.isMoveToTucked();
     }
 
-    void moveToEdgeAndHide() {
-        mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ true);
-
+    PointF getTuckedMenuPosition() {
         final PointF position = mMenuView.getMenuPosition();
         final float menuHalfWidth = mMenuView.getMenuWidth() / 2.0f;
         final float endX = isOnLeftSide()
                 ? position.x - menuHalfWidth
                 : position.x + menuHalfWidth;
-        moveToPosition(new PointF(endX, position.y));
+        return new PointF(endX, position.y);
+    }
+
+    void moveToEdgeAndHide() {
+        mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ true);
+        final PointF position = mMenuView.getMenuPosition();
+        moveToPosition(getTuckedMenuPosition());
 
         // Keep the touch region let users could click extra space to pop up the menu view
         // from the screen edge
@@ -335,6 +370,11 @@
         mPositionAnimations.get(property).cancel();
     }
 
+    @VisibleForTesting
+    DynamicAnimation getAnimation(DynamicAnimation.ViewProperty property) {
+        return mPositionAnimations.getOrDefault(property, null);
+    }
+
     void onDraggingStart() {
         mMenuView.onDraggingStart();
     }
@@ -361,9 +401,9 @@
                 .start();
     }
 
-    private void onSpringAnimationsEnd(PointF position) {
+    private void onSpringAnimationsEnd(PointF position, boolean writeToPosition) {
         mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
-        constrainPositionAndUpdate(position);
+        constrainPositionAndUpdate(position, writeToPosition);
 
         fadeOutIfEnabled();
 
@@ -372,7 +412,7 @@
         }
     }
 
-    private void constrainPositionAndUpdate(PointF position) {
+    private void constrainPositionAndUpdate(PointF position, boolean writeToPosition) {
         final Rect draggableBounds = mMenuView.getMenuDraggableBoundsExcludeIme();
         // Have the space gap margin between the top bound and the menu view, so actually the
         // position y range needs to cut the margin.
@@ -384,7 +424,12 @@
         final float percentageY = position.y < 0 || draggableBounds.height() == 0
                 ? MIN_PERCENT
                 : Math.min(MAX_PERCENT, position.y / draggableBounds.height());
-        mMenuView.persistPositionAndUpdateEdge(new Position(percentageX, percentageY));
+
+        if (Flags.floatingMenuImeDisplacementAnimation() && !writeToPosition) {
+            mMenuView.onEdgeChangedIfNeeded();
+        } else {
+            mMenuView.persistPositionAndUpdateEdge(new Position(percentageX, percentageY));
+        }
     }
 
     void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) {
@@ -463,4 +508,11 @@
             mProperty.setValue(menuView, value);
         }
     }
+
+    @VisibleForTesting
+    static SpringForce createSpringForce() {
+        return new SpringForce()
+                .setStiffness(SPRING_STIFFNESS)
+                .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index e1612b0..ea5a56c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -54,7 +54,6 @@
     private final List<AccessibilityTarget> mTargetFeatures = new ArrayList<>();
     private final AccessibilityTargetAdapter mAdapter;
     private final MenuViewModel mMenuViewModel;
-    private final MenuAnimationController mMenuAnimationController;
     private final Rect mBoundsInParent = new Rect();
     private final RecyclerView mTargetFeaturesView;
     private final ViewTreeObserver.OnDrawListener mSystemGestureExcludeUpdater =
@@ -70,6 +69,7 @@
 
     private boolean mIsMoveToTucked;
 
+    private final MenuAnimationController mMenuAnimationController;
     private OnTargetFeaturesChangeListener mFeaturesChangeListener;
 
     MenuView(Context context, MenuViewModel menuViewModel, MenuViewAppearance menuViewAppearance) {
@@ -197,8 +197,30 @@
     }
 
     void onPositionChanged() {
-        final PointF position = mMenuViewAppearance.getMenuPosition();
-        mMenuAnimationController.moveToPosition(position);
+        onPositionChanged(/* animateMovement = */ false);
+    }
+
+    void onPositionChanged(boolean animateMovement) {
+        final PointF position;
+        if (isMoveToTucked()) {
+            position = mMenuAnimationController.getTuckedMenuPosition();
+        } else {
+            position = getMenuPosition();
+        }
+
+        // We can skip animating if FAB is not visible
+        if (Flags.floatingMenuImeDisplacementAnimation()
+                && animateMovement && getVisibility() == VISIBLE) {
+            mMenuAnimationController.moveToPosition(position, /* animateMovement = */ true);
+            // onArrivalAtPosition() is called at the end of the animation.
+        } else {
+            mMenuAnimationController.moveToPosition(position);
+            onArrivalAtPosition(); // no animation, so we call this immediately.
+        }
+    }
+
+    void onArrivalAtPosition() {
+        final PointF position = getMenuPosition();
         onBoundsInParentChanged((int) position.x, (int) position.y);
 
         if (isMoveToTucked()) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index b6e8997..fbca022 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -59,6 +59,7 @@
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.systemui.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.wm.shell.bubbles.DismissViewUtils;
@@ -331,7 +332,7 @@
             mMenuViewAppearance.onImeVisibilityChanged(windowInsets.isVisible(ime()), imeTop);
 
             mMenuView.onEdgeChanged();
-            mMenuView.onPositionChanged();
+            mMenuView.onPositionChanged(/* animateMovement = */ true);
 
             mImeInsetsRect.set(imeInsetsRect);
         }
@@ -362,6 +363,10 @@
 
             mMenuAnimationController.startTuckedAnimationPreview();
         }
+
+        if (Flags.floatingMenuImeDisplacementAnimation()) {
+            mMenuView.onArrivalAtPosition();
+        }
     }
 
     private CharSequence getMigrationMessage() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 272e0f2..934f9f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,7 +46,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
@@ -240,15 +239,14 @@
             }
             REASON_AUTH_KEYGUARD -> {
                 if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
-                    udfpsKeyguardViewModels.get().setSensorBounds(sensorBounds)
-                    UdfpsKeyguardViewController(
-                        view.addUdfpsView(R.layout.udfps_keyguard_view),
-                        statusBarStateController,
-                        primaryBouncerInteractor,
-                        dialogManager,
-                        dumpManager,
-                        alternateBouncerInteractor,
-                        udfpsKeyguardViewModels.get(),
+                    // note: empty controller, currently shows no visual affordance
+                    // instead SysUI will show the fingerprint icon in its DeviceEntryIconView
+                    UdfpsBpViewController(
+                            view.addUdfpsView(R.layout.udfps_bp_view),
+                            statusBarStateController,
+                            primaryBouncerInteractor,
+                            dialogManager,
+                            dumpManager
                     )
                 } else {
                     UdfpsKeyguardViewControllerLegacy(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index 56e3f39..2f493ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -2,11 +2,11 @@
 
 import android.content.Context
 import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.graphics.Insets
 import android.text.TextUtils
 import android.util.AttributeSet
 import android.view.View
 import android.view.WindowInsets
-import android.view.WindowInsets.Type.ime
 import android.view.accessibility.AccessibilityManager
 import android.widget.LinearLayout
 import android.widget.TextView
@@ -41,7 +41,10 @@
     }
 
     override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
-        val imeBottomInset = insets.getInsets(ime()).bottom
+        val statusBarInsets: Insets = insets.getInsets(WindowInsets.Type.statusBars())
+        val keyboardInsets: Insets = insets.getInsets(WindowInsets.Type.ime())
+        val navigationInsets: Insets = insets.getInsets(WindowInsets.Type.navigationBars())
+        val imeBottomInset = keyboardInsets.bottom
         if (bottomInset != imeBottomInset) {
             val titleView: TextView? = findViewById(R.id.title)
             if (titleView != null) {
@@ -61,8 +64,14 @@
                 }
             }
         }
-        setPadding(paddingLeft, paddingTop, paddingRight, imeBottomInset)
-        return insets.inset(0, 0, 0, imeBottomInset)
+
+        setPadding(
+            0,
+            statusBarInsets.top,
+            0,
+            if (keyboardInsets.bottom == 0) navigationInsets.bottom else keyboardInsets.bottom
+        )
+        return WindowInsets.CONSUMED
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
index 75331f0..1086897 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
@@ -1,7 +1,11 @@
 package com.android.systemui.biometrics.ui
 
 import android.content.Context
+import android.graphics.Insets
 import android.util.AttributeSet
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowInsets.Type
 import android.widget.LinearLayout
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
@@ -9,7 +13,7 @@
 
 /** Pattern credential view for BiometricPrompt. */
 class CredentialPatternView(context: Context, attrs: AttributeSet?) :
-    LinearLayout(context, attrs), CredentialView {
+    LinearLayout(context, attrs), CredentialView, View.OnApplyWindowInsetsListener {
 
     /** Initializes the view. */
     override fun init(
@@ -20,4 +24,17 @@
     ) {
         CredentialViewBinder.bind(this, host, viewModel, panelViewController, animatePanel)
     }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        setOnApplyWindowInsetsListener(this)
+    }
+
+    override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
+        val statusBarInsets: Insets = insets.getInsets(Type.statusBars())
+        val navigationInsets: Insets = insets.getInsets(Type.navigationBars())
+
+        setPadding(0, statusBarInsets.top, 0, navigationInsets.bottom)
+        return WindowInsets.CONSUMED
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 21578f4..56dfa5ed 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -415,7 +415,7 @@
     /** Whether we want to wait to show the bouncer in case passive auth succeeds. */
     private fun usePrimaryBouncerPassiveAuthDelay(): Boolean {
         val canRunFaceAuth =
-            keyguardStateController.isFaceAuthEnabled &&
+            keyguardStateController.isFaceEnrolled &&
                 keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) &&
                 keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()
         val canRunActiveUnlock =
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 8d5b84f..7bca86e 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -15,18 +15,26 @@
 package com.android.systemui.common.ui
 
 import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
 import androidx.annotation.AttrRes
 import androidx.annotation.ColorInt
 import androidx.annotation.DimenRes
+import androidx.annotation.LayoutRes
 import com.android.settingslib.Utils
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
 import com.android.systemui.statusbar.policy.onThemeChanged
 import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.view.bindLatest
 import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 
 /** Configuration-aware-state-tracking utilities. */
 class ConfigurationState
@@ -34,6 +42,7 @@
 constructor(
     private val configurationController: ConfigurationController,
     @Application private val context: Context,
+    private val layoutInflater: LayoutInflater,
 ) {
     /**
      * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
@@ -57,4 +66,65 @@
             Utils.getColorAttrDefaultColor(context, id, defaultValue)
         }
     }
+
+    /**
+     * Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
+     * the device configuration.
+     *
+     * @see LayoutInflater.inflate
+     */
+    @Suppress("UNCHECKED_CAST")
+    fun <T : View> inflateLayout(
+        @LayoutRes id: Int,
+        root: ViewGroup?,
+        attachToRoot: Boolean,
+    ): Flow<T> {
+        // TODO(b/305930747): This may lead to duplicate invocations if both flows emit, find a
+        //  solution to only emit one event.
+        return merge(
+                configurationController.onThemeChanged,
+                configurationController.onDensityOrFontScaleChanged,
+            )
+            .emitOnStart()
+            .map { layoutInflater.inflate(id, root, attachToRoot) as T }
+    }
+}
+
+/**
+ * Perform an inflation right away, then re-inflate whenever the device configuration changes, and
+ * call [onInflate] on the resulting view each time. Disposes of the [DisposableHandle] returned by
+ * [onInflate] when done.
+ *
+ * This never completes unless cancelled, it just suspends and waits for updates.
+ *
+ * For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate].
+ *
+ * An example use-case of this is when a view needs to be re-inflated whenever a configuration
+ * change occurs, which would require the ViewBinder to then re-bind the new view. For example, the
+ * code in the parent view's binder would look like:
+ * ```
+ * parentView.repeatWhenAttached {
+ *     configurationState
+ *         .reinflateOnChange(
+ *             R.layout.my_layout,
+ *             parentView,
+ *             attachToRoot = false,
+ *             coroutineScope = lifecycleScope,
+ *             configurationController.onThemeChanged,
+ *         ) { view: ChildView ->
+ *             ChildViewBinder.bind(view, childViewModel)
+ *         }
+ * }
+ * ```
+ *
+ * In turn, the bind method (passed through [onInflate]) uses [repeatWhenAttached], which returns a
+ * [DisposableHandle].
+ */
+suspend fun <T : View> ConfigurationState.reinflateAndBindLatest(
+    @LayoutRes resource: Int,
+    root: ViewGroup?,
+    attachToRoot: Boolean,
+    onInflate: (T) -> DisposableHandle?,
+) {
+    inflateLayout<T>(resource, root, attachToRoot).bindLatest(onInflate)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
index 323070a..0781451 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
@@ -22,6 +22,7 @@
 import android.util.AttributeSet
 import android.view.MotionEvent
 import android.view.View
+import android.view.ViewConfiguration
 import com.android.systemui.shade.TouchLogger
 import kotlin.math.pow
 import kotlin.math.sqrt
@@ -36,11 +37,18 @@
 class LongPressHandlingView(
     context: Context,
     attrs: AttributeSet?,
+    private val longPressDuration: () -> Long,
 ) :
     View(
         context,
         attrs,
     ) {
+
+    constructor(
+        context: Context,
+        attrs: AttributeSet?,
+    ) : this(context, attrs, { ViewConfiguration.getLongPressTimeout().toLong() })
+
     interface Listener {
         /** Notifies that a long-press has been detected by the given view. */
         fun onLongPressDetected(
@@ -77,6 +85,7 @@
                 )
             },
             onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) },
+            longPressDuration = longPressDuration,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
index c2d4d12..a742e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
@@ -33,6 +33,8 @@
     private val onLongPressDetected: (x: Int, y: Int) -> Unit,
     /** Callback reporting the a single tap gesture was detected at the given coordinates. */
     private val onSingleTapDetected: () -> Unit,
+    /** Time for the touch to be considered a long-press in ms */
+    private val longPressDuration: () -> Long,
 ) {
     sealed class MotionEventModel {
         object Other : MotionEventModel()
@@ -77,7 +79,7 @@
                 cancelScheduledLongPress()
                 if (
                     event.distanceMoved <= ViewConfiguration.getTouchSlop() &&
-                        event.gestureDuration < ViewConfiguration.getLongPressTimeout()
+                        event.gestureDuration < longPressDuration()
                 ) {
                     dispatchSingleTap()
                 }
@@ -103,7 +105,7 @@
                         y = y,
                     )
                 },
-                ViewConfiguration.getLongPressTimeout().toLong(),
+                longPressDuration(),
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c32acf6..f4a9f73 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -87,11 +87,6 @@
     @JvmField
     val NOTIFICATION_SHELF_REFACTOR = releasedFlag("notification_shelf_refactor")
 
-    // TODO(b/290787599): Tracking Bug
-    @JvmField
-    val NOTIFICATION_ICON_CONTAINER_REFACTOR =
-        unreleasedFlag("notification_icon_container_refactor")
-
     // TODO(b/288326013): Tracking Bug
     @JvmField
     val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION =
@@ -123,7 +118,7 @@
     // TODO(b/301955929)
     @JvmField
     val NOTIF_LS_BACKGROUND_THREAD =
-            unreleasedFlag("notification_lockscreen_mgr_bg_thread")
+            unreleasedFlag("notification_lockscreen_mgr_bg_thread", teamfood = true)
 
     // 200 - keyguard/lockscreen
     // ** Flag retired **
@@ -488,7 +483,7 @@
     val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
 
     // TODO(b/304506662): Tracking Bug
-    val MEDIA_DEVICE_NAME_FIX = unreleasedFlag("media_device_name_fix", teamfood = true)
+    val MEDIA_DEVICE_NAME_FIX = releasedFlag("media_device_name_fix")
 
     // 1000 - dock
     val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
@@ -627,7 +622,7 @@
 
     /** TODO(b/295143676): Tracking bug. When enable, captures a screenshot for each display. */
     @JvmField
-    val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot", teamfood = true)
+    val MULTI_DISPLAY_SCREENSHOT = releasedFlag("multi_display_screenshot")
 
     // 1400 - columbus
     // TODO(b/254512756): Tracking Bug
@@ -731,7 +726,7 @@
     @JvmField val KEYBOARD_BACKLIGHT_INDICATOR = releasedFlag("keyboard_backlight_indicator")
 
     // TODO(b/277192623): Tracking Bug
-    @JvmField val KEYBOARD_EDUCATION = unreleasedFlag("keyboard_education", teamfood = true)
+    @JvmField val KEYBOARD_EDUCATION = releasedFlag("keyboard_education")
 
     // TODO(b/277201412): Tracking Bug
     @JvmField
@@ -777,7 +772,8 @@
     @JvmField val PRECOMPUTED_TEXT = releasedFlag("precomputed_text")
 
     // TODO(b/302087895): Tracking Bug
-    @JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+    @JvmField val CALL_LAYOUT_ASYNC_SET_DATA =
+            unreleasedFlag("call_layout_async_set_data", teamfood = true)
 
     // TODO(b/302144438): Tracking Bug
     @JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 119ade4..c56dfde 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -72,7 +73,7 @@
     private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
     private val context: Context,
     private val keyguardIndicationController: KeyguardIndicationController,
-    private val lockIconViewController: LockIconViewController,
+    private val lockIconViewController: Lazy<LockIconViewController>,
     private val shadeInteractor: ShadeInteractor,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
@@ -131,7 +132,9 @@
         val indicationArea = KeyguardIndicationArea(context, null)
         keyguardIndicationController.setIndicationArea(indicationArea)
 
-        lockIconViewController.setLockIconView(LockIconView(context, null))
+        if (!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+            lockIconViewController.get().setLockIconView(LockIconView(context, null))
+        }
     }
 
     private fun bindKeyguardRootView() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 2dc4908..4d26466 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -93,6 +93,12 @@
     val isKeyguardOccluded: Flow<Boolean>
 
     /**
+     * Whether the device is locked or unlocked right now. This is true when keyguard has been
+     * dismissed or can be dismissed by a swipe
+     */
+    val isKeyguardUnlocked: StateFlow<Boolean>
+
+    /**
      * Observable for the signal that keyguard is about to go away.
      *
      * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed.
@@ -332,6 +338,44 @@
             }
             .distinctUntilChanged()
 
+    override val isKeyguardUnlocked: StateFlow<Boolean> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : KeyguardStateController.Callback {
+                        override fun onUnlockedChanged() {
+                            trySendWithFailureLogging(
+                                keyguardStateController.isUnlocked,
+                                TAG,
+                                "updated isKeyguardUnlocked due to onUnlockedChanged"
+                            )
+                        }
+
+                        override fun onKeyguardShowingChanged() {
+                            trySendWithFailureLogging(
+                                keyguardStateController.isUnlocked,
+                                TAG,
+                                "updated isKeyguardUnlocked due to onKeyguardShowingChanged"
+                            )
+                        }
+                    }
+
+                keyguardStateController.addCallback(callback)
+                // Adding the callback does not send an initial update.
+                trySendWithFailureLogging(
+                    keyguardStateController.isUnlocked,
+                    TAG,
+                    "initial isKeyguardUnlocked"
+                )
+
+                awaitClose { keyguardStateController.removeCallback(callback) }
+            }
+            .distinctUntilChanged()
+            .stateIn(
+                scope,
+                SharingStarted.Eagerly,
+                initialValue = false,
+            )
+
     override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
         val callback =
             object : KeyguardStateController.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index b953b48..eaec0d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -78,7 +77,6 @@
     private val powerInteractor: PowerInteractor,
     featureFlags: FeatureFlags,
     sceneContainerFlags: SceneContainerFlags,
-    deviceEntryRepository: DeviceEntryRepository,
     bouncerRepository: KeyguardBouncerRepository,
     configurationRepository: ConfigurationRepository,
     shadeRepository: ShadeRepository,
@@ -160,7 +158,7 @@
     val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
 
     /** Whether the keyguard is unlocked or not. */
-    val isKeyguardUnlocked: Flow<Boolean> = deviceEntryRepository.isUnlocked
+    val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
 
     /** Whether the keyguard is occluded (covered by an activity). */
     val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index 122c4c4..55b420b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -29,8 +29,10 @@
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Handles key events arriving when the keyguard is showing or device is dozing. */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class KeyguardKeyEventInteractor
 @Inject
@@ -53,9 +55,14 @@
         }
 
         if (event.handleAction()) {
+            if (KeyEvent.isConfirmKey(event.keyCode)) {
+                if (isDeviceAwake()) {
+                    return collapseShadeLockedOrShowPrimaryBouncer()
+                }
+            }
+
             when (event.keyCode) {
                 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
-                KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
             }
         }
         return false
@@ -91,16 +98,22 @@
                 (statusBarStateController.state != StatusBarState.SHADE) &&
                 statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
         if (shouldUnlockOnMenuPressed) {
-            shadeController.animateCollapseShadeForced()
-            return true
+            return collapseShadeLockedOrShowPrimaryBouncer()
         }
         return false
     }
 
-    private fun dispatchSpaceEvent(): Boolean {
-        if (isDeviceAwake() && statusBarStateController.state != StatusBarState.SHADE) {
-            shadeController.animateCollapseShadeForced()
-            return true
+    private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
+        when (statusBarStateController.state) {
+            StatusBarState.SHADE -> return false
+            StatusBarState.SHADE_LOCKED -> {
+                shadeController.animateCollapseShadeForced()
+                return true
+            }
+            StatusBarState.KEYGUARD -> {
+                statusBarKeyguardViewManager.showPrimaryBouncer(true)
+                return true
+            }
         }
         return false
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
new file mode 100644
index 0000000..e82ea7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.binder
+
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.FalsingManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object DeviceEntryIconViewBinder {
+
+    /**
+     * Updates UI for the device entry icon view (lock, unlock and fingerprint icons) and its
+     * background.
+     */
+    @SuppressLint("ClickableViewAccessibility")
+    @JvmStatic
+    fun bind(
+        view: DeviceEntryIconView,
+        viewModel: DeviceEntryIconViewModel,
+        falsingManager: FalsingManager,
+    ) {
+        val iconView = view.iconView
+        val bgView = view.bgView
+        val longPressHandlingView = view.longPressHandlingView
+        longPressHandlingView.listener =
+            object : LongPressHandlingView.Listener {
+                override fun onLongPressDetected(view: View, x: Int, y: Int) {
+                    if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+                        return
+                    }
+                    viewModel.onLongPress()
+                }
+            }
+        view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.iconViewModel.collect { iconViewModel ->
+                        iconView.setImageState(
+                            view.getIconState(iconViewModel.type, iconViewModel.useAodVariant),
+                            /* merge */ false
+                        )
+                        iconView.imageTintList = ColorStateList.valueOf(iconViewModel.tint)
+                        iconView.alpha = iconViewModel.alpha
+                        iconView.setPadding(
+                            iconViewModel.padding,
+                            iconViewModel.padding,
+                            iconViewModel.padding,
+                            iconViewModel.padding,
+                        )
+                    }
+                }
+                launch {
+                    viewModel.backgroundViewModel.collect { bgViewModel ->
+                        bgView.alpha = bgViewModel.alpha
+                        bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+                    }
+                }
+                launch {
+                    viewModel.burnInViewModel.collect { burnInViewModel ->
+                        view.translationX = burnInViewModel.x.toFloat()
+                        view.translationY = burnInViewModel.y.toFloat()
+                        view.aodFpDrawable.progress = burnInViewModel.progress
+                    }
+                }
+                launch {
+                    viewModel.isLongPressEnabled.collect { isEnabled ->
+                        longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
+                    }
+                }
+                launch {
+                    viewModel.accessibilityDelegateHint.collect { hint ->
+                        view.accessibilityHintType = hint
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
new file mode 100644
index 0000000..c9e3954
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.view
+
+import android.content.Context
+import android.graphics.drawable.AnimatedStateListDrawable
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.util.AttributeSet
+import android.util.StateSet
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.FrameLayout
+import android.widget.ImageView
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.res.R
+
+class DeviceEntryIconView
+@JvmOverloads
+constructor(
+    context: Context,
+    attrs: AttributeSet?,
+    defStyleAttrs: Int = 0,
+) : FrameLayout(context, attrs, defStyleAttrs) {
+    val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+    val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
+    val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
+    val aodFpDrawable: LottieDrawable = LottieDrawable()
+    var accessibilityHintType: AccessibilityHintType = AccessibilityHintType.NONE
+
+    private var animatedIconDrawable: AnimatedStateListDrawable = AnimatedStateListDrawable()
+
+    init {
+        setupIconStates()
+        setupIconTransitions()
+        setupAccessibilityDelegate()
+
+        // Ordering matters. From background to foreground we want:
+        //     bgView, iconView, longpressHandlingView overlay
+        addBgImageView()
+        addIconImageView()
+        addLongpressHandlingView()
+    }
+
+    private fun setupAccessibilityDelegate() {
+        accessibilityDelegate =
+            object : AccessibilityDelegate() {
+                private val accessibilityAuthenticateHint =
+                    AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
+                        resources.getString(R.string.accessibility_authenticate_hint)
+                    )
+                private val accessibilityEnterHint =
+                    AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
+                        resources.getString(R.string.accessibility_enter_hint)
+                    )
+                override fun onInitializeAccessibilityNodeInfo(
+                    v: View,
+                    info: AccessibilityNodeInfo
+                ) {
+                    super.onInitializeAccessibilityNodeInfo(v, info)
+                    when (accessibilityHintType) {
+                        AccessibilityHintType.AUTHENTICATE ->
+                            info.addAction(accessibilityAuthenticateHint)
+                        AccessibilityHintType.ENTER -> info.addAction(accessibilityEnterHint)
+                        AccessibilityHintType.NONE -> return
+                    }
+                }
+            }
+    }
+
+    private fun setupIconStates() {
+        // Lockscreen States
+        // LOCK
+        animatedIconDrawable.addState(
+            getIconState(IconType.LOCK, false),
+            context.getDrawable(R.drawable.ic_lock)!!,
+            R.id.locked,
+        )
+        // UNLOCK
+        animatedIconDrawable.addState(
+            getIconState(IconType.UNLOCK, false),
+            context.getDrawable(R.drawable.ic_unlocked)!!,
+            R.id.unlocked,
+        )
+        // FINGERPRINT
+        animatedIconDrawable.addState(
+            getIconState(IconType.FINGERPRINT, false),
+            context.getDrawable(R.drawable.ic_kg_fingerprint)!!,
+            R.id.locked_fp,
+        )
+
+        // AOD states
+        // LOCK
+        animatedIconDrawable.addState(
+            getIconState(IconType.LOCK, true),
+            context.getDrawable(R.drawable.ic_lock_aod)!!,
+            R.id.locked_aod,
+        )
+        // UNLOCK
+        animatedIconDrawable.addState(
+            getIconState(IconType.UNLOCK, true),
+            context.getDrawable(R.drawable.ic_unlocked_aod)!!,
+            R.id.unlocked_aod,
+        )
+        // FINGERPRINT
+        LottieCompositionFactory.fromRawRes(mContext, R.raw.udfps_aod_fp).addListener { result ->
+            aodFpDrawable.setComposition(result)
+        }
+        animatedIconDrawable.addState(
+            getIconState(IconType.FINGERPRINT, true),
+            aodFpDrawable,
+            R.id.udfps_aod_fp,
+        )
+
+        // WILDCARD: should always be the last state added since any states will match with this
+        // and therefore won't get matched with subsequent states.
+        animatedIconDrawable.addState(
+            StateSet.WILD_CARD,
+            context.getDrawable(R.color.transparent)!!,
+            R.id.no_icon,
+        )
+    }
+
+    private fun setupIconTransitions() {
+        // LockscreenFp <=> LockscreenUnlocked
+        animatedIconDrawable.addTransition(
+            R.id.locked_fp,
+            R.id.unlocked,
+            context.getDrawable(R.drawable.fp_to_unlock) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+        animatedIconDrawable.addTransition(
+            R.id.unlocked,
+            R.id.locked_fp,
+            context.getDrawable(R.drawable.unlock_to_fp) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+
+        // LockscreenLocked <=> AodLocked
+        animatedIconDrawable.addTransition(
+            R.id.locked_aod,
+            R.id.locked,
+            context.getDrawable(R.drawable.lock_aod_to_ls) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+        animatedIconDrawable.addTransition(
+            R.id.locked,
+            R.id.locked_aod,
+            context.getDrawable(R.drawable.lock_ls_to_aod) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+
+        // LockscreenUnlocked <=> AodUnlocked
+        animatedIconDrawable.addTransition(
+            R.id.unlocked_aod,
+            R.id.unlocked,
+            context.getDrawable(R.drawable.unlocked_aod_to_ls) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+        animatedIconDrawable.addTransition(
+            R.id.unlocked,
+            R.id.unlocked_aod,
+            context.getDrawable(R.drawable.unlocked_ls_to_aod) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+
+        // LockscreenLocked <=> LockscreenUnlocked
+        animatedIconDrawable.addTransition(
+            R.id.locked,
+            R.id.unlocked,
+            context.getDrawable(R.drawable.lock_to_unlock) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+        animatedIconDrawable.addTransition(
+            R.id.unlocked,
+            R.id.locked,
+            context.getDrawable(R.drawable.unlocked_to_locked) as AnimatedVectorDrawable,
+            /* reversible */ false,
+        )
+
+        // LockscreenFingerprint <=> LockscreenLocked
+        animatedIconDrawable.addTransition(
+            R.id.locked_fp,
+            R.id.locked,
+            context.getDrawable(R.drawable.fp_to_locked) as AnimatedVectorDrawable,
+            /* reversible */ true,
+        )
+
+        // LockscreenUnlocked <=> AodLocked
+        animatedIconDrawable.addTransition(
+            R.id.unlocked,
+            R.id.locked_aod,
+            context.getDrawable(R.drawable.unlocked_to_aod_lock) as AnimatedVectorDrawable,
+            /* reversible */ true,
+        )
+    }
+
+    private fun addLongpressHandlingView() {
+        addView(longPressHandlingView)
+        val lp = longPressHandlingView.layoutParams as LayoutParams
+        lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+        lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+        longPressHandlingView.setLayoutParams(lp)
+    }
+
+    private fun addIconImageView() {
+        iconView.scaleType = ImageView.ScaleType.CENTER_CROP
+        iconView.setImageDrawable(animatedIconDrawable)
+        addView(iconView)
+        val lp = iconView.layoutParams as LayoutParams
+        lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+        lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+        lp.gravity = Gravity.CENTER
+        iconView.setLayoutParams(lp)
+    }
+
+    private fun addBgImageView() {
+        bgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg))
+        addView(bgView)
+        val lp = bgView.layoutParams as LayoutParams
+        lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+        lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+        bgView.setLayoutParams(lp)
+    }
+
+    fun getIconState(icon: IconType, aod: Boolean): IntArray {
+        val lockIconState = IntArray(2)
+        when (icon) {
+            IconType.LOCK -> lockIconState[0] = android.R.attr.state_first
+            IconType.UNLOCK -> lockIconState[0] = android.R.attr.state_last
+            IconType.FINGERPRINT -> lockIconState[0] = android.R.attr.state_middle
+        }
+        if (aod) {
+            lockIconState[1] = android.R.attr.state_single
+        } else {
+            lockIconState[1] = -android.R.attr.state_single
+        }
+        return lockIconState
+    }
+
+    enum class IconType {
+        LOCK,
+        UNLOCK,
+        FINGERPRINT,
+    }
+
+    enum class AccessibilityHintType {
+        NONE,
+        AUTHENTICATE,
+        ENTER,
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index d8e4396..21eba56 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -23,8 +23,8 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
 import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
@@ -44,7 +44,7 @@
 @Inject
 constructor(
     defaultIndicationAreaSection: DefaultIndicationAreaSection,
-    defaultLockIconSection: DefaultLockIconSection,
+    defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
     defaultShortcutsSection: DefaultShortcutsSection,
     defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
     defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
@@ -61,7 +61,7 @@
     override val sections =
         setOf(
             defaultIndicationAreaSection,
-            defaultLockIconSection,
+            defaultDeviceEntryIconSection,
             defaultShortcutsSection,
             defaultAmbientIndicationAreaSection,
             defaultSettingsPopupMenuSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index ce76f56..f8dd7c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -23,8 +23,8 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
 import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
@@ -38,7 +38,7 @@
 @Inject
 constructor(
     defaultIndicationAreaSection: DefaultIndicationAreaSection,
-    defaultLockIconSection: DefaultLockIconSection,
+    defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
     defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
     defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
     alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
@@ -54,7 +54,7 @@
     override val sections =
         setOf(
             defaultIndicationAreaSection,
-            defaultLockIconSection,
+            defaultDeviceEntryIconSection,
             defaultAmbientIndicationAreaSection,
             defaultSettingsPopupMenuSection,
             alignShortcutsToUdfpsSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 669db34..b7fe960 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -26,23 +26,40 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
+import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.policy.ConfigurationController
 import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
 
 class AodNotificationIconsSection
 @Inject
 constructor(
     private val context: Context,
-    private val featureFlags: FeatureFlags,
+    private val configurationState: ConfigurationState,
+    private val configurationController: ConfigurationController,
+    private val dozeParameters: DozeParameters,
+    private val featureFlags: FeatureFlagsClassic,
+    private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+    private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
     private val notificationPanelView: NotificationPanelView,
     private val notificationIconAreaController: NotificationIconAreaController,
+    private val screenOffAnimationController: ScreenOffAnimationController,
 ) : KeyguardSection() {
+
+    private var nicBindingDisposable: DisposableHandle? = null
     private val nicId = R.id.aod_notification_icon_container
     private lateinit var nic: NotificationIconContainer
 
@@ -70,7 +87,23 @@
             return
         }
 
-        notificationIconAreaController.setupAodIcons(nic)
+        if (NotificationIconContainerRefactor.isEnabled) {
+            nic.setOnLockScreen(true)
+            nicBindingDisposable?.dispose()
+            nicBindingDisposable =
+                NotificationIconContainerViewBinder.bind(
+                    nic,
+                    nicAodViewModel,
+                    configurationState,
+                    configurationController,
+                    dozeParameters,
+                    featureFlags,
+                    screenOffAnimationController,
+                    nicAodIconViewStore,
+                )
+        } else {
+            notificationIconAreaController.setupAodIcons(nic)
+        }
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
@@ -102,5 +135,6 @@
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
         constraintLayout.removeView(nicId)
+        nicBindingDisposable?.dispose()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index f4bc713..62c5988 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -33,11 +33,18 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationPanelView
+import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-class DefaultLockIconSection
+@ExperimentalCoroutinesApi
+class DefaultDeviceEntryIconSection
 @Inject
 constructor(
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@@ -46,24 +53,47 @@
     private val context: Context,
     private val notificationPanelView: NotificationPanelView,
     private val featureFlags: FeatureFlags,
-    private val lockIconViewController: LockIconViewController,
+    private val lockIconViewController: Lazy<LockIconViewController>,
+    private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+    private val falsingManager: Lazy<FalsingManager>,
 ) : KeyguardSection() {
-    private val lockIconViewId = R.id.lock_icon_view
+    private val deviceEntryIconViewId = R.id.device_entry_icon_view
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (!featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+        if (
+            !featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON) &&
+                !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
+        ) {
             return
         }
-        notificationPanelView.findViewById<View>(lockIconViewId).let {
+
+        notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
             notificationPanelView.removeView(it)
         }
-        val view = LockIconView(context, null).apply { id = lockIconViewId }
+
+        val view =
+            if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+                DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
+            } else {
+                // Flags.MIGRATE_LOCK_ICON
+                LockIconView(context, null).apply { id = deviceEntryIconViewId }
+            }
         constraintLayout.addView(view)
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        constraintLayout.findViewById<LockIconView?>(lockIconViewId)?.let {
-            lockIconViewController.setLockIconView(it)
+        if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+            constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
+                DeviceEntryIconViewBinder.bind(
+                    it,
+                    deviceEntryIconViewModel.get(),
+                    falsingManager.get(),
+                )
+            }
+        } else {
+            constraintLayout.findViewById<LockIconView?>(deviceEntryIconViewId)?.let {
+                lockIconViewController.get().setLockIconView(it)
+            }
         }
     }
 
@@ -84,30 +114,30 @@
         val defaultDensity =
             DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
                 DisplayMetrics.DENSITY_DEFAULT.toFloat()
-        val lockIconRadiusPx = (defaultDensity * 36).toInt()
+        val iconRadiusPx = (defaultDensity * 36).toInt()
 
         if (isUdfpsSupported) {
             authController.udfpsLocation?.let { udfpsLocation ->
-                centerLockIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
+                centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
             }
         } else {
-            centerLockIcon(
+            centerIcon(
                 Point(
                     (widthPixels / 2).toInt(),
-                    (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
+                    (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt()
                 ),
-                lockIconRadiusPx * scaleFactor,
+                iconRadiusPx * scaleFactor,
                 constraintSet,
             )
         }
     }
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
-        constraintLayout.removeView(lockIconViewId)
+        constraintLayout.removeView(deviceEntryIconViewId)
     }
 
     @VisibleForTesting
-    internal fun centerLockIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
+    internal fun centerIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
         val sensorRect =
             Rect().apply {
                 set(
@@ -119,17 +149,17 @@
             }
 
         constraintSet.apply {
-            constrainWidth(lockIconViewId, sensorRect.right - sensorRect.left)
-            constrainHeight(lockIconViewId, sensorRect.bottom - sensorRect.top)
+            constrainWidth(deviceEntryIconViewId, sensorRect.right - sensorRect.left)
+            constrainHeight(deviceEntryIconViewId, sensorRect.bottom - sensorRect.top)
             connect(
-                lockIconViewId,
+                deviceEntryIconViewId,
                 ConstraintSet.TOP,
                 ConstraintSet.PARENT_ID,
                 ConstraintSet.TOP,
                 sensorRect.top
             )
             connect(
-                lockIconViewId,
+                deviceEntryIconViewId,
                 ConstraintSet.START,
                 ConstraintSet.PARENT_ID,
                 ConstraintSet.START,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
new file mode 100644
index 0000000..842dde3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Color
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+@ExperimentalCoroutinesApi
+class DeviceEntryIconViewModel @Inject constructor() {
+    // TODO: b/305234447 update these states from the data layer
+    val iconViewModel: Flow<IconViewModel> =
+        flowOf(
+            IconViewModel(
+                type = DeviceEntryIconView.IconType.LOCK,
+                useAodVariant = false,
+                tint = Color.WHITE,
+                alpha = 1f,
+                padding = 48,
+            )
+        )
+    val backgroundViewModel: Flow<BackgroundViewModel> =
+        flowOf(BackgroundViewModel(alpha = 1f, tint = Color.GRAY))
+    val burnInViewModel: Flow<BurnInViewModel> = flowOf(BurnInViewModel(0, 0, 0f))
+    val isLongPressEnabled: Flow<Boolean> = flowOf(true)
+    val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
+        flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
+
+    fun onLongPress() {
+        // TODO() vibrate & perform action based on current lock/unlock state
+    }
+    data class BurnInViewModel(
+        val x: Int, // current x burn in offset based on the aodTransitionAmount
+        val y: Int, // current y burn in offset based on the aodTransitionAmount
+        val progress: Float, // current progress based on the aodTransitionAmount
+    )
+
+    class IconViewModel(
+        val type: DeviceEntryIconView.IconType,
+        val useAodVariant: Boolean,
+        val tint: Int,
+        val alpha: Float,
+        val padding: Int,
+    )
+
+    class BackgroundViewModel(
+        val alpha: Float,
+        val tint: Int,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothTileDialogLog.kt
similarity index 69%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
copy to packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothTileDialogLog.kt
index 6d7c576..c73afe8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothTileDialogLog.kt
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.log.dagger
 
-enum class QSTileLifecycle {
-    ALIVE,
-    DEAD,
-}
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for Bluetooth QS tile dialog related logging. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class BluetoothTileDialogLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index fd6b3f1..e768f16 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -91,7 +91,7 @@
     @SysUISingleton
     @NotifInflationLog
     public static LogBuffer provideNotifInflationLogBuffer(LogBufferFactory factory) {
-        return factory.create("NotifInflationLog", 100);
+        return factory.create("NotifInflationLog", 250);
     }
 
     /** Provides a logging buffer for notification interruption calculations. */
@@ -552,4 +552,12 @@
     public static LogBuffer provideSceneFrameworkLogBuffer(LogBufferFactory factory) {
         return factory.create("SceneFramework", 50);
     }
+
+    /** Provides a {@link LogBuffer} for the bluetooth QS tile dialog. */
+    @Provides
+    @SysUISingleton
+    @BluetoothTileDialogLog
+    public static LogBuffer provideQBluetoothTileDialogLogBuffer(LogBufferFactory factory) {
+        return factory.create("BluetoothTileDialogLog", 50);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 1db31ae..2034d97 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -47,6 +47,7 @@
 import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.Lazy
 import java.io.PrintWriter
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -62,10 +63,10 @@
     private val context: Context,
     private val controllerFactory: MediaControllerFactory,
     private val localMediaManagerFactory: LocalMediaManagerFactory,
-    private val mr2manager: MediaRouter2Manager,
+    private val mr2manager: Lazy<MediaRouter2Manager>,
     private val muteAwaitConnectionManagerFactory: MediaMuteAwaitConnectionManagerFactory,
     private val configurationController: ConfigurationController,
-    private val localBluetoothManager: LocalBluetoothManager?,
+    private val localBluetoothManager: Lazy<LocalBluetoothManager?>,
     @Main private val fgExecutor: Executor,
     @Background private val bgExecutor: Executor,
     dumpManager: DumpManager,
@@ -210,8 +211,8 @@
 
         fun dump(pw: PrintWriter) {
             val routingSession =
-                controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
-            val selectedRoutes = routingSession?.let { mr2manager.getSelectedRoutes(it) }
+                controller?.let { mr2manager.get().getRoutingSessionForMediaController(it) }
+            val selectedRoutes = routingSession?.let { mr2manager.get().getSelectedRoutes(it) }
             with(pw) {
                 println("    current device is ${current?.name}")
                 val type = controller?.playbackInfo?.playbackType
@@ -355,7 +356,7 @@
                 val device =
                     aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice
                 val routingSession =
-                    controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
+                    controller?.let { mr2manager.get().getRoutingSessionForMediaController(it) }
 
                 // If we have a controller but get a null route, then don't trust the device
                 val enabled = device != null && (controller == null || routingSession != null)
@@ -380,7 +381,7 @@
             device: MediaDevice?,
             routingSession: RoutingSessionInfo?,
         ): String? {
-            val selectedRoutes = routingSession?.let { mr2manager.getSelectedRoutes(it) }
+            val selectedRoutes = routingSession?.let { mr2manager.get().getSelectedRoutes(it) }
 
             if (DEBUG) {
                 Log.d(
@@ -426,7 +427,9 @@
             return null
         }
 
+        @WorkerThread
         private fun isLeAudioBroadcastEnabled(): Boolean {
+            val localBluetoothManager = localBluetoothManager.get()
             if (localBluetoothManager != null) {
                 val profileManager = localBluetoothManager.profileManager
                 if (profileManager != null) {
@@ -446,19 +449,20 @@
             return false
         }
 
+        @WorkerThread
         private fun getBroadcastingInfo(bluetoothLeBroadcast: LocalBluetoothLeBroadcast) {
-            var currentBroadcastedApp = bluetoothLeBroadcast.appSourceName
+            val currentBroadcastedApp = bluetoothLeBroadcast.appSourceName
             // TODO(b/233698402): Use the package name instead of app label to avoid the
             // unexpected result.
             // Check the current media app's name is the same with current broadcast app's name
             // or not.
-            var mediaApp =
+            val mediaApp =
                 MediaDataUtils.getAppLabel(
                     context,
                     localMediaManager.packageName,
                     context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name)
                 )
-            var isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp)
+            val isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp)
             if (isCurrentBroadcastedApp) {
                 broadcastDescription =
                     context.getString(R.string.broadcasting_description_is_broadcasting)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index 1962119..98a3896 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.mediaprojection
 
 import android.media.projection.IMediaProjectionManager
-import android.os.Process
 import android.os.RemoteException
 import android.util.Log
 import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
@@ -66,6 +65,19 @@
     }
 
     /**
+     * Request to log that the permission request was cancelled.
+     *
+     * @param hostUid The UID of the package that initiates MediaProjection.
+     */
+    fun notifyProjectionRequestCancelled(hostUid: Int) {
+        try {
+            service.notifyPermissionRequestCancelled(hostUid)
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Error notifying server of projection cancelled", e)
+        }
+    }
+
+    /**
      * Request to log that the app selector was displayed.
      *
      * @param hostUid The UID of the package that initiates MediaProjection.
@@ -78,47 +90,6 @@
         }
     }
 
-    /**
-     * Request to log that the permission request moved to the given state.
-     *
-     * Should not be used for the initialization state, since that should use {@link
-     * MediaProjectionMetricsLogger#notifyProjectionInitiated(Int)} and pass the
-     * sessionCreationSource.
-     */
-    fun notifyPermissionProgress(state: Int) {
-        // TODO validate state is valid
-        notifyToServer(state, SessionCreationSource.UNKNOWN)
-    }
-
-    /**
-     * Notifies system server that we are handling a particular state during the consent flow.
-     *
-     * Only used for emitting atoms.
-     *
-     * @param state The state that SystemUI is handling during the consent flow. Must be a valid
-     *   state defined in the MediaProjectionState enum.
-     * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
-     *   Indicates the entry point for requesting the permission. Must be a valid state defined in
-     *   the SessionCreationSource enum.
-     */
-    private fun notifyToServer(state: Int, sessionCreationSource: SessionCreationSource) {
-        Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
-        try {
-            service.notifyPermissionRequestStateChange(
-                Process.myUid(),
-                state,
-                sessionCreationSource.toMetricsConstant()
-            )
-        } catch (e: RemoteException) {
-            Log.e(
-                TAG,
-                "Error notifying server of permission flow state $state from source " +
-                    "$sessionCreationSource",
-                e
-            )
-        }
-    }
-
     companion object {
         const val TAG = "MediaProjectionMetricsLogger"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 04d5566..50e9e751 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -210,6 +210,10 @@
                 reviewGrantedConsentRequired,
                 /* projection= */ null
             )
+            if (isFinishing) {
+                // Only log dismissed when actually finishing, and not when changing configuration.
+                controller.onSelectorDismissed()
+            }
         }
         activityLauncher.destroy()
         controller.destroy()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 67ef119..575e198 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -18,7 +18,6 @@
 
 import android.content.ComponentName
 import android.os.UserHandle
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
@@ -76,6 +75,10 @@
         scope.cancel()
     }
 
+    fun onSelectorDismissed() {
+        logger.notifyProjectionRequestCancelled(hostUid)
+    }
+
     private suspend fun refreshForegroundTaskThumbnails(tasks: List<RecentTask>) {
         coroutineScope {
             val thumbnails: List<Deferred<ThumbnailData?>> =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
index 8b437c3..eea369f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
@@ -32,6 +32,7 @@
 import androidx.annotation.DrawableRes
 import androidx.annotation.LayoutRes
 import androidx.annotation.StringRes
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
 
@@ -40,16 +41,31 @@
     context: Context,
     private val screenShareOptions: List<ScreenShareOption>,
     private val appName: String?,
+    private val hostUid: Int,
+    private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
     @DrawableRes private val dialogIconDrawable: Int? = null,
-    @ColorRes private val dialogIconTint: Int? = null
+    @ColorRes private val dialogIconTint: Int? = null,
 ) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
     private lateinit var dialogTitle: TextView
     private lateinit var startButton: TextView
     private lateinit var cancelButton: TextView
     private lateinit var warning: TextView
     private lateinit var screenShareModeSpinner: Spinner
+    private var hasCancelBeenLogged: Boolean = false
     var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
 
+    override fun dismiss() {
+        super.dismiss()
+
+        // Dismiss can be called multiple times and we only want to log once.
+        if (hasCancelBeenLogged) {
+            return
+        }
+
+        mediaProjectionMetricsLogger.notifyProjectionRequestCancelled(hostUid)
+        hasCancelBeenLogged = true
+    }
+
     public override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index f7cc589..eacfa57 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -222,13 +222,19 @@
         // the correct screen width when in split screen.
         Context dialogContext = getApplicationContext();
         if (isPartialScreenSharingEnabled()) {
-            mDialog = new MediaProjectionPermissionDialog(dialogContext, getMediaProjectionConfig(),
+            mDialog = new MediaProjectionPermissionDialog(
+                    dialogContext,
+                    getMediaProjectionConfig(),
                     () -> {
                         MediaProjectionPermissionDialog dialog =
                                 (MediaProjectionPermissionDialog) mDialog;
                         ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
                         grantMediaProjectionPermission(selectedOption.getMode());
-                    }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
+                    },
+                    () -> finish(RECORD_CANCEL, /* projection= */ null),
+                    appName,
+                    mUid,
+                    mMediaProjectionMetricsLogger);
         } else {
             AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
                     R.style.Theme_SystemUI_Dialog)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
index b9bafd4..cff22b0 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
@@ -18,6 +18,7 @@
 import android.content.Context
 import android.media.projection.MediaProjectionConfig
 import android.os.Bundle
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
 
 /** Dialog to select screen recording options */
@@ -26,12 +27,16 @@
     mediaProjectionConfig: MediaProjectionConfig?,
     private val onStartRecordingClicked: Runnable,
     private val onCancelClicked: Runnable,
-    private val appName: String?
+    private val appName: String?,
+    hostUid: Int,
+    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
 ) :
     BaseScreenSharePermissionDialog(
         context,
         createOptionList(context, appName, mediaProjectionConfig),
-        appName
+        appName,
+        hostUid,
+        mediaProjectionMetricsLogger
     ) {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 38204ab..a6c6233 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -64,14 +64,13 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
-import com.android.settingslib.utils.PowerUtil;
-import com.android.systemui.res.R;
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -376,14 +375,6 @@
                 TAG_AUTO_SAVER, SystemMessage.NOTE_AUTO_SAVER_SUGGESTION, n, UserHandle.ALL);
     }
 
-    private String getHybridContentString(String percentage) {
-        return PowerUtil.getBatteryRemainingStringFormatted(
-                mContext,
-                mCurrentBatterySnapshot.getTimeRemainingMillis(),
-                percentage,
-                mCurrentBatterySnapshot.isBasedOnUsage());
-    }
-
     private PendingIntent pendingBroadcast(String action) {
         return PendingIntent.getBroadcastAsUser(
                 mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index 68bf88b..3b3844a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -74,6 +74,7 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(getView());
         mQsImpl = mQsImplProvider.get();
+        mQsImpl.onCreate(null);
         mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
     }
 
@@ -85,21 +86,13 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (mQsImpl != null) {
-            mQsImpl.onCreate(savedInstanceState);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
+    public void onDestroyView() {
         if (mQsImpl != null) {
             mQsImpl.onDestroy();
+            mQsImpl = null;
         }
+        super.onDestroyView();
     }
-
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 4aad6a0..fab7e95 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -170,6 +170,7 @@
     private CommandQueue mCommandQueue;
 
     private View mRootView;
+    private View mFooterActionsView;
 
     @Inject
     public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -285,6 +286,7 @@
         if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)
                 || !ComposeFacade.INSTANCE.isComposeAvailable()) {
             Log.d(TAG, "Binding the View implementation of the QS footer actions");
+            mFooterActionsView = footerActionsView;
             mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
                     mListeningAndVisibilityLifecycleOwner);
             return;
@@ -294,6 +296,7 @@
         Log.d(TAG, "Binding the Compose implementation of the QS footer actions");
         View composeView = ComposeFacade.INSTANCE.createFooterActionsView(root.getContext(),
                 mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner);
+        mFooterActionsView = composeView;
 
         // The id R.id.qs_footer_actions is used by QSContainerImpl to set the horizontal margin
         // to all views except for qs_footer_actions, so we set it to the Compose view.
@@ -472,7 +475,7 @@
         boolean footerVisible = qsPanelVisible && (mQsExpanded || !keyguardShowing
                 || mHeaderAnimating || mShowCollapsedOnKeyguard);
         mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
-        mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible);
+        mFooterActionsView.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (mQsExpanded && !mStackScrollerOverscrolling));
         mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
@@ -856,7 +859,7 @@
         boolean customizing = isCustomizing();
         mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
         mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
-        mQSFooterActionsViewModel.onVisibilityChangeRequested(!customizing);
+        mFooterActionsView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
         mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
         // Let the panel know the position changed and it needs to update where notifications
         // and whatnot are.
@@ -940,7 +943,7 @@
     @Override
     public void dump(PrintWriter pw, String[] args) {
         IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ "  ");
-        indentingPw.println("QSFragment:");
+        indentingPw.println("QSImpl:");
         indentingPw.increaseIndent();
         indentingPw.println("mQsBounds: " + mQsBounds);
         indentingPw.println("mQsExpanded: " + mQsExpanded);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index fc2f5f9..11db69b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -174,7 +174,9 @@
 
     @Override
     public void destroy() {
-        super.destroy();
+        // Don't call super as this may be called before the view is dettached and calling super
+        // will remove the attach listener. We don't need to do that, because once this object is
+        // detached from the graph, it will be gc.
         mHost.removeCallback(mQSHostCallback);
 
         for (TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index a65967a..bd4c6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -31,7 +31,7 @@
 import com.android.systemui.qs.external.QSExternalModule;
 import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel;
+import com.android.systemui.qs.tiles.di.QSTilesModule;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -60,20 +60,18 @@
                 QSFlagsModule.class,
                 QSHostModule.class,
                 QSPipelineModule.class,
+                QSTilesModule.class,
         }
 )
 public interface QSModule {
 
-    /** A map of internal QS tiles. Ensures that this can be injected even if
-     * it is empty */
+    /**
+     * A map of internal QS tiles. Ensures that this can be injected even if
+     * it is empty
+     */
     @Multibinds
     Map<String, QSTileImpl<?>> tileMap();
 
-    /** A map of internal QS tile ViewModels. Ensures that this can be injected even if
-     * it is empty */
-    @Multibinds
-    Map<String, QSTileViewModel> tileViewModelMap();
-
     @Provides
     @SysUISingleton
     static AutoTileManager provideAutoTileManager(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index d09b210..0995dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -24,13 +24,11 @@
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
-import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.dagger.SysUISingleton
@@ -40,6 +38,7 @@
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlin.math.roundToInt
 import kotlinx.coroutines.launch
@@ -98,10 +97,6 @@
         var previousForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
         var previousUserSwitcher: FooterActionsButtonViewModel? = null
 
-        // Set the initial visibility on the View directly so that we don't briefly show it for a
-        // few frames before [viewModel.isVisible] is collected.
-        view.isInvisible = !viewModel.isVisible.value
-
         // Listen for ViewModel updates when the View is attached.
         view.repeatWhenAttached {
             val attachedScope = this.lifecycleScope
@@ -111,12 +106,7 @@
                 // TODO(b/242040009): Should this move somewhere else?
                 launch { viewModel.observeDeviceMonitoringDialogRequests(view.context) }
 
-                // Make sure we set the correct visibility and alpha even when QS are not currently
-                // shown.
-                launch {
-                    viewModel.isVisible.collect { isVisible -> view.isInvisible = !isVisible }
-                }
-
+                // Make sure we set the correct alphas even when QS are not currently shown.
                 launch { viewModel.alpha.collect { view.alpha = it } }
                 launch {
                     viewModel.backgroundAlpha.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index eff3e76..aff4a67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -77,14 +77,6 @@
      */
     val observeDeviceMonitoringDialogRequests: suspend (quickSettingsContext: Context) -> Unit,
 ) {
-    /**
-     * Whether the UI rendering this ViewModel should be visible. Note that even when this is false,
-     * the UI should still participate to the layout it is included in (i.e. in the View world it
-     * should be INVISIBLE, not GONE).
-     */
-    private val _isVisible = MutableStateFlow(false)
-    val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
-
     /** The alpha the UI rendering this ViewModel should have. */
     private val _alpha = MutableStateFlow(1f)
     val alpha: StateFlow<Float> = _alpha.asStateFlow()
@@ -93,10 +85,6 @@
     private val _backgroundAlpha = MutableStateFlow(1f)
     val backgroundAlpha: StateFlow<Float> = _backgroundAlpha.asStateFlow()
 
-    fun onVisibilityChangeRequested(visible: Boolean) {
-        _isVisible.value = visible
-    }
-
     /** Called when the expansion of the Quick Settings changed. */
     fun onQuickSettingsExpansionChanged(expansion: Float, isInSplitShade: Boolean) {
         if (isInSplitShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
index 056f967..d1f8945 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.base.interactor
 
 import android.content.Context
+import android.os.UserHandle
 import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
 import com.android.settingslib.RestrictedLockUtils
@@ -32,7 +33,7 @@
 
 /**
  * Provides restrictions data for the tiles. This is used in
- * [com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel] to determine if the tile is
+ * [com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl] to determine if the tile is
  * disabled based on the [com.android.systemui.qs.tiles.viewmodel.QSTileConfig.policy].
  */
 interface DisabledByPolicyInteractor {
@@ -41,7 +42,7 @@
      * Checks if the tile is restricted by the policy for a specific user. Pass the result to the
      * [handlePolicyResult] to let the user know that the tile is disable by the admin.
      */
-    suspend fun isDisabled(userId: Int, userRestriction: String?): PolicyResult
+    suspend fun isDisabled(user: UserHandle, userRestriction: String?): PolicyResult
 
     /**
      * Returns true when [policyResult] is [PolicyResult.TileDisabled] and has been handled by this
@@ -75,14 +76,14 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : DisabledByPolicyInteractor {
 
-    override suspend fun isDisabled(userId: Int, userRestriction: String?): PolicyResult =
+    override suspend fun isDisabled(user: UserHandle, userRestriction: String?): PolicyResult =
         withContext(backgroundDispatcher) {
             val admin: EnforcedAdmin =
-                restrictedLockProxy.getEnforcedAdmin(userId, userRestriction)
+                restrictedLockProxy.getEnforcedAdmin(user.identifier, userRestriction)
                     ?: return@withContext PolicyResult.TileEnabled
 
             return@withContext if (
-                !restrictedLockProxy.hasBaseUserRestriction(userId, userRestriction)
+                !restrictedLockProxy.hasBaseUserRestriction(user.identifier, userRestriction)
             ) {
                 PolicyResult.TileDisabled(admin)
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
index a3e3850..9752fea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.base.interactor
 
+import android.os.UserHandle
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 
@@ -29,13 +30,12 @@
 
     /**
      * Returns a data flow scoped to the user. This means the subscription will live when the tile
-     * is listened for the [userId]. It's cancelled when the tile is not listened or the user
-     * changes.
+     * is listened for the [user]. It's cancelled when the tile is not listened or the user changes.
      *
      * You can use [Flow.onStart] on the returned to update the tile with the current state as soon
      * as possible.
      */
-    fun tileData(userId: Int, triggers: Flow<DataUpdateTrigger>): Flow<DATA_TYPE>
+    fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<DATA_TYPE>
 
     /**
      * Returns tile availability - whether this device currently supports this tile.
@@ -43,5 +43,5 @@
      * You can use [Flow.onStart] on the returned to update the tile with the current state as soon
      * as possible.
      */
-    fun availability(userId: Int): Flow<Boolean>
+    fun availability(user: UserHandle): Flow<Boolean>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt
index 102fa36..77ff609 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt
@@ -16,11 +16,12 @@
 
 package com.android.systemui.qs.tiles.base.interactor
 
+import android.os.UserHandle
 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 
 /** @see QSTileUserActionInteractor.handleInput */
 data class QSTileInput<T>(
-    val userId: Int,
+    val user: UserHandle,
     val action: QSTileUserAction,
     val data: T,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
deleted file mode 100644
index 14de5eb..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2023 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.systemui.qs.tiles.base.viewmodel
-
-import androidx.annotation.CallSuper
-import androidx.annotation.VisibleForTesting
-import com.android.internal.util.Preconditions
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.kotlin.throttle
-import com.android.systemui.util.time.SystemClock
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelChildren
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.cancellable
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.stateIn
-
-/**
- * Provides a hassle-free way to implement new tiles according to current System UI architecture
- * standards. THis ViewModel is cheap to instantiate and does nothing until it's moved to
- * [QSTileLifecycle.ALIVE] state.
- *
- * Inject [BaseQSTileViewModel.Factory] to create a new instance of this class.
- */
-@OptIn(ExperimentalCoroutinesApi::class)
-class BaseQSTileViewModel<DATA_TYPE>
-@VisibleForTesting
-constructor(
-    override val config: QSTileConfig,
-    private val userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
-    private val tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
-    private val mapper: QSTileDataToStateMapper<DATA_TYPE>,
-    private val disabledByPolicyInteractor: DisabledByPolicyInteractor,
-    userRepository: UserRepository,
-    private val falsingManager: FalsingManager,
-    private val qsTileAnalytics: QSTileAnalytics,
-    private val qsTileLogger: QSTileLogger,
-    private val systemClock: SystemClock,
-    private val backgroundDispatcher: CoroutineDispatcher,
-    private val tileScope: CoroutineScope,
-) : QSTileViewModel {
-
-    @AssistedInject
-    constructor(
-        @Assisted config: QSTileConfig,
-        @Assisted userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
-        @Assisted tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
-        @Assisted mapper: QSTileDataToStateMapper<DATA_TYPE>,
-        disabledByPolicyInteractor: DisabledByPolicyInteractor,
-        userRepository: UserRepository,
-        falsingManager: FalsingManager,
-        qsTileAnalytics: QSTileAnalytics,
-        qsTileLogger: QSTileLogger,
-        systemClock: SystemClock,
-        @Background backgroundDispatcher: CoroutineDispatcher,
-    ) : this(
-        config,
-        userActionInteractor,
-        tileDataInteractor,
-        mapper,
-        disabledByPolicyInteractor,
-        userRepository,
-        falsingManager,
-        qsTileAnalytics,
-        qsTileLogger,
-        systemClock,
-        backgroundDispatcher,
-        CoroutineScope(SupervisorJob())
-    )
-
-    private val userIds: MutableStateFlow<Int> =
-        MutableStateFlow(userRepository.getSelectedUserInfo().id)
-    private val userInputs: MutableSharedFlow<QSTileUserAction> =
-        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
-    private val forceUpdates: MutableSharedFlow<Unit> =
-        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
-    private val spec
-        get() = config.tileSpec
-
-    private lateinit var tileData: SharedFlow<DATA_TYPE>
-
-    override lateinit var state: SharedFlow<QSTileState>
-    override val isAvailable: StateFlow<Boolean> =
-        userIds
-            .flatMapLatest { tileDataInteractor.availability(it) }
-            .flowOn(backgroundDispatcher)
-            .stateIn(
-                tileScope,
-                SharingStarted.WhileSubscribed(),
-                true,
-            )
-
-    private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
-
-    @CallSuper
-    override fun forceUpdate() {
-        Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
-        forceUpdates.tryEmit(Unit)
-    }
-
-    @CallSuper
-    override fun onUserIdChanged(userId: Int) {
-        Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
-        userIds.tryEmit(userId)
-    }
-
-    @CallSuper
-    override fun onActionPerformed(userAction: QSTileUserAction) {
-        Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
-
-        qsTileLogger.logUserAction(
-            userAction,
-            spec,
-            tileData.replayCache.isNotEmpty(),
-            state.replayCache.isNotEmpty()
-        )
-        userInputs.tryEmit(userAction)
-    }
-
-    @CallSuper
-    override fun onLifecycle(lifecycle: QSTileLifecycle) {
-        when (lifecycle) {
-            QSTileLifecycle.ALIVE -> {
-                Preconditions.checkState(currentLifeState == QSTileLifecycle.DEAD)
-                tileData = createTileDataFlow()
-                state =
-                    tileData
-                        .map { data ->
-                            mapper.map(config, data).also { state ->
-                                qsTileLogger.logStateUpdate(spec, state, data)
-                            }
-                        }
-                        .flowOn(backgroundDispatcher)
-                        .shareIn(
-                            tileScope,
-                            SharingStarted.WhileSubscribed(),
-                            replay = 1,
-                        )
-            }
-            QSTileLifecycle.DEAD -> {
-                Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
-                tileScope.coroutineContext.cancelChildren()
-            }
-        }
-        currentLifeState = lifecycle
-    }
-
-    private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
-        userIds
-            .flatMapLatest { userId ->
-                val updateTriggers =
-                    merge(
-                            userInputFlow(userId),
-                            forceUpdates
-                                .map { DataUpdateTrigger.ForceUpdate }
-                                .onEach { qsTileLogger.logForceUpdate(spec) },
-                        )
-                        .onStart {
-                            emit(DataUpdateTrigger.InitialRequest)
-                            qsTileLogger.logInitialRequest(spec)
-                        }
-                tileDataInteractor
-                    .tileData(userId, updateTriggers)
-                    .cancellable()
-                    .flowOn(backgroundDispatcher)
-            }
-            .shareIn(
-                tileScope,
-                SharingStarted.WhileSubscribed(),
-                replay = 1, // we only care about the most recent value
-            )
-
-    /**
-     * Creates a user input flow which:
-     * - filters false inputs with [falsingManager]
-     * - takes care of a tile being disable by policy using [disabledByPolicyInteractor]
-     * - notifies [userActionInteractor] about the action
-     * - logs it accordingly using [qsTileLogger] and [qsTileAnalytics]
-     *
-     * Subscribing to the result flow twice will result in doubling all actions, logs and analytics.
-     */
-    private fun userInputFlow(userId: Int): Flow<DataUpdateTrigger> {
-        return userInputs
-            .filterFalseActions()
-            .filterByPolicy(userId)
-            .throttle(CLICK_THROTTLE_DURATION, systemClock)
-            // Skip the input until there is some data
-            .mapNotNull { action ->
-                val state: QSTileState = state.replayCache.lastOrNull() ?: return@mapNotNull null
-                val data: DATA_TYPE = tileData.replayCache.lastOrNull() ?: return@mapNotNull null
-                qsTileLogger.logUserActionPipeline(spec, action, state, data)
-                qsTileAnalytics.trackUserAction(config, action)
-
-                DataUpdateTrigger.UserInput(QSTileInput(userId, action, data))
-            }
-            .onEach { userActionInteractor.handleInput(it.input) }
-            .flowOn(backgroundDispatcher)
-    }
-
-    private fun Flow<QSTileUserAction>.filterByPolicy(userId: Int): Flow<QSTileUserAction> =
-        when (config.policy) {
-            is QSTilePolicy.NoRestrictions -> this
-            is QSTilePolicy.Restricted ->
-                filter { action ->
-                    val result =
-                        disabledByPolicyInteractor.isDisabled(userId, config.policy.userRestriction)
-                    !disabledByPolicyInteractor.handlePolicyResult(result).also { isDisabled ->
-                        if (isDisabled) {
-                            qsTileLogger.logUserActionRejectedByPolicy(action, spec)
-                        }
-                    }
-                }
-        }
-
-    private fun Flow<QSTileUserAction>.filterFalseActions(): Flow<QSTileUserAction> =
-        filter { action ->
-            val isFalseAction =
-                when (action) {
-                    is QSTileUserAction.Click ->
-                        falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
-                    is QSTileUserAction.LongClick ->
-                        falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
-                }
-            if (isFalseAction) {
-                qsTileLogger.logUserActionRejectedByFalsing(action, spec)
-            }
-            !isFalseAction
-        }
-
-    private companion object {
-        const val CLICK_THROTTLE_DURATION = 200L
-    }
-
-    /**
-     * Factory interface for assisted inject. Dagger has bad time supporting generics in assisted
-     * injection factories now. That's why you need to create an interface implementing this one and
-     * annotate it with [dagger.assisted.AssistedFactory].
-     *
-     * ex: @AssistedFactory interface FooFactory : BaseQSTileViewModel.Factory<FooData>
-     */
-    interface Factory<T> {
-
-        /**
-         * @param config contains all the static information (like TileSpec) about the tile.
-         * @param userActionInteractor encapsulates user input processing logic. Use it to start
-         *   activities, show dialogs or otherwise update the tile state.
-         * @param tileDataInteractor provides [DATA_TYPE] and its availability.
-         * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View
-         *   layer. It's called in [backgroundDispatcher], so it's safe to perform long running
-         *   operations there.
-         */
-        fun create(
-            config: QSTileConfig,
-            userActionInteractor: QSTileUserActionInteractor<T>,
-            tileDataInteractor: QSTileDataInteractor<T>,
-            mapper: QSTileDataToStateMapper<T>,
-        ): BaseQSTileViewModel<T>
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
new file mode 100644
index 0000000..736f7cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.base.viewmodel
+
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.impl.di.QSTileComponent
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+
+/**
+ * Factory to create an appropriate [QSTileViewModelImpl] instance depending on your circumstances.
+ *
+ * @see [QSTileViewModelFactory.Component]
+ * @see [QSTileViewModelFactory.Static]
+ */
+sealed interface QSTileViewModelFactory<T> {
+
+    /**
+     * This factory allows you to pass an instance of [QSTileComponent] to a view model effectively
+     * binding them together. This achieves a DI scope that lives along the instance of
+     * [QSTileViewModelImpl].
+     */
+    class Component<T>
+    @Inject
+    constructor(
+        private val disabledByPolicyInteractor: DisabledByPolicyInteractor,
+        private val userRepository: UserRepository,
+        private val falsingManager: FalsingManager,
+        private val qsTileAnalytics: QSTileAnalytics,
+        private val qsTileLogger: QSTileLogger,
+        private val qsTileConfigProvider: QSTileConfigProvider,
+        private val systemClock: SystemClock,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+    ) : QSTileViewModelFactory<T> {
+
+        /**
+         * Creates [QSTileViewModelImpl] based on the interactors obtained from [component].
+         * Reference of that [component] is then stored along the view model.
+         */
+        fun create(tileSpec: TileSpec, component: QSTileComponent<T>): QSTileViewModelImpl<T> =
+            QSTileViewModelImpl(
+                qsTileConfigProvider.getConfig(tileSpec.spec),
+                component::userActionInteractor,
+                component::dataInteractor,
+                component::dataToStateMapper,
+                disabledByPolicyInteractor,
+                userRepository,
+                falsingManager,
+                qsTileAnalytics,
+                qsTileLogger,
+                systemClock,
+                backgroundDispatcher,
+            )
+    }
+
+    /**
+     * This factory passes by necessary implementations to the [QSTileViewModelImpl]. This is a
+     * default choice for most of the tiles.
+     */
+    class Static<T>
+    @Inject
+    constructor(
+        private val disabledByPolicyInteractor: DisabledByPolicyInteractor,
+        private val userRepository: UserRepository,
+        private val falsingManager: FalsingManager,
+        private val qsTileAnalytics: QSTileAnalytics,
+        private val qsTileLogger: QSTileLogger,
+        private val qsTileConfigProvider: QSTileConfigProvider,
+        private val systemClock: SystemClock,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+    ) : QSTileViewModelFactory<T> {
+
+        /**
+         * @param tileSpec of the created tile.
+         * @param userActionInteractor encapsulates user input processing logic. Use it to start
+         *   activities, show dialogs or otherwise update the tile state.
+         * @param tileDataInteractor provides [DATA_TYPE] and its availability.
+         * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View
+         *   layer. It's called in [backgroundDispatcher], so it's safe to perform long running
+         *   operations there.
+         */
+        fun create(
+            tileSpec: TileSpec,
+            userActionInteractor: QSTileUserActionInteractor<T>,
+            tileDataInteractor: QSTileDataInteractor<T>,
+            mapper: QSTileDataToStateMapper<T>,
+        ): QSTileViewModelImpl<T> =
+            QSTileViewModelImpl(
+                qsTileConfigProvider.getConfig(tileSpec.spec),
+                { userActionInteractor },
+                { tileDataInteractor },
+                { mapper },
+                disabledByPolicyInteractor,
+                userRepository,
+                falsingManager,
+                qsTileAnalytics,
+                qsTileLogger,
+                systemClock,
+                backgroundDispatcher,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
new file mode 100644
index 0000000..0bee48f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.base.viewmodel
+
+import android.os.UserHandle
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.throttle
+import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.cancellable
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides a hassle-free way to implement new tiles according to current System UI architecture
+ * standards. This ViewModel is cheap to instantiate and does nothing until its [state] is listened.
+ *
+ * Don't use this constructor directly. Instead, inject [QSTileViewModelFactory] to create a new
+ * instance of this class.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelImpl<DATA_TYPE>(
+    override val config: QSTileConfig,
+    private val userActionInteractor: () -> QSTileUserActionInteractor<DATA_TYPE>,
+    private val tileDataInteractor: () -> QSTileDataInteractor<DATA_TYPE>,
+    private val mapper: () -> QSTileDataToStateMapper<DATA_TYPE>,
+    private val disabledByPolicyInteractor: DisabledByPolicyInteractor,
+    userRepository: UserRepository,
+    private val falsingManager: FalsingManager,
+    private val qsTileAnalytics: QSTileAnalytics,
+    private val qsTileLogger: QSTileLogger,
+    private val systemClock: SystemClock,
+    private val backgroundDispatcher: CoroutineDispatcher,
+    private val tileScope: CoroutineScope = CoroutineScope(SupervisorJob()),
+) : QSTileViewModel {
+
+    private val users: MutableStateFlow<UserHandle> =
+        MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
+    private val userInputs: MutableSharedFlow<QSTileUserAction> =
+        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+    private val forceUpdates: MutableSharedFlow<Unit> =
+        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+    private val spec
+        get() = config.tileSpec
+
+    private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow()
+
+    override val state: SharedFlow<QSTileState> =
+        tileData
+            .map { data ->
+                mapper().map(config, data).also { state ->
+                    qsTileLogger.logStateUpdate(spec, state, data)
+                }
+            }
+            .flowOn(backgroundDispatcher)
+            .shareIn(
+                tileScope,
+                SharingStarted.WhileSubscribed(),
+                replay = 1,
+            )
+    override val isAvailable: StateFlow<Boolean> =
+        users
+            .flatMapLatest { tileDataInteractor().availability(it) }
+            .flowOn(backgroundDispatcher)
+            .stateIn(
+                tileScope,
+                SharingStarted.WhileSubscribed(),
+                true,
+            )
+
+    override fun forceUpdate() {
+        forceUpdates.tryEmit(Unit)
+    }
+
+    override fun onUserChanged(user: UserHandle) {
+        users.tryEmit(user)
+    }
+
+    override fun onActionPerformed(userAction: QSTileUserAction) {
+        qsTileLogger.logUserAction(
+            userAction,
+            spec,
+            tileData.replayCache.isNotEmpty(),
+            state.replayCache.isNotEmpty()
+        )
+        userInputs.tryEmit(userAction)
+    }
+
+    override fun destroy() {
+        tileScope.cancel()
+    }
+
+    private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
+        users
+            .flatMapLatest { user ->
+                val updateTriggers =
+                    merge(
+                            userInputFlow(user),
+                            forceUpdates
+                                .map { DataUpdateTrigger.ForceUpdate }
+                                .onEach { qsTileLogger.logForceUpdate(spec) },
+                        )
+                        .onStart {
+                            emit(DataUpdateTrigger.InitialRequest)
+                            qsTileLogger.logInitialRequest(spec)
+                        }
+                tileDataInteractor()
+                    .tileData(user, updateTriggers)
+                    .cancellable()
+                    .flowOn(backgroundDispatcher)
+            }
+            .shareIn(
+                tileScope,
+                SharingStarted.WhileSubscribed(),
+                replay = 1, // we only care about the most recent value
+            )
+
+    /**
+     * Creates a user input flow which:
+     * - filters false inputs with [falsingManager]
+     * - takes care of a tile being disable by policy using [disabledByPolicyInteractor]
+     * - notifies [userActionInteractor] about the action
+     * - logs it accordingly using [qsTileLogger] and [qsTileAnalytics]
+     *
+     * Subscribing to the result flow twice will result in doubling all actions, logs and analytics.
+     */
+    private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> {
+        return userInputs
+            .filterFalseActions()
+            .filterByPolicy(user)
+            .throttle(CLICK_THROTTLE_DURATION, systemClock)
+            // Skip the input until there is some data
+            .mapNotNull { action ->
+                val state: QSTileState = state.replayCache.lastOrNull() ?: return@mapNotNull null
+                val data: DATA_TYPE = tileData.replayCache.lastOrNull() ?: return@mapNotNull null
+                qsTileLogger.logUserActionPipeline(spec, action, state, data)
+                qsTileAnalytics.trackUserAction(config, action)
+
+                DataUpdateTrigger.UserInput(QSTileInput(user, action, data))
+            }
+            .onEach { userActionInteractor().handleInput(it.input) }
+            .flowOn(backgroundDispatcher)
+    }
+
+    private fun Flow<QSTileUserAction>.filterByPolicy(user: UserHandle): Flow<QSTileUserAction> =
+        config.policy.let { policy ->
+            when (policy) {
+                is QSTilePolicy.NoRestrictions -> this@filterByPolicy
+                is QSTilePolicy.Restricted ->
+                    filter { action ->
+                        val result =
+                            disabledByPolicyInteractor.isDisabled(user, policy.userRestriction)
+                        !disabledByPolicyInteractor.handlePolicyResult(result).also { isDisabled ->
+                            if (isDisabled) {
+                                qsTileLogger.logUserActionRejectedByPolicy(action, spec)
+                            }
+                        }
+                    }
+            }
+        }
+
+    private fun Flow<QSTileUserAction>.filterFalseActions(): Flow<QSTileUserAction> =
+        filter { action ->
+            val isFalseAction =
+                when (action) {
+                    is QSTileUserAction.Click ->
+                        falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
+                    is QSTileUserAction.LongClick ->
+                        falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
+                }
+            if (isFalseAction) {
+                qsTileLogger.logUserActionRejectedByFalsing(action, spec)
+            }
+            !isFalseAction
+        }
+
+    private companion object {
+        const val CLICK_THROTTLE_DURATION = 200L
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index d0809c5..7d7af64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.qs.QSFactory
 import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
 import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
 import javax.inject.Inject
@@ -30,15 +30,22 @@
 class NewQSTileFactory
 @Inject
 constructor(
+    qsTileConfigProvider: QSTileConfigProvider,
     private val adapterFactory: QSTileViewModelAdapter.Factory,
     private val tileMap:
         Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
 ) : QSFactory {
 
+    init {
+        for (viewModelTileSpec in tileMap.keys) {
+            // throws an exception when there is no config for a tileSpec of an injected viewModel
+            qsTileConfigProvider.getConfig(viewModelTileSpec)
+        }
+    }
+
     override fun createTile(tileSpec: String): QSTile? =
         tileMap[tileSpec]?.let {
             val tile = it.get()
-            tile.onLifecycle(QSTileLifecycle.ALIVE)
             adapterFactory.create(tile)
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
new file mode 100644
index 0000000..32522ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.di
+
+import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.Multibinds
+
+/** Module listing subcomponents */
+@Module(
+    subcomponents =
+        [
+            CustomTileComponent::class,
+        ]
+)
+interface QSTilesModule {
+
+    /**
+     * A map of internal QS tile ViewModels. Ensures that this can be injected even if it is empty
+     */
+    @Multibinds fun tileViewModelConfigs(): Map<String, QSTileConfig>
+
+    /**
+     * A map of internal QS tile ViewModels. Ensures that this can be injected even if it is empty
+     */
+    @Multibinds fun tileViewModelMap(): Map<String, QSTileViewModel>
+
+    @Binds fun bindQSTileConfigProvider(impl: QSTileConfigProviderImpl): QSTileConfigProvider
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
index 8957fc3..9c63a30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.dialog.bluetooth
 
+import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothAdapter.STATE_OFF
 import android.bluetooth.BluetoothAdapter.STATE_ON
 import com.android.settingslib.bluetooth.BluetoothCallback
@@ -37,6 +38,7 @@
 @Inject
 constructor(
     private val localBluetoothManager: LocalBluetoothManager?,
+    private val logger: BluetoothTileDialogLogger,
     @Application private val coroutineScope: CoroutineScope,
 ) {
 
@@ -47,6 +49,10 @@
                         override fun onBluetoothStateChanged(bluetoothState: Int) {
                             if (bluetoothState == STATE_ON || bluetoothState == STATE_OFF) {
                                 super.onBluetoothStateChanged(bluetoothState)
+                                logger.logBluetoothState(
+                                    BluetoothStateStage.BLUETOOTH_STATE_CHANGE_RECEIVED,
+                                    BluetoothAdapter.nameForState(bluetoothState)
+                                )
                                 trySendWithFailureLogging(
                                     bluetoothState == STATE_ON,
                                     TAG,
@@ -70,6 +76,10 @@
             if (isBluetoothEnabled != value) {
                 localBluetoothManager?.bluetoothAdapter?.apply {
                     if (value) enable() else disable()
+                    logger.logBluetoothState(
+                        BluetoothStateStage.BLUETOOTH_STATE_VALUE_SET,
+                        value.toString()
+                    )
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 80af76d..7b4b557 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.time.SystemClock
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asSharedFlow
@@ -46,7 +47,9 @@
     private val bluetoothToggleInitialValue: Boolean,
     private val subtitleResIdInitialValue: Int,
     private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
+    private val systemClock: SystemClock,
     private val uiEventLogger: UiEventLogger,
+    private val logger: BluetoothTileDialogLogger,
     context: Context,
 ) : SystemUIDialog(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK) {
 
@@ -75,7 +78,10 @@
         super.onCreate(savedInstanceState)
         uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
 
-        setContentView(LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null))
+        LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null).apply {
+            accessibilityPaneTitle = context.getText(R.string.accessibility_desc_quick_settings)
+            setContentView(this)
+        }
 
         toggleView = requireViewById(R.id.bluetooth_toggle)
         subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView
@@ -102,21 +108,32 @@
         showSeeAll: Boolean,
         showPairNewDevice: Boolean
     ) {
+        val start = systemClock.elapsedRealtime()
         deviceItemAdapter.refreshDeviceItemList(deviceItem) {
             seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
             pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+            logger.logDeviceUiUpdate(systemClock.elapsedRealtime() - start)
         }
     }
 
     internal fun onBluetoothStateUpdated(isEnabled: Boolean, subtitleResId: Int) {
-        toggleView.isChecked = isEnabled
+        toggleView.apply {
+            isChecked = isEnabled
+            setEnabled(true)
+            alpha = ENABLED_ALPHA
+        }
         subtitleTextView.text = context.getString(subtitleResId)
     }
 
     private fun setupToggle() {
         toggleView.isChecked = bluetoothToggleInitialValue
-        toggleView.setOnCheckedChangeListener { _, isChecked ->
+        toggleView.setOnCheckedChangeListener { view, isChecked ->
             mutableBluetoothStateToggle.value = isChecked
+            view.apply {
+                isEnabled = false
+                alpha = DISABLED_ALPHA
+            }
+            logger.logBluetoothState(BluetoothStateStage.USER_TOGGLED, isChecked.toString())
             uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TOGGLE_CLICKED)
         }
     }
@@ -219,5 +236,7 @@
         const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
             "com.android.settings.PREVIOUSLY_CONNECTED_DEVICE"
         const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
+        const val DISABLED_ALPHA = 0.3f
+        const val ENABLED_ALPHA = 1f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogLogger.kt
new file mode 100644
index 0000000..5d18dc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogLogger.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.dialog.bluetooth
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.dagger.BluetoothTileDialogLog
+import javax.inject.Inject
+
+private const val TAG = "BluetoothTileDialogLog"
+
+enum class BluetoothStateStage {
+    USER_TOGGLED,
+    BLUETOOTH_STATE_VALUE_SET,
+    BLUETOOTH_STATE_CHANGE_RECEIVED
+}
+
+enum class DeviceFetchTrigger {
+    FIRST_LOAD,
+    BLUETOOTH_STATE_CHANGE_RECEIVED,
+    BLUETOOTH_CALLBACK_RECEIVED
+}
+
+enum class JobStatus {
+    FINISHED,
+    CANCELLED
+}
+
+class BluetoothTileDialogLogger
+@Inject
+constructor(@BluetoothTileDialogLog private val logBuffer: LogBuffer) {
+
+    fun logBluetoothState(stage: BluetoothStateStage, state: String) =
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = stage.toString()
+                str2 = state
+            },
+            { "BluetoothState. stage=$str1 state=$str2" }
+        )
+
+    fun logDeviceClick(address: String, type: DeviceItemType) =
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = address
+                str2 = type.toString()
+            },
+            { "DeviceClick. address=$str1 type=$str2" }
+        )
+
+    fun logActiveDeviceChanged(address: String?, profileId: Int) =
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = address
+                int1 = profileId
+            },
+            { "ActiveDeviceChanged. address=$str1 profileId=$int1" }
+        )
+
+    fun logProfileConnectionStateChanged(address: String, state: String, profileId: Int) =
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = address
+                str2 = state
+                int1 = profileId
+            },
+            { "ProfileConnectionStateChanged. address=$str1 state=$str2 profileId=$int1" }
+        )
+
+    fun logDeviceFetch(status: JobStatus, trigger: DeviceFetchTrigger, duration: Long) =
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = status.toString()
+                str2 = trigger.toString()
+                long1 = duration
+            },
+            { "DeviceFetch. status=$str1 trigger=$str2 duration=$long1" }
+        )
+
+    fun logDeviceUiUpdate(duration: Long) =
+        logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" })
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
index 2865ad7..86e5dde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
@@ -28,7 +28,10 @@
     @UiEvent(doc = "Gear icon clicked") DEVICE_GEAR_CLICKED(1497),
     @UiEvent(doc = "Device clicked") DEVICE_CLICKED(1498),
     @UiEvent(doc = "Connected device clicked to active") CONNECTED_DEVICE_SET_ACTIVE(1499),
-    @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500);
+    @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500),
+    @UiEvent(doc = "Active device clicked to disconnect") ACTIVE_DEVICE_DISCONNECT(1507),
+    @UiEvent(doc = "Connected other device clicked to disconnect")
+    CONNECTED_OTHER_DEVICE_DISCONNECT(1508);
 
     override fun getId() = metricId
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 8e27493..f7e0de3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -35,10 +35,12 @@
 import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.MAX_DEVICE_ITEM_ENTRY
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -53,7 +55,9 @@
     private val bluetoothStateInteractor: BluetoothStateInteractor,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
     private val activityStarter: ActivityStarter,
+    private val systemClock: SystemClock,
     private val uiEventLogger: UiEventLogger,
+    private val logger: BluetoothTileDialogLogger,
     @Application private val coroutineScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
 ) : BluetoothTileDialogCallback {
@@ -90,7 +94,11 @@
                 }
                     ?: dialog!!.show()
                 updateDeviceItemJob?.cancel()
-                updateDeviceItemJob = launch { deviceItemInteractor.updateDeviceItems(context) }
+                updateDeviceItemJob = launch {
+                    // Add a slight delay for smoother dialog bounds change
+                    delay(FIRST_LOAD_DELAY_MS)
+                    deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
+                }
 
                 bluetoothStateInteractor.bluetoothStateUpdate
                     .filterNotNull()
@@ -98,7 +106,10 @@
                         dialog!!.onBluetoothStateUpdated(it, getSubtitleResId(it))
                         updateDeviceItemJob?.cancel()
                         updateDeviceItemJob = launch {
-                            deviceItemInteractor.updateDeviceItems(context)
+                            deviceItemInteractor.updateDeviceItems(
+                                context,
+                                DeviceFetchTrigger.BLUETOOTH_STATE_CHANGE_RECEIVED
+                            )
                         }
                     }
                     .launchIn(this)
@@ -107,7 +118,10 @@
                     .onEach {
                         updateDeviceItemJob?.cancel()
                         updateDeviceItemJob = launch {
-                            deviceItemInteractor.updateDeviceItems(context)
+                            deviceItemInteractor.updateDeviceItems(
+                                context,
+                                DeviceFetchTrigger.BLUETOOTH_CALLBACK_RECEIVED
+                            )
                         }
                     }
                     .launchIn(this)
@@ -139,7 +153,9 @@
                 bluetoothStateInteractor.isBluetoothEnabled,
                 getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled),
                 this@BluetoothTileDialogViewModel,
+                systemClock,
                 uiEventLogger,
+                logger,
                 context
             )
             .apply { SystemUIDialog.registerDismissListener(this) { dismissDialog() } }
@@ -189,6 +205,7 @@
 
     companion object {
         private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
+        private const val FIRST_LOAD_DELAY_MS = 500L
         private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
             if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
             else R.string.bt_is_off
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
index 50eaf38..2c8d2a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
@@ -36,6 +36,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 
 enum class DeviceItemType {
+    ACTIVE_MEDIA_BLUETOOTH_DEVICE,
     AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
     CONNECTED_BLUETOOTH_DEVICE,
     SAVED_BLUETOOTH_DEVICE,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
index 8c22614..7bb1619 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
@@ -39,12 +39,38 @@
     abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem
 }
 
+internal class ActiveMediaDeviceItemFactory : DeviceItemFactory() {
+    override fun isFilterMatched(
+        cachedDevice: CachedBluetoothDevice,
+        audioManager: AudioManager?
+    ): Boolean {
+        return BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
+            BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
+    }
+
+    override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
+        return DeviceItem(
+            type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+            cachedBluetoothDevice = cachedDevice,
+            deviceName = cachedDevice.name,
+            connectionSummary = cachedDevice.connectionSummary ?: "",
+            iconWithDescription =
+                BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
+                    Pair(p.first, p.second)
+                },
+            background = backgroundOn,
+            isEnabled = !cachedDevice.isBusy,
+        )
+    }
+}
+
 internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
     override fun isFilterMatched(
         cachedDevice: CachedBluetoothDevice,
         audioManager: AudioManager?
     ): Boolean {
-        return BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
+        return !BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
+            BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
     }
 
     // TODO(b/298124674): move create() to the abstract class to reduce duplicate code
@@ -59,7 +85,7 @@
                 BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
                     Pair(p.first, p.second)
                 },
-            background = backgroundOn,
+            background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
             isEnabled = !cachedDevice.isBusy,
         )
     }
@@ -84,7 +110,7 @@
                 BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
                     Pair(p.first, p.second)
                 },
-            background = backgroundOn,
+            background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
             isEnabled = !cachedDevice.isBusy,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index e196c6c..76fbf8e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -22,7 +22,6 @@
 import android.media.AudioManager
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.bluetooth.BluetoothCallback
-import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -30,6 +29,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -39,6 +39,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.isActive
 import kotlinx.coroutines.withContext
 
 /** Holds business logic for the Bluetooth Dialog after clicking on the Bluetooth QS tile. */
@@ -50,7 +51,9 @@
     private val audioManager: AudioManager,
     private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter(),
     private val localBluetoothManager: LocalBluetoothManager?,
+    private val systemClock: SystemClock,
     private val uiEventLogger: UiEventLogger,
+    private val logger: BluetoothTileDialogLogger,
     @Application private val coroutineScope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
@@ -69,22 +72,10 @@
                             bluetoothProfile: Int
                         ) {
                             super.onActiveDeviceChanged(activeDevice, bluetoothProfile)
+                            logger.logActiveDeviceChanged(activeDevice?.address, bluetoothProfile)
                             trySendWithFailureLogging(Unit, TAG, "onActiveDeviceChanged")
                         }
 
-                        override fun onConnectionStateChanged(
-                            cachedDevice: CachedBluetoothDevice?,
-                            state: Int
-                        ) {
-                            super.onConnectionStateChanged(cachedDevice, state)
-                            trySendWithFailureLogging(Unit, TAG, "onConnectionStateChanged")
-                        }
-
-                        override fun onDeviceAdded(cachedDevice: CachedBluetoothDevice) {
-                            super.onDeviceAdded(cachedDevice)
-                            trySendWithFailureLogging(Unit, TAG, "onDeviceAdded")
-                        }
-
                         override fun onProfileConnectionStateChanged(
                             cachedDevice: CachedBluetoothDevice,
                             state: Int,
@@ -95,8 +86,24 @@
                                 state,
                                 bluetoothProfile
                             )
+                            logger.logProfileConnectionStateChanged(
+                                cachedDevice.address,
+                                state.toString(),
+                                bluetoothProfile
+                            )
                             trySendWithFailureLogging(Unit, TAG, "onProfileConnectionStateChanged")
                         }
+
+                        override fun onAclConnectionStateChanged(
+                            cachedDevice: CachedBluetoothDevice,
+                            state: Int
+                        ) {
+                            super.onAclConnectionStateChanged(cachedDevice, state)
+                            // Listen only when a device is disconnecting
+                            if (state == 0) {
+                                trySendWithFailureLogging(Unit, TAG, "onAclConnectionStateChanged")
+                            }
+                        }
                     }
                 localBluetoothManager?.eventManager?.registerCallback(listener)
                 awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) }
@@ -105,6 +112,7 @@
 
     private var deviceItemFactoryList: List<DeviceItemFactory> =
         listOf(
+            ActiveMediaDeviceItemFactory(),
             AvailableMediaDeviceItemFactory(),
             ConnectedDeviceItemFactory(),
             SavedDeviceItemFactory()
@@ -112,14 +120,16 @@
 
     private var displayPriority: List<DeviceItemType> =
         listOf(
+            DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
             DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
             DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
             DeviceItemType.SAVED_BLUETOOTH_DEVICE,
         )
 
-    internal suspend fun updateDeviceItems(context: Context) {
+    internal suspend fun updateDeviceItems(context: Context, trigger: DeviceFetchTrigger) {
         withContext(backgroundDispatcher) {
-            mutableDeviceItemUpdate.tryEmit(
+            val start = systemClock.elapsedRealtime()
+            val deviceItems =
                 bluetoothTileDialogRepository.cachedDevices
                     .mapNotNull { cachedDevice ->
                         deviceItemFactoryList
@@ -127,7 +137,22 @@
                             ?.create(context, cachedDevice)
                     }
                     .sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices)
-            )
+
+            // Only emit when the job is not cancelled
+            if (isActive) {
+                mutableDeviceItemUpdate.tryEmit(deviceItems)
+                logger.logDeviceFetch(
+                    JobStatus.FINISHED,
+                    trigger,
+                    systemClock.elapsedRealtime() - start
+                )
+            } else {
+                logger.logDeviceFetch(
+                    JobStatus.CANCELLED,
+                    trigger,
+                    systemClock.elapsedRealtime() - start
+                )
+            }
         }
     }
 
@@ -144,17 +169,26 @@
     }
 
     internal fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
-        when (deviceItem.type) {
-            DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
-                if (!BluetoothUtils.isActiveMediaDevice(deviceItem.cachedBluetoothDevice)) {
-                    deviceItem.cachedBluetoothDevice.setActive()
+        logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+
+        deviceItem.cachedBluetoothDevice.apply {
+            when (deviceItem.type) {
+                DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
+                    disconnect()
+                    uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
+                }
+                DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
+                    setActive()
                     uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
                 }
-            }
-            DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {}
-            DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
-                deviceItem.cachedBluetoothDevice.connect()
-                uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+                DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
+                    disconnect()
+                    uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT)
+                }
+                DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
+                    connect()
+                    uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
new file mode 100644
index 0000000..bb5a229
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
+
+data class CustomTileData(
+    val user: UserHandle,
+    val componentName: ComponentName,
+    val tile: Tile,
+    val callingAppUid: Int,
+    val isActive: Boolean,
+    val hasPendingBind: Boolean,
+    val shouldShowChevron: Boolean,
+    val defaultTileLabel: CharSequence?,
+    val defaultTileIcon: Icon?,
+    val component: CustomTileBoundComponent,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
new file mode 100644
index 0000000..761274e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom
+
+import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@QSTileScope
+class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileData> {
+
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>
+    ): Flow<CustomTileData> {
+        TODO("Not yet implemented")
+    }
+
+    override fun availability(user: UserHandle): Flow<Boolean> {
+        TODO("Not yet implemented")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
new file mode 100644
index 0000000..f7bec02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom
+
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import javax.inject.Inject
+
+@QSTileScope
+class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileData> {
+
+    override fun map(config: QSTileConfig, data: CustomTileData): QSTileState {
+        TODO("Not yet implemented")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
new file mode 100644
index 0000000..6c1c1a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom
+
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import javax.inject.Inject
+
+@QSTileScope
+class CustomTileUserActionInteractor @Inject constructor() :
+    QSTileUserActionInteractor<CustomTileData> {
+
+    override suspend fun handleInput(input: QSTileInput<CustomTileData>) {
+        TODO("Not yet implemented")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
new file mode 100644
index 0000000..01df906
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom.di
+
+import com.android.systemui.qs.tiles.impl.di.QSTileComponent
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import dagger.Subcomponent
+
+@QSTileScope
+@Subcomponent(modules = [QSTileConfigModule::class, CustomTileModule::class])
+interface CustomTileComponent : QSTileComponent<Any> {
+
+    @Subcomponent.Builder
+    interface Builder {
+
+        fun qsTileConfigModule(module: QSTileConfigModule): Builder
+
+        fun build(): CustomTileComponent
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
new file mode 100644
index 0000000..ccff8af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom.di
+
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.custom.CustomTileData
+import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
+import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
+import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
+import dagger.Binds
+import dagger.Module
+
+/** Provides bindings for QSTile interfaces */
+@Module(subcomponents = [CustomTileBoundComponent::class])
+interface CustomTileModule {
+
+    @Binds
+    fun bindDataInteractor(
+        dataInteractor: CustomTileInteractor
+    ): QSTileDataInteractor<CustomTileData>
+
+    @Binds
+    fun bindUserActionInteractor(
+        userActionInteractor: CustomTileUserActionInteractor
+    ): QSTileUserActionInteractor<CustomTileData>
+
+    @Binds
+    fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileData>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt
new file mode 100644
index 0000000..558fb64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom.di
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import dagger.Module
+import dagger.Provides
+
+/**
+ * Provides [QSTileConfig] and [TileSpec]. To be used along in a QS tile scoped component
+ * implementing [com.android.systemui.qs.tiles.impl.di.QSTileComponent]. In that case it makes it
+ * possible to inject config and tile spec associated with the current tile
+ */
+@Module
+class QSTileConfigModule(private val config: QSTileConfig) {
+
+    @Provides fun provideConfig(): QSTileConfig = config
+
+    @Provides fun provideTileSpec(): TileSpec = config.tileSpec
+
+    @Provides
+    fun provideCustomTileSpec(): TileSpec.CustomTileSpec =
+        config.tileSpec as TileSpec.CustomTileSpec
+
+    @Provides
+    fun providePlatformTileSpec(): TileSpec.PlatformTileSpec =
+        config.tileSpec as TileSpec.PlatformTileSpec
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
new file mode 100644
index 0000000..e33b3e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.custom.di.bound
+
+import android.os.UserHandle
+import dagger.BindsInstance
+import dagger.Subcomponent
+import kotlinx.coroutines.CoroutineScope
+
+/** @see CustomTileBoundScope */
+@CustomTileBoundScope
+@Subcomponent
+interface CustomTileBoundComponent {
+
+    @Subcomponent.Builder
+    interface Builder {
+        @BindsInstance fun user(@CustomTileUser user: UserHandle): Builder
+        @BindsInstance fun coroutineScope(@CustomTileBoundScope scope: CoroutineScope): Builder
+
+        fun build(): CustomTileBoundComponent
+    }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptStatusListener.aidl b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
similarity index 60%
rename from core/java/android/hardware/biometrics/IBiometricPromptStatusListener.aidl
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
index 7a0f438..4a4ba2b 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptStatusListener.aidl
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics;
+package com.android.systemui.qs.tiles.impl.custom.di.bound
+
+import javax.inject.Scope
 
 /**
- * Communication channel to propagate biometric prompt status. Implementation of this interface
- * should be registered in BiometricService#registerBiometricPromptStatusListener.
- * @hide
+ * Scope annotation for bound custom tile scope. This scope lives when a particular
+ * [com.android.systemui.qs.external.CustomTile] is listening and bound to the
+ * [android.service.quicksettings.TileService].
  */
-oneway interface IBiometricPromptStatusListener {
-    void onBiometricPromptShowing();
-    void onBiometricPromptIdle();
-}
\ No newline at end of file
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+@Scope
+annotation class CustomTileBoundScope
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
index 6d7c576..efc7431 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.impl.custom.di.bound
 
-enum class QSTileLifecycle {
-    ALIVE,
-    DEAD,
-}
+import javax.inject.Qualifier
+
+/** User associated with current custom tile binding. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class CustomTileUser
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
new file mode 100644
index 0000000..b3d916a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.di
+
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+
+/**
+ * Base QS tile component. It should be used with [QSTileScope] to create a custom tile scoped
+ * component. Pass this component to
+ * [com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory.Component].
+ */
+interface QSTileComponent<T> {
+
+    fun dataInteractor(): QSTileDataInteractor<T>
+
+    fun userActionInteractor(): QSTileUserActionInteractor<T>
+
+    fun dataToStateMapper(): QSTileDataToStateMapper<T>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt
new file mode 100644
index 0000000..a412de3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.impl.di
+
+import javax.inject.Scope
+
+/**
+ * Scope annotation for QS tiles. This scope is created for each tile and is disposed when the tile
+ * is no longer needed (ex. it's removed from QS). So, it lives along the instance of
+ * [com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl]. This doesn't align with tile
+ * visibility. For example, the tile scope survives shade open/close.
+ */
+@MustBeDocumented @Retention(AnnotationRetention.RUNTIME) @Scope annotation class QSTileScope
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index 4a3bcae..c4d7dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -16,20 +16,49 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
+import android.content.res.Resources
+import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
 import com.android.internal.logging.InstanceId
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
 data class QSTileConfig(
     val tileSpec: TileSpec,
-    val tileIcon: Icon,
-    @StringRes val tileLabelRes: Int,
+    val uiConfig: QSTileUIConfig,
     val instanceId: InstanceId,
     val metricsSpec: String = tileSpec.spec,
     val policy: QSTilePolicy = QSTilePolicy.NoRestrictions,
 )
 
+/**
+ * Static tile icon and label to be used when the fully operational tile isn't needed (ex. in edit
+ * mode). Icon and label are resources to better support config/locale changes.
+ */
+sealed interface QSTileUIConfig {
+
+    val tileIconRes: Int
+        @DrawableRes get
+    val tileLabelRes: Int
+        @StringRes get
+
+    /**
+     * Represents the absence of static UI state. This should be avoided by platform tiles in favour
+     * of [Resource]. Returns [Resources.ID_NULL] for each field.
+     */
+    data object Empty : QSTileUIConfig {
+        override val tileIconRes: Int
+            get() = Resources.ID_NULL
+        override val tileLabelRes: Int
+            get() = Resources.ID_NULL
+    }
+
+    /** Config containing actual icon and label resources. */
+    data class Resource(
+        @StringRes override val tileIconRes: Int,
+        @StringRes override val tileLabelRes: Int,
+    ) : QSTileUIConfig
+}
+
 /** Represents policy restrictions that may be imposed on the tile. */
 sealed interface QSTilePolicy {
     /** Tile has no policy restrictions */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
new file mode 100644
index 0000000..3f3b94e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.viewmodel
+
+import com.android.internal.util.Preconditions
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+interface QSTileConfigProvider {
+
+    /**
+     * Returns a [QSTileConfig] for a [tileSpec] or throws [IllegalArgumentException] if there is no
+     * config for such [tileSpec].
+     */
+    fun getConfig(tileSpec: String): QSTileConfig
+}
+
+@SysUISingleton
+class QSTileConfigProviderImpl @Inject constructor(private val configs: Map<String, QSTileConfig>) :
+    QSTileConfigProvider {
+
+    init {
+        for (entry in configs.entries) {
+            val configTileSpec = entry.value.tileSpec.spec
+            val keyTileSpec = entry.key
+            Preconditions.checkArgument(
+                configTileSpec == keyTileSpec,
+                "A wrong config is injected keySpec=$keyTileSpec configSpec=$configTileSpec"
+            )
+        }
+    }
+
+    override fun getConfig(tileSpec: String): QSTileConfig =
+        configs[tileSpec] ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index e5cb7ea..580c421 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
+import android.os.UserHandle
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
 
@@ -29,38 +30,32 @@
  */
 interface QSTileViewModel {
 
-    /**
-     * State of the tile to be shown by the view. It's guaranteed that it's only accessed between
-     * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD].
-     */
+    /** State of the tile to be shown by the view. */
     val state: SharedFlow<QSTileState>
 
     val config: QSTileConfig
 
-    /**
-     * Specifies whether this device currently supports this tile. This might be called outside of
-     * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds (for example in Edit Mode).
-     */
+    /** Specifies whether this device currently supports this tile. */
     val isAvailable: StateFlow<Boolean>
 
     /**
-     * Handles ViewModel lifecycle. Implementations should be inactive outside of
-     * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds.
-     */
-    fun onLifecycle(lifecycle: QSTileLifecycle)
-
-    /**
      * Notifies about the user change. Implementations should avoid using 3rd party userId sources
      * and use this value instead. This is to maintain consistent and concurrency-free behaviour
      * across different parts of QS.
      */
-    fun onUserIdChanged(userId: Int)
+    fun onUserChanged(user: UserHandle)
 
     /** Triggers the emission of the new [QSTileState] in a [state]. */
     fun forceUpdate()
 
     /** Notifies underlying logic about user input. */
     fun onActionPerformed(userAction: QSTileUserAction)
+
+    /**
+     * Frees the resources held by this view model. Call it when you no longer need the instance,
+     * because there is no guarantee it will work as expected beyond this point.
+     */
+    fun destroy()
 }
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 33f55ab..efa6da7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.viewmodel
 
 import android.content.Context
+import android.os.UserHandle
 import android.util.Log
 import android.view.View
 import androidx.annotation.GuardedBy
@@ -134,7 +135,7 @@
         qsTileViewModel.currentState?.supportedActions?.contains(action) == true
 
     override fun userSwitch(currentUser: Int) {
-        qsTileViewModel.onUserIdChanged(currentUser)
+        qsTileViewModel.onUserChanged(UserHandle.of(currentUser))
     }
 
     @Deprecated(
@@ -180,7 +181,7 @@
     override fun destroy() {
         stateJob?.cancel()
         availabilityJob?.cancel()
-        qsTileViewModel.onLifecycle(QSTileLifecycle.DEAD)
+        qsTileViewModel.destroy()
     }
 
     override fun getState(): QSTile.State? =
@@ -188,7 +189,13 @@
 
     override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
     override fun getTileLabel(): CharSequence =
-        context.getString(qsTileViewModel.config.tileLabelRes)
+        with(qsTileViewModel.config.uiConfig) {
+            when (this) {
+                is QSTileUIConfig.Empty -> qsTileViewModel.currentState?.label ?: ""
+                is QSTileUIConfig.Resource -> context.getString(tileLabelRes)
+            }
+        }
+
     override fun getTileSpec(): String = qsTileViewModel.config.tileSpec.spec
 
     private companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index f0650d3..9ba02b1 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.scene.shared.flag
 
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.FeatureFlags
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -47,15 +49,15 @@
 class SceneContainerFlagsImpl
 @AssistedInject
 constructor(
-    private val featureFlags: FeatureFlagsClassic,
+    private val featureFlagsClassic: FeatureFlagsClassic,
+    featureFlags: FeatureFlags,
     @Assisted private val isComposeAvailable: Boolean,
 ) : SceneContainerFlags {
 
     companion object {
         @VisibleForTesting
-        val flags: List<Flag<Boolean>> =
+        val classicFlagTokens: List<Flag<Boolean>> =
             listOf(
-                Flags.SCENE_CONTAINER,
                 Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA,
                 Flags.MIGRATE_LOCK_ICON,
                 Flags.MIGRATE_NSSL,
@@ -67,7 +69,13 @@
 
     /** The list of requirements, all must be met for the feature to be enabled. */
     private val requirements =
-        flags.map { FlagMustBeEnabled(it) } +
+        listOf(
+            AconfigFlagMustBeEnabled(
+                flagName = AConfigFlags.FLAG_SCENE_CONTAINER,
+                flagValue = featureFlags.sceneContainer(),
+            ),
+        ) +
+            classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
             listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
 
     override fun isEnabled(): Boolean {
@@ -115,14 +123,25 @@
 
         override fun isMet(): Boolean {
             return when (flag) {
-                is ResourceBooleanFlag -> featureFlags.isEnabled(flag)
-                is ReleasedFlag -> featureFlags.isEnabled(flag)
-                is UnreleasedFlag -> featureFlags.isEnabled(flag)
+                is ResourceBooleanFlag -> featureFlagsClassic.isEnabled(flag)
+                is ReleasedFlag -> featureFlagsClassic.isEnabled(flag)
+                is UnreleasedFlag -> featureFlagsClassic.isEnabled(flag)
                 else -> error("Unsupported flag type ${flag.javaClass}")
             }
         }
     }
 
+    private inner class AconfigFlagMustBeEnabled(
+        flagName: String,
+        private val flagValue: Boolean,
+    ) : Requirement {
+        override val name: String = "Aconfig flag $flagName must be enabled"
+
+        override fun isMet(): Boolean {
+            return flagValue
+        }
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ea1205a..05f125f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -163,8 +163,7 @@
         }
 
         mMediaProjectionMetricsLogger.notifyProjectionInitiated(
-                mUserContextProvider.getUserContext().getUserId(),
-                SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+                getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
 
         return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
                 ? new ScreenRecordPermissionDialog(
@@ -174,7 +173,8 @@
                         /* controller= */ this,
                         activityStarter,
                         mUserContextProvider,
-                        onStartRecordingClicked)
+                        onStartRecordingClicked,
+                        mMediaProjectionMetricsLogger)
                 : new ScreenRecordDialog(
                         context,
                         /* controller= */ this,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f2e94e9..e7481cc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -66,8 +66,10 @@
     private Switch mAudioSwitch;
     private Spinner mOptions;
 
-    public ScreenRecordDialog(Context context, RecordingController controller,
-            UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
+    public ScreenRecordDialog(Context context,
+                              RecordingController controller,
+                              UserContextProvider userContextProvider,
+                              @Nullable Runnable onStartRecordingClicked) {
         super(context);
         mController = controller;
         mUserContextProvider = userContextProvider;
@@ -89,7 +91,6 @@
 
         TextView cancelBtn = findViewById(R.id.button_cancel);
         cancelBtn.setOnClickListener(v -> dismiss());
-
         TextView startBtn = findViewById(R.id.button_start);
         startBtn.setOnClickListener(v -> {
             if (mOnStartRecordingClicked != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 3b3aa53..f74234b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -33,6 +33,7 @@
 import android.widget.Switch
 import androidx.annotation.LayoutRes
 import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
@@ -50,12 +51,15 @@
     private val controller: RecordingController,
     private val activityStarter: ActivityStarter,
     private val userContextProvider: UserContextProvider,
-    private val onStartRecordingClicked: Runnable?
+    private val onStartRecordingClicked: Runnable?,
+    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
 ) :
     BaseScreenSharePermissionDialog(
         context,
         createOptionList(),
         appName = null,
+        hostUid = hostUid,
+        mediaProjectionMetricsLogger,
         R.drawable.ic_screenrecord,
         R.color.screenrecord_icon_color
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 8dc97c0..cc59f87 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2629,7 +2629,6 @@
         if (isPanelExpanded() != isExpanded) {
             setExpandedOrAwaitingInputTransfer(isExpanded);
             updateSystemUiStateFlags();
-            mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
             if (!isExpanded) {
                 mQsController.closeQsCustomizer();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 3c68438..0ec7a36 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -65,7 +65,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
@@ -77,6 +76,7 @@
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.res.R;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.ShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -1026,7 +1026,6 @@
                 && mPanelViewControllerLazy.get().mAnimateBack) {
             mPanelViewControllerLazy.get().adjustBackAnimationScale(adjustedExpansionFraction);
         }
-        mShadeExpansionStateManager.onQsExpansionFractionChanged(qsExpansionFraction);
         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
         int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
         mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 0554c58..9493989 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -36,10 +36,7 @@
 class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
 
     private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
-    private val fullExpansionListeners = CopyOnWriteArrayList<ShadeFullExpansionListener>()
     private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
-    private val qsExpansionFractionListeners =
-        CopyOnWriteArrayList<ShadeQsExpansionFractionListener>()
     private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
     private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
 
@@ -67,15 +64,6 @@
         expansionListeners.remove(listener)
     }
 
-    fun addFullExpansionListener(listener: ShadeFullExpansionListener) {
-        fullExpansionListeners.add(listener)
-        listener.onShadeExpansionFullyChanged(qsExpanded)
-    }
-
-    fun removeFullExpansionListener(listener: ShadeFullExpansionListener) {
-        fullExpansionListeners.remove(listener)
-    }
-
     fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
         qsExpansionListeners.add(listener)
         listener.onQsExpansionChanged(qsExpanded)
@@ -85,25 +73,11 @@
         qsExpansionListeners.remove(listener)
     }
 
-    fun addQsExpansionFractionListener(listener: ShadeQsExpansionFractionListener) {
-        qsExpansionFractionListeners.add(listener)
-        listener.onQsExpansionFractionChanged(qsExpansionFraction)
-    }
-
-    fun removeQsExpansionFractionListener(listener: ShadeQsExpansionFractionListener) {
-        qsExpansionFractionListeners.remove(listener)
-    }
-
     /** Adds a listener that will be notified when the panel state has changed. */
     fun addStateListener(listener: ShadeStateListener) {
         stateListeners.add(listener)
     }
 
-    /** Removes a state listener. */
-    fun removeStateListener(listener: ShadeStateListener) {
-        stateListeners.remove(listener)
-    }
-
     override fun addShadeStateEventsListener(listener: ShadeStateEventsListener) {
         shadeStateEventsListeners.addIfAbsent(listener)
     }
@@ -187,22 +161,6 @@
         qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) }
     }
 
-    fun onQsExpansionFractionChanged(qsExpansionFraction: Float) {
-        this.qsExpansionFraction = qsExpansionFraction
-
-        debugLog("qsExpansionFraction=$qsExpansionFraction")
-        qsExpansionFractionListeners.forEach {
-            it.onQsExpansionFractionChanged(qsExpansionFraction)
-        }
-    }
-
-    fun onShadeExpansionFullyChanged(isExpanded: Boolean) {
-        this.expanded = isExpanded
-
-        debugLog("expanded=$isExpanded")
-        fullExpansionListeners.forEach { it.onShadeExpansionFullyChanged(isExpanded) }
-    }
-
     /** Updates the panel state if necessary. */
     fun updateState(@PanelState state: Int) {
         debugLog(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
deleted file mode 100644
index 6d13e19..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2022 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.systemui.shade
-
-/** A listener interface to be notified of expansion events for the notification shade. */
-fun interface ShadeFullExpansionListener {
-    /** Invoked whenever the shade expansion changes, when it is fully collapsed or expanded */
-    fun onShadeExpansionFullyChanged(isExpanded: Boolean)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 2f68476..f043c71 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -148,12 +148,13 @@
      *
      * TODO(b/300258424) remove all but the first sentence of this comment
      */
-    val isAnyExpanded: Flow<Boolean> =
+    val isAnyExpanded: StateFlow<Boolean> =
         if (sceneContainerFlags.isEnabled()) {
-            anyExpansion.map { it > 0f }.distinctUntilChanged()
-        } else {
-            repository.legacyExpandedOrAwaitingInputTransfer
-        }
+                anyExpansion.map { it > 0f }.distinctUntilChanged()
+            } else {
+                repository.legacyExpandedOrAwaitingInputTransfer
+            }
+            .stateIn(scope, SharingStarted.Eagerly, false)
 
     /**
      * Whether the user is expanding or collapsing the shade with user input. This will be true even
@@ -184,7 +185,7 @@
      * but a transition they initiated is still animating.
      */
     val isUserInteracting: Flow<Boolean> =
-        combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
+        combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
             .distinctUntilChanged()
 
     /** Are touches allowed on the notification panel? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 7d81e55..c8669072 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -48,6 +48,13 @@
         fadeOut(view, duration, delay, (Runnable) null);
     }
 
+    /**
+     * Perform a fade-out animation, invoking {@code endRunnable} when the animation ends. It will
+     * not be invoked if the animation is cancelled.
+     *
+     * @deprecated Use {@link #fadeOut(View, long, int, Animator.AnimatorListener)} instead.
+     */
+    @Deprecated
     public static void fadeOut(final View view, long duration, int delay,
             @Nullable final Runnable endRunnable) {
         view.animate().cancel();
@@ -155,6 +162,13 @@
         fadeIn(view, duration, delay, /* endRunnable= */ (Runnable) null);
     }
 
+    /**
+     * Perform a fade-in animation, invoking {@code endRunnable} when the animation ends. It will
+     * not be invoked if the animation is cancelled.
+     *
+     * @deprecated Use {@link #fadeIn(View, long, int, Animator.AnimatorListener)} instead.
+     */
+    @Deprecated
     public static void fadeIn(final View view, long duration, int delay,
             @Nullable Runnable endRunnable) {
         view.animate().cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 33c42e0..ab015ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -72,10 +72,10 @@
         return findViewById(R.id.no_notifications_footer);
     }
 
-    public void setTextColor(@ColorInt int color) {
-        mEmptyText.setTextColor(color);
-        mEmptyFooterText.setTextColor(color);
-        mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(color));
+    public void setTextColors(@ColorInt int onSurface, @ColorInt int onSurfaceVariant) {
+        mEmptyText.setTextColor(onSurfaceVariant);
+        mEmptyFooterText.setTextColor(onSurface);
+        mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
     }
 
     public void setText(@StringRes int text) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 2f1b589..dd24ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -1457,7 +1457,7 @@
     }
 
     private boolean canUnlockWithFingerprint() {
-        return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+        return mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
                 getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 2e1e395..49743bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -29,14 +29,13 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
 import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
 import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
 import com.android.systemui.util.time.SystemClock;
@@ -62,7 +61,6 @@
     private static final long MAX_RANKING_DELAY_MILLIS = 500L;
 
     private final Context mContext;
-    private final FeatureFlagsClassic mFeatureFlags;
     private final NotificationManager mNotificationManager;
     private final SilentNotificationStatusIconsVisibilityInteractor mStatusIconInteractor;
     private final SystemClock mSystemClock;
@@ -80,7 +78,6 @@
     @Inject
     public NotificationListener(
             Context context,
-            FeatureFlagsClassic featureFlags,
             NotificationManager notificationManager,
             SilentNotificationStatusIconsVisibilityInteractor statusIconInteractor,
             SystemClock systemClock,
@@ -88,7 +85,6 @@
             PluginManager pluginManager) {
         super(pluginManager);
         mContext = context;
-        mFeatureFlags = featureFlags;
         mNotificationManager = notificationManager;
         mStatusIconInteractor = statusIconInteractor;
         mSystemClock = systemClock;
@@ -106,6 +102,7 @@
     /** Registers a listener that's notified when any notification-related settings change. */
     @Deprecated
     public void addNotificationSettingsListener(NotificationSettingsListener listener) {
+        NotificationIconContainerRefactor.assertInLegacyMode();
         mSettingsListeners.add(listener);
     }
 
@@ -240,7 +237,7 @@
 
     @Override
     public void onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             mStatusIconInteractor.setHideSilentStatusIcons(hideSilentStatusIcons);
         } else {
             for (NotificationSettingsListener listener : mSettingsListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 61ebcc0..5f0b298 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -48,11 +48,13 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dumpable;
-import com.android.systemui.dump.DumpManager;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
@@ -65,6 +67,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputView;
 import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.util.ListenerSet;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -72,13 +75,16 @@
 import java.util.Objects;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+
 /**
  * Class for handling remote input state over a set of notifications. This class handles things
  * like keeping notifications temporarily that were cancelled as a response to a remote input
  * interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
  * and handling clicks on remote views.
  */
-public class NotificationRemoteInputManager implements Dumpable {
+@SysUISingleton
+public class NotificationRemoteInputManager implements CoreStartable {
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
     public static boolean FORCE_REMOTE_INPUT_HISTORY =
@@ -94,6 +100,8 @@
     private final NotificationVisibilityProvider mVisibilityProvider;
     private final PowerInteractor mPowerInteractor;
     private final ActionClickLogger mLogger;
+    private final JavaAdapter mJavaAdapter;
+    private final ShadeInteractor mShadeInteractor;
     protected final Context mContext;
     protected final NotifPipelineFlags mNotifPipelineFlags;
     private final UserManager mUserManager;
@@ -249,6 +257,7 @@
     /**
      * Injected constructor. See {@link CentralSurfacesDependenciesModule}.
      */
+    @Inject
     public NotificationRemoteInputManager(
             Context context,
             NotifPipelineFlags notifPipelineFlags,
@@ -261,7 +270,8 @@
             RemoteInputControllerLogger remoteInputControllerLogger,
             NotificationClickNotifier clickNotifier,
             ActionClickLogger logger,
-            DumpManager dumpManager) {
+            JavaAdapter javaAdapter,
+            ShadeInteractor shadeInteractor) {
         mContext = context;
         mNotifPipelineFlags = notifPipelineFlags;
         mLockscreenUserManager = lockscreenUserManager;
@@ -269,6 +279,8 @@
         mVisibilityProvider = visibilityProvider;
         mPowerInteractor = powerInteractor;
         mLogger = logger;
+        mJavaAdapter = javaAdapter;
+        mShadeInteractor = shadeInteractor;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -277,8 +289,25 @@
         mRemoteInputUriController = remoteInputUriController;
         mRemoteInputControllerLogger = remoteInputControllerLogger;
         mClickNotifier = clickNotifier;
+    }
 
-        dumpManager.registerDumpable(this);
+    @Override
+    public void start() {
+        mJavaAdapter.alwaysCollectFlow(mShadeInteractor.isAnyExpanded(),
+                this::onShadeOrQsExpanded);
+    }
+
+    private void onShadeOrQsExpanded(boolean expanded) {
+        if (expanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
+            try {
+                mBarService.clearNotificationEffects();
+            } catch (RemoteException e) {
+                // Won't fail unless the world has ended.
+            }
+        }
+        if (!expanded) {
+            onPanelCollapsed();
+        }
     }
 
     /** Add a listener for various remote input events.  Works with NEW pipeline only. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 37a4ef1..537f8a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -42,15 +42,16 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.systemui.DejankUtils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.util.Compile;
+import com.android.systemui.util.kotlin.JavaAdapter;
+
+import dagger.Lazy;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -64,8 +65,7 @@
 @SysUISingleton
 public class StatusBarStateControllerImpl implements
         SysuiStatusBarStateController,
-        CallbackController<StateListener>,
-        Dumpable {
+        CallbackController<StateListener> {
     private static final String TAG = "SbStateController";
     private static final boolean DEBUG_IMMERSIVE_APPS =
             SystemProperties.getBoolean("persist.debug.immersive_apps", false);
@@ -95,6 +95,8 @@
     private final ArrayList<RankedListener> mListeners = new ArrayList<>();
     private final UiEventLogger mUiEventLogger;
     private final InteractionJankMonitor mInteractionJankMonitor;
+    private final JavaAdapter mJavaAdapter;
+    private final Lazy<ShadeInteractor> mShadeInteractorLazy;
     private int mState;
     private int mLastState;
     private int mUpcomingState;
@@ -156,18 +158,22 @@
     @Inject
     public StatusBarStateControllerImpl(
             UiEventLogger uiEventLogger,
-            DumpManager dumpManager,
             InteractionJankMonitor interactionJankMonitor,
-            ShadeExpansionStateManager shadeExpansionStateManager
-    ) {
+            JavaAdapter javaAdapter,
+            Lazy<ShadeInteractor> shadeInteractorLazy) {
         mUiEventLogger = uiEventLogger;
         mInteractionJankMonitor = interactionJankMonitor;
+        mJavaAdapter = javaAdapter;
+        mShadeInteractorLazy = shadeInteractorLazy;
         for (int i = 0; i < HISTORY_SIZE; i++) {
             mHistoricalRecords[i] = new HistoricalState();
         }
-        shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+    }
 
-        dumpManager.registerDumpable(this);
+    @Override
+    public void start() {
+        mJavaAdapter.alwaysCollectFlow(mShadeInteractorLazy.get().isAnyExpanded(),
+                this::onShadeOrQsExpanded);
     }
 
     @Override
@@ -345,7 +351,7 @@
         }
     }
 
-    private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+    private void onShadeOrQsExpanded(Boolean isExpanded) {
         if (mIsExpanded != isExpanded) {
             mIsExpanded = isExpanded;
             String tag = getClass().getSimpleName() + "#setIsExpanded";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index aa32d5c..8104755 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.view.View;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -29,7 +30,7 @@
 /**
  * Sends updates to {@link StateListener}s about changes to the status bar state and dozing state
  */
-public interface SysuiStatusBarStateController extends StatusBarStateController {
+public interface SysuiStatusBarStateController extends StatusBarStateController, CoreStartable {
 
     // TODO: b/115739177 (remove this explicit ordering if we can)
     @Retention(SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 1fe6b83..a957095 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -23,6 +23,7 @@
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.AnimationFeatureFlags;
 import com.android.systemui.animation.DialogLaunchAnimator;
@@ -33,24 +34,19 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeSurface;
 import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
-import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationClickNotifier;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -63,12 +59,13 @@
 import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
 
 import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 /**
  * This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
@@ -78,36 +75,12 @@
  */
 @Module(includes = {StatusBarNotificationPresenterModule.class})
 public interface CentralSurfacesDependenciesModule {
+
     /** */
-    @SysUISingleton
-    @Provides
-    static NotificationRemoteInputManager provideNotificationRemoteInputManager(
-            Context context,
-            NotifPipelineFlags notifPipelineFlags,
-            NotificationLockscreenUserManager lockscreenUserManager,
-            SmartReplyController smartReplyController,
-            NotificationVisibilityProvider visibilityProvider,
-            PowerInteractor powerInteractor,
-            StatusBarStateController statusBarStateController,
-            RemoteInputUriController remoteInputUriController,
-            RemoteInputControllerLogger remoteInputControllerLogger,
-            NotificationClickNotifier clickNotifier,
-            ActionClickLogger actionClickLogger,
-            DumpManager dumpManager) {
-        return new NotificationRemoteInputManager(
-                context,
-                notifPipelineFlags,
-                lockscreenUserManager,
-                smartReplyController,
-                visibilityProvider,
-                powerInteractor,
-                statusBarStateController,
-                remoteInputUriController,
-                remoteInputControllerLogger,
-                clickNotifier,
-                actionClickLogger,
-                dumpManager);
-    }
+    @Binds
+    @IntoMap
+    @ClassKey(NotificationRemoteInputManager.class)
+    CoreStartable bindsStartNotificationRemoteInputManager(NotificationRemoteInputManager nrim);
 
     /** */
     @SysUISingleton
@@ -164,20 +137,23 @@
         return new CommandQueue(context, displayTracker, registry, dumpHandler, powerInteractor);
     }
 
-    /**
-     */
+    /** */
     @Binds
     ManagedProfileController provideManagedProfileController(
             ManagedProfileControllerImpl controllerImpl);
 
-    /**
-     */
+    /** */
     @Binds
     SysuiStatusBarStateController providesSysuiStatusBarStateController(
             StatusBarStateControllerImpl statusBarStateControllerImpl);
 
-    /**
-     */
+    /** */
+    @Binds
+    @IntoMap
+    @ClassKey(SysuiStatusBarStateController.class)
+    CoreStartable bindsStartStatusBarStateController(StatusBarStateControllerImpl sbsc);
+
+    /** */
     @Binds
     StatusBarIconController provideStatusBarIconController(
             StatusBarIconControllerImpl controllerImpl);
@@ -212,16 +188,14 @@
     ShadeCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
             ShadeCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
 
-    /**
-     */
+    /** */
     @Provides
     @SysUISingleton
     static ActivityLaunchAnimator provideActivityLaunchAnimator() {
         return new ActivityLaunchAnimator();
     }
 
-    /**
-     */
+    /** */
     @Provides
     @SysUISingleton
     static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
@@ -253,8 +227,7 @@
         return new DialogLaunchAnimator(callback, interactionJankMonitor, animationFeatureFlags);
     }
 
-    /**
-     */
+    /** */
     @Provides
     @SysUISingleton
     static AnimationFeatureFlags provideAnimationFeatureFlags(FeatureFlags featureFlags) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 07e84bb..e0c4bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -25,6 +23,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.util.traceSection
@@ -38,7 +37,6 @@
 class StackCoordinator
 @Inject
 internal constructor(
-    private val featureFlags: FeatureFlagsClassic,
     private val groupExpansionManagerImpl: GroupExpansionManagerImpl,
     private val notificationIconAreaController: NotificationIconAreaController,
     private val renderListInteractor: RenderNotificationListInteractor,
@@ -52,7 +50,7 @@
     fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
         traceSection("StackCoordinator.onAfterRenderList") {
             controller.setNotifStats(calculateNotifStats(entries))
-            if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+            if (NotificationIconContainerRefactor.isEnabled) {
                 renderListInteractor.setRenderedList(entries)
             } else {
                 notificationIconAreaController.updateNotificationIcons(entries)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index ba91654..9c51e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -234,26 +234,24 @@
      */
     public void updateColors() {
         Resources.Theme theme = mContext.getTheme();
-        final @ColorInt int textColor = getResources().getColor(R.color.notif_pill_text, theme);
+        final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.materialColorOnSurface);
+        final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
         final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
         final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
         // TODO(b/282173943): Remove redundant tinting once Resources are thread-safe
-        final @ColorInt int buttonBgColor =
-                Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface);
-        final ColorFilter bgColorFilter = new PorterDuffColorFilter(buttonBgColor, SRC_ATOP);
-        if (buttonBgColor != 0) {
+        final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
+        if (scHigh != 0) {
             clearAllBg.setColorFilter(bgColorFilter);
             manageBg.setColorFilter(bgColorFilter);
         }
         mClearAllButton.setBackground(clearAllBg);
-        mClearAllButton.setTextColor(textColor);
+        mClearAllButton.setTextColor(onSurface);
         mManageButton.setBackground(manageBg);
-        mManageButton.setTextColor(textColor);
-        final @ColorInt int labelTextColor =
-                Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
-        mSeenNotifsFooterTextView.setTextColor(labelTextColor);
-        mSeenNotifsFooterTextView.setCompoundDrawableTintList(
-                ColorStateList.valueOf(labelTextColor));
+        mManageButton.setTextColor(onSurface);
+        mSeenNotifsFooterTextView.setTextColor(onSurface);
+        mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
     }
 
     private void updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
index 246933a..1992ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
@@ -18,24 +18,15 @@
 import android.content.Context
 import android.graphics.Rect
 import android.view.View
-import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.RefactorFlag
 import com.android.systemui.statusbar.NotificationShelfController
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
-import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
 import javax.inject.Inject
-import kotlinx.coroutines.DisposableHandle
 
 /**
  * Controller class for [NotificationIconContainer]. This implementation serves as a temporary
@@ -45,67 +36,16 @@
  * can be used directly.
  */
 @SysUISingleton
-class NotificationIconAreaControllerViewBinderWrapperImpl
-@Inject
-constructor(
-    private val configuration: ConfigurationState,
-    private val configurationController: ConfigurationController,
-    private val dozeParameters: DozeParameters,
-    private val featureFlags: FeatureFlagsClassic,
-    private val screenOffAnimationController: ScreenOffAnimationController,
-    private val shelfIconViewStore: ShelfNotificationIconViewStore,
-    private val shelfIconsViewModel: NotificationIconContainerShelfViewModel,
-    private val aodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
-    private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
-) : NotificationIconAreaController {
-
-    private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
-
-    private var shelfIcons: NotificationIconContainer? = null
-    private var aodIcons: NotificationIconContainer? = null
-    private var aodBindJob: DisposableHandle? = null
+class NotificationIconAreaControllerViewBinderWrapperImpl @Inject constructor() :
+    NotificationIconAreaController {
 
     /** Called by the Keyguard*ViewController whose view contains the aod icons. */
-    override fun setupAodIcons(aodIcons: NotificationIconContainer) {
-        val changed = this.aodIcons != null && aodIcons !== this.aodIcons
-        if (changed) {
-            this.aodIcons!!.setAnimationsEnabled(false)
-            this.aodIcons!!.removeAllViews()
-            aodBindJob?.dispose()
-        }
-        this.aodIcons = aodIcons
-        this.aodIcons!!.setOnLockScreen(true)
-        aodBindJob =
-            NotificationIconContainerViewBinder.bind(
-                aodIcons,
-                aodIconsViewModel,
-                configuration,
-                configurationController,
-                dozeParameters,
-                featureFlags,
-                screenOffAnimationController,
-                aodIconViewStore,
-            )
-    }
+    override fun setupAodIcons(aodIcons: NotificationIconContainer?) = unsupported
 
     override fun setupShelf(notificationShelfController: NotificationShelfController) =
         NotificationShelfViewBinderWrapperControllerImpl.unsupported
 
-    override fun setShelfIcons(icons: NotificationIconContainer) {
-        if (shelfRefactor.isUnexpectedlyInLegacyMode()) {
-            NotificationIconContainerViewBinder.bind(
-                icons,
-                shelfIconsViewModel,
-                configuration,
-                configurationController,
-                dozeParameters,
-                featureFlags,
-                screenOffAnimationController,
-                shelfIconViewStore,
-            )
-            shelfIcons = icons
-        }
-    }
+    override fun setShelfIcons(icons: NotificationIconContainer) = unsupported
 
     override fun onDensityOrFontScaleChanged(context: Context) = unsupported
 
@@ -126,15 +66,14 @@
 
     override fun onThemeChanged() = unsupported
 
-    override fun getHeight(): Int {
-        return if (aodIcons == null) 0 else aodIcons!!.height
-    }
+    override fun getHeight(): Int = unsupported
 
     companion object {
         val unsupported: Nothing
             get() =
                 error(
-                    "Code path not supported when NOTIFICATION_ICON_CONTAINER_REFACTOR is disabled"
+                    "Code path not supported when ${NotificationIconContainerRefactor.FLAG_NAME}" +
+                        " is disabled"
                 )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 7592619..c1f728a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -17,13 +17,15 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.graphics.Color
 import android.graphics.Rect
 import android.view.View
+import android.view.ViewGroup
 import android.view.ViewPropertyAnimator
 import android.widget.FrameLayout
+import androidx.annotation.ColorInt
 import androidx.collection.ArrayMap
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
+import androidx.lifecycle.lifecycleScope
 import com.android.app.animation.Interpolators
 import com.android.internal.policy.SystemBarUtils
 import com.android.internal.util.ContrastColorUtil
@@ -37,9 +39,12 @@
 import com.android.systemui.statusbar.notification.NotificationUtils
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColorLookup
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.NotificationIconContainer
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -49,6 +54,7 @@
 import com.android.systemui.util.kotlin.mapValuesNotNullTo
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.kotlin.stateFlow
+import com.android.systemui.util.ui.AnimatedValue
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.stopAnimating
 import com.android.systemui.util.ui.value
@@ -62,12 +68,53 @@
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
-/** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */
+/** Binds a view-model to a [NotificationIconContainer]. */
 object NotificationIconContainerViewBinder {
     @JvmStatic
     fun bind(
         view: NotificationIconContainer,
-        viewModel: NotificationIconContainerViewModel,
+        viewModel: NotificationIconContainerShelfViewModel,
+        configuration: ConfigurationState,
+        configurationController: ConfigurationController,
+        viewStore: ShelfNotificationIconViewStore,
+    ): DisposableHandle {
+        return view.repeatWhenAttached {
+            lifecycleScope.launch {
+                viewModel.icons.bindIcons(view, configuration, configurationController, viewStore)
+            }
+        }
+    }
+
+    @JvmStatic
+    fun bind(
+        view: NotificationIconContainer,
+        viewModel: NotificationIconContainerStatusBarViewModel,
+        configuration: ConfigurationState,
+        configurationController: ConfigurationController,
+        viewStore: StatusBarNotificationIconViewStore,
+    ): DisposableHandle {
+        val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
+        return view.repeatWhenAttached {
+            lifecycleScope.run {
+                launch {
+                    viewModel.icons.bindIcons(
+                        view,
+                        configuration,
+                        configurationController,
+                        viewStore
+                    )
+                }
+                launch { viewModel.iconColors.bindIconColors(view, contrastColorUtil) }
+                launch { viewModel.bindIsolatedIcon(view, viewStore) }
+                launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
+            }
+        }
+    }
+
+    @JvmStatic
+    fun bind(
+        view: NotificationIconContainer,
+        viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
         configuration: ConfigurationState,
         configurationController: ConfigurationController,
         dozeParameters: DozeParameters,
@@ -75,60 +122,72 @@
         screenOffAnimationController: ScreenOffAnimationController,
         viewStore: IconViewStore,
     ): DisposableHandle {
-        val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
         return view.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                launch { bindAnimationsEnabled(viewModel, view) }
-                launch { bindIsDozing(viewModel, view, dozeParameters) }
-                // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC
-                //  view-binder
+            lifecycleScope.launch {
                 launch {
-                    bindVisibility(
-                        viewModel,
-                        view,
-                        configuration,
-                        featureFlags,
-                        screenOffAnimationController,
-                    )
-                }
-                launch { bindIconColors(viewModel, view, contrastColorUtil) }
-                launch {
-                    bindIconViewData(
-                        viewModel,
+                    viewModel.icons.bindIcons(
                         view,
                         configuration,
                         configurationController,
                         viewStore,
                     )
                 }
-                launch { bindIsolatedIcon(viewModel, view, viewStore) }
+                launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
+                launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
+                // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC
+                //  view-binder
+                launch {
+                    viewModel.isVisible.bindIsVisible(
+                        view,
+                        configuration,
+                        featureFlags,
+                        screenOffAnimationController,
+                    )
+                }
+                launch {
+                    configuration
+                        .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
+                        .bindIconColors(view)
+                }
             }
         }
     }
 
-    private suspend fun bindAnimationsEnabled(
-        viewModel: NotificationIconContainerViewModel,
-        view: NotificationIconContainer
-    ) {
-        viewModel.animationsEnabled.collect(view::setAnimationsEnabled)
+    /** Binds to [NotificationIconContainer.setAnimationsEnabled] */
+    private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
+        collect(view::setAnimationsEnabled)
     }
 
-    private suspend fun bindIconColors(
-        viewModel: NotificationIconContainerViewModel,
+    /**
+     * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
+     * of the [children] of an [NotificationIconContainer].
+     */
+    private suspend fun Flow<NotificationIconColorLookup>.bindIconColors(
         view: NotificationIconContainer,
         contrastColorUtil: ContrastColorUtil,
     ) {
-        viewModel.iconColors
-            .mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
-            .collect { iconLookup -> applyTint(view, iconLookup, contrastColorUtil) }
+        mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
+            .collect { iconLookup -> view.applyTint(iconLookup, contrastColorUtil) }
     }
 
-    private suspend fun bindIsDozing(
-        viewModel: NotificationIconContainerViewModel,
+    /**
+     * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
+     * of the [children] of an [NotificationIconContainer].
+     */
+    private suspend fun Flow<Int>.bindIconColors(view: NotificationIconContainer) {
+        collect { tint ->
+            view.children.filterIsInstance<StatusBarIconView>().forEach { icon ->
+                icon.staticDrawableColor = tint
+                icon.setDecorColor(tint)
+            }
+        }
+    }
+
+    private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
         view: NotificationIconContainer,
         dozeParameters: DozeParameters,
     ) {
-        viewModel.isDozing.collect { isDozing ->
+        collect { isDozing ->
             if (isDozing.isAnimating) {
                 val animate = !dozeParameters.displayNeedsBlanking
                 view.setDozing(
@@ -147,19 +206,18 @@
         }
     }
 
-    private suspend fun bindIsolatedIcon(
-        viewModel: NotificationIconContainerViewModel,
+    private suspend fun NotificationIconContainerStatusBarViewModel.bindIsolatedIcon(
         view: NotificationIconContainer,
         viewStore: IconViewStore,
     ) {
         coroutineScope {
             launch {
-                viewModel.isolatedIconLocation.collect { location ->
+                isolatedIconLocation.collect { location ->
                     view.setIsolatedIconLocation(location, true)
                 }
             }
             launch {
-                viewModel.isolatedIcon.collect { iconInfo ->
+                isolatedIcon.collect { iconInfo ->
                     val iconView = iconInfo.value?.let { viewStore.iconView(it.notifKey) }
                     if (iconInfo.isAnimating) {
                         view.showIconIsolatedAnimated(iconView, iconInfo::stopAnimating)
@@ -171,8 +229,8 @@
         }
     }
 
-    private suspend fun bindIconViewData(
-        viewModel: NotificationIconContainerViewModel,
+    /** Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children]. */
+    private suspend fun Flow<NotificationIconsViewData>.bindIcons(
         view: NotificationIconContainer,
         configuration: ConfigurationState,
         configurationController: ConfigurationController,
@@ -205,11 +263,11 @@
             }
         }
 
-        var prevIcons = IconsViewData()
-        viewModel.iconsViewData.sample(layoutParams, ::Pair).collect {
-            (iconsData: IconsViewData, layoutParams: FrameLayout.LayoutParams),
+        var prevIcons = NotificationIconsViewData()
+        sample(layoutParams, ::Pair).collect {
+            (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
             ->
-            val iconsDiff = IconsViewData.computeDifference(iconsData, prevIcons)
+            val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
             prevIcons = iconsData
 
             val replacingIcons =
@@ -255,30 +313,27 @@
 
     // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
     //  can be moved there and cleaned up.
-    private fun applyTint(
-        view: NotificationIconContainer,
-        iconColors: IconColors,
+    private fun ViewGroup.applyTint(
+        iconColors: NotificationIconColors,
         contrastColorUtil: ContrastColorUtil,
     ) {
-        view.children
+        children
             .filterIsInstance<StatusBarIconView>()
             .filter { it.width != 0 }
-            .forEach { iv -> updateTintForIcon(iv, iconColors, contrastColorUtil) }
+            .forEach { iv -> iv.updateTintForIcon(iconColors, contrastColorUtil) }
     }
 
-    private fun updateTintForIcon(
-        v: StatusBarIconView,
-        iconColors: IconColors,
+    private fun StatusBarIconView.updateTintForIcon(
+        iconColors: NotificationIconColors,
         contrastColorUtil: ContrastColorUtil,
     ) {
-        val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L)
-        val isColorized = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil)
-        v.staticDrawableColor = iconColors.staticDrawableColor(v.viewBounds, isColorized)
-        v.setDecorColor(iconColors.tint)
+        val isPreL = java.lang.Boolean.TRUE == getTag(R.id.icon_is_pre_L)
+        val isColorized = !isPreL || NotificationUtils.isGrayscale(this, contrastColorUtil)
+        staticDrawableColor = iconColors.staticDrawableColor(viewBounds, isColorized)
+        setDecorColor(iconColors.tint)
     }
 
-    private suspend fun bindVisibility(
-        viewModel: NotificationIconContainerViewModel,
+    private suspend fun Flow<AnimatedValue<Boolean>>.bindIsVisible(
         view: NotificationIconContainer,
         configuration: ConfigurationState,
         featureFlags: FeatureFlagsClassic,
@@ -287,7 +342,7 @@
         val iconAppearTranslation =
             configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
         val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
-        viewModel.isVisible.collect { isVisible ->
+        collect { isVisible ->
             view.animate().cancel()
             val animatorListener =
                 object : AnimatorListenerAdapter() {
@@ -304,7 +359,7 @@
                     view.visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
                 }
                 featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
-                    animateInIconTranslation(view, statusViewMigrated)
+                    view.animateInIconTranslation(statusViewMigrated)
                     if (isVisible.value) {
                         CrossFadeHelper.fadeIn(view, animatorListener)
                     } else {
@@ -313,15 +368,14 @@
                 }
                 !isVisible.value -> {
                     // Let's make sure the icon are translated to 0, since we cancelled it above
-                    animateInIconTranslation(view, statusViewMigrated)
+                    view.animateInIconTranslation(statusViewMigrated)
                     CrossFadeHelper.fadeOut(view, animatorListener)
                 }
                 view.visibility != View.VISIBLE -> {
                     // No fading here, let's just appear the icons instead!
                     view.visibility = View.VISIBLE
                     view.alpha = 1f
-                    appearIcons(
-                        view,
+                    view.appearIcons(
                         animate = screenOffAnimationController.shouldAnimateAodIcons(),
                         iconAppearTranslation.value,
                         statusViewMigrated,
@@ -330,7 +384,7 @@
                 }
                 else -> {
                     // Let's make sure the icons are translated to 0, since we cancelled it above
-                    animateInIconTranslation(view, statusViewMigrated)
+                    view.animateInIconTranslation(statusViewMigrated)
                     // We were fading out, let's fade in instead
                     CrossFadeHelper.fadeIn(view, animatorListener)
                 }
@@ -338,8 +392,7 @@
         }
     }
 
-    private fun appearIcons(
-        view: View,
+    private fun View.appearIcons(
         animate: Boolean,
         iconAppearTranslation: Int,
         statusViewMigrated: Boolean,
@@ -347,11 +400,10 @@
     ) {
         if (animate) {
             if (!statusViewMigrated) {
-                view.translationY = -iconAppearTranslation.toFloat()
+                translationY = -iconAppearTranslation.toFloat()
             }
-            view.alpha = 0f
-            view
-                .animate()
+            alpha = 0f
+            animate()
                 .alpha(1f)
                 .setInterpolator(Interpolators.LINEAR)
                 .setDuration(AOD_ICONS_APPEAR_DURATION)
@@ -359,40 +411,29 @@
                 .setListener(animatorListener)
                 .start()
         } else {
-            view.alpha = 1.0f
+            alpha = 1.0f
             if (!statusViewMigrated) {
-                view.translationY = 0f
+                translationY = 0f
             }
         }
     }
 
-    private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) {
+    private fun View.animateInIconTranslation(statusViewMigrated: Boolean) {
         if (!statusViewMigrated) {
-            view.animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
+            animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
         }
     }
 
     private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
         setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
 
-    private const val AOD_ICONS_APPEAR_DURATION: Long = 200
-
-    private val View.viewBounds: Rect
-        get() {
-            val tmpArray = intArrayOf(0, 0)
-            getLocationOnScreen(tmpArray)
-            return Rect(
-                /* left = */ tmpArray[0],
-                /* top = */ tmpArray[1],
-                /* right = */ left + width,
-                /* bottom = */ top + height,
-            )
-        }
-
     /** External storage for [StatusBarIconView] instances. */
     fun interface IconViewStore {
         fun iconView(key: String): StatusBarIconView?
     }
+
+    private const val AOD_ICONS_APPEAR_DURATION: Long = 200
+    @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
 }
 
 /** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
@@ -424,3 +465,15 @@
     override fun iconView(key: String): StatusBarIconView? =
         notifCollection.getEntry(key)?.icons?.statusBarIcon
 }
+
+private val View.viewBounds: Rect
+    get() {
+        val tmpArray = intArrayOf(0, 0)
+        getLocationOnScreen(tmpArray)
+        return Rect(
+            /* left = */ tmpArray[0],
+            /* top = */ tmpArray[1],
+            /* right = */ left + width,
+            /* bottom = */ top + height,
+        )
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
new file mode 100644
index 0000000..97d1e1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar.notification.icon.ui.viewmodel
+
+import android.graphics.Rect
+
+/**
+ * Lookup the colors to use for the notification icons based on the bounds of the icon container. A
+ * result of `null` indicates that no color changes should be applied.
+ */
+fun interface NotificationIconColorLookup {
+    fun iconColors(viewBounds: Rect): NotificationIconColors?
+}
+
+/** Colors to apply to notification icons. */
+interface NotificationIconColors {
+
+    /** A tint to apply to the icons. */
+    val tint: Int
+
+    /**
+     * Returns the color to be applied to an icon, based on that icon's view bounds and whether or
+     * not the notification icon is colorized.
+     */
+    fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 120d342..611ed89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -15,10 +15,7 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
-import android.graphics.Color
 import android.graphics.Rect
-import androidx.annotation.ColorInt
-import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -27,14 +24,9 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.util.kotlin.pairwise
@@ -47,8 +39,6 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 /** View-model for the row of notification icons displayed on the always-on display. */
@@ -56,7 +46,6 @@
 class NotificationIconContainerAlwaysOnDisplayViewModel
 @Inject
 constructor(
-    configuration: ConfigurationState,
     private val deviceEntryInteractor: DeviceEntryInteractor,
     private val dozeParameters: DozeParameters,
     private val featureFlags: FeatureFlagsClassic,
@@ -66,14 +55,10 @@
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
     screenOffAnimationController: ScreenOffAnimationController,
     shadeInteractor: ShadeInteractor,
-) : NotificationIconContainerViewModel {
+) {
 
-    override val iconColors: Flow<ColorLookup> =
-        configuration.getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR).map { tint ->
-            ColorLookup { IconColorsImpl(tint) }
-        }
-
-    override val animationsEnabled: Flow<Boolean> =
+    /** Are changes to the icon container animated? */
+    val animationsEnabled: Flow<Boolean> =
         combine(
             shadeInteractor.isShadeTouchable,
             keyguardInteractor.isKeyguardVisible,
@@ -81,7 +66,8 @@
             panelTouchesEnabled && isKeyguardVisible
         }
 
-    override val isDozing: Flow<AnimatedValue<Boolean>> =
+    /** Should icons be rendered in "dozing" mode? */
+    val isDozing: Flow<AnimatedValue<Boolean>> =
         keyguardTransitionInteractor.startedKeyguardTransitionStep
             // Determine if we're dozing based on the most recent transition
             .map { step: TransitionStep ->
@@ -98,7 +84,8 @@
             .distinctUntilChanged()
             .toAnimatedValueFlow()
 
-    override val isVisible: Flow<AnimatedValue<Boolean>> =
+    /** Is the icon container visible? */
+    val isVisible: Flow<AnimatedValue<Boolean>> =
         combine(
                 keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE },
                 deviceEntryInteractor.isBypassEnabled,
@@ -136,17 +123,14 @@
             }
             .distinctUntilChanged()
 
-    override val iconsViewData: Flow<IconsViewData> =
+    /** [NotificationIconsViewData] indicating which icons to display in the view. */
+    val icons: Flow<NotificationIconsViewData> =
         iconsInteractor.aodNotifs.map { entries ->
-            IconsViewData(
+            NotificationIconsViewData(
                 visibleKeys = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
             )
         }
 
-    override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> =
-        flowOf(AnimatedValue.NotAnimating(null))
-    override val isolatedIconLocation: Flow<Rect> = emptyFlow()
-
     /** Is there an expanded pulse, are we animating in response? */
     private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
         return notificationsKeyguardInteractor.isPulseExpanding
@@ -182,11 +166,7 @@
             .toAnimatedValueFlow()
     }
 
-    private class IconColorsImpl(override val tint: Int) : IconColors {
+    private class IconColorsImpl(override val tint: Int) : NotificationIconColors {
         override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int = tint
     }
-
-    companion object {
-        @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index c6aabb7..1560106 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -15,16 +15,9 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
-import android.graphics.Rect
 import com.android.systemui.statusbar.notification.icon.domain.interactor.NotificationIconsInteractor
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
-import com.android.systemui.util.ui.AnimatedValue
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 /** View-model for the overflow row of notification icons displayed in the notification shade. */
@@ -32,19 +25,11 @@
 @Inject
 constructor(
     interactor: NotificationIconsInteractor,
-) : NotificationIconContainerViewModel {
-
-    override val animationsEnabled: Flow<Boolean> = flowOf(true)
-    override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
-    override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
-    override val iconColors: Flow<ColorLookup> = emptyFlow()
-    override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> =
-        flowOf(AnimatedValue.NotAnimating(null))
-    override val isolatedIconLocation: Flow<Rect> = emptyFlow()
-
-    override val iconsViewData: Flow<IconsViewData> =
+) {
+    /** [NotificationIconsViewData] indicating which icons to display in the view. */
+    val icons: Flow<NotificationIconsViewData> =
         interactor.filteredNotifSet().map { entries ->
-            IconsViewData(
+            NotificationIconsViewData(
                 visibleKeys = entries.mapNotNull { it.toIconInfo(it.shelfIcon) },
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 4d14024..53631e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -22,10 +22,6 @@
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.StatusBarNotificationIconsInteractor
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
 import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
@@ -35,7 +31,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 
@@ -49,8 +44,10 @@
     keyguardInteractor: KeyguardInteractor,
     notificationsInteractor: ActiveNotificationsInteractor,
     shadeInteractor: ShadeInteractor,
-) : NotificationIconContainerViewModel {
-    override val animationsEnabled: Flow<Boolean> =
+) {
+
+    /** Are changes to the icon container animated? */
+    val animationsEnabled: Flow<Boolean> =
         combine(
             shadeInteractor.isShadeTouchable,
             keyguardInteractor.isKeyguardShowing,
@@ -58,14 +55,15 @@
             panelTouchesEnabled && !isKeyguardShowing
         }
 
-    override val iconColors: Flow<ColorLookup> =
+    /** The colors with which to display the notification icons. */
+    val iconColors: Flow<NotificationIconColorLookup> =
         combine(
             darkIconInteractor.tintAreas,
             darkIconInteractor.tintColor,
             // Included so that tints are re-applied after entries are changed.
             notificationsInteractor.notifications,
         ) { areas, tint, _ ->
-            ColorLookup { viewBounds: Rect ->
+            NotificationIconColorLookup { viewBounds: Rect ->
                 if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
                     IconColorsImpl(tint, areas)
                 } else {
@@ -74,20 +72,19 @@
             }
         }
 
-    override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
-    override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
-
-    override val iconsViewData: Flow<IconsViewData> =
+    /** [NotificationIconsViewData] indicating which icons to display in the view. */
+    val icons: Flow<NotificationIconsViewData> =
         iconsInteractor.statusBarNotifs.map { entries ->
-            IconsViewData(
+            NotificationIconsViewData(
                 visibleKeys = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
             )
         }
 
-    override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> =
+    /** An Icon to show "isolated" in the IconContainer. */
+    val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
         headsUpIconInteractor.isolatedNotification
             .pairwise(initialValue = null)
-            .sample(combine(iconsViewData, shadeInteractor.shadeExpansion, ::Pair)) {
+            .sample(combine(icons, shadeInteractor.shadeExpansion, ::Pair)) {
                 (prev, isolatedNotif),
                 (iconsViewData, shadeExpansion),
                 ->
@@ -105,13 +102,14 @@
             }
             .toAnimatedValueFlow()
 
-    override val isolatedIconLocation: Flow<Rect> =
+    /** Location to show an isolated icon, if there is one. */
+    val isolatedIconLocation: Flow<Rect> =
         headsUpIconInteractor.isolatedIconLocation.filterNotNull()
 
     private class IconColorsImpl(
         override val tint: Int,
         private val areas: Collection<Rect>,
-    ) : IconColors {
+    ) : NotificationIconColors {
         override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int {
             return if (isColorized && DarkIconDispatcher.isInAreas(areas, viewBounds)) {
                 tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
deleted file mode 100644
index a611323..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2023 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.systemui.statusbar.notification.icon.ui.viewmodel
-
-import android.graphics.Rect
-import android.graphics.drawable.Icon
-import androidx.collection.ArrayMap
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
-import com.android.systemui.util.kotlin.mapValuesNotNullTo
-import com.android.systemui.util.ui.AnimatedValue
-import kotlinx.coroutines.flow.Flow
-
-/**
- * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and
- * AOD.
- */
-interface NotificationIconContainerViewModel {
-
-    /** Are changes to the icon container animated? */
-    val animationsEnabled: Flow<Boolean>
-
-    /** Should icons be rendered in "dozing" mode? */
-    val isDozing: Flow<AnimatedValue<Boolean>>
-
-    /** Is the icon container visible? */
-    val isVisible: Flow<AnimatedValue<Boolean>>
-
-    /** The colors with which to display the notification icons. */
-    val iconColors: Flow<ColorLookup>
-
-    /** [IconsViewData] indicating which icons to display in the view. */
-    val iconsViewData: Flow<IconsViewData>
-
-    /** An Icon to show "isolated" in the IconContainer. */
-    val isolatedIcon: Flow<AnimatedValue<IconInfo?>>
-
-    /** Location to show an isolated icon, if there is one. */
-    val isolatedIconLocation: Flow<Rect>
-
-    /**
-     * Lookup the colors to use for the notification icons based on the bounds of the icon
-     * container. A result of `null` indicates that no color changes should be applied.
-     */
-    fun interface ColorLookup {
-        fun iconColors(viewBounds: Rect): IconColors?
-    }
-
-    /** Colors to apply to notification icons. */
-    interface IconColors {
-
-        /** A tint to apply to the icons. */
-        val tint: Int
-
-        /**
-         * Returns the color to be applied to an icon, based on that icon's view bounds and whether
-         * or not the notification icon is colorized.
-         */
-        fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int
-    }
-
-    /** Encapsulates the collection of notification icons present on the device. */
-    data class IconsViewData(
-        /** Icons that are visible in the container. */
-        val visibleKeys: List<IconInfo> = emptyList(),
-        /** Keys of icons that are "behind" the overflow dot. */
-        val collapsedKeys: Set<String> = emptySet(),
-        /** Whether the overflow dot should be shown regardless if [collapsedKeys] is empty. */
-        val forceShowDot: Boolean = false,
-    ) {
-        /** The difference between two [IconsViewData]s. */
-        data class Diff(
-            /** Icons added in the newer dataset. */
-            val added: List<IconInfo> = emptyList(),
-            /** Icons removed from the older dataset. */
-            val removed: List<String> = emptyList(),
-            /**
-             * Groups whose icon was replaced with a single new notification icon. The key of the
-             * [Map] is the notification group key, and the value is the new icon.
-             *
-             * Specifically, this models a difference where the older dataset had notification
-             * groups with a single icon in the set, and the newer dataset has a single, different
-             * icon for the same group. A view binder can use this information for special
-             * animations for this specific change.
-             */
-            val groupReplacements: Map<String, IconInfo> = emptyMap(),
-        )
-
-        companion object {
-            /**
-             * Returns an [IconsViewData.Diff] calculated from a [new] and [previous][prev]
-             * [IconsViewData] state.
-             */
-            fun computeDifference(new: IconsViewData, prev: IconsViewData): Diff {
-                val added: List<IconInfo> =
-                    new.visibleKeys.filter {
-                        it.notifKey !in prev.visibleKeys.asSequence().map { it.notifKey }
-                    }
-                val removed: List<IconInfo> =
-                    prev.visibleKeys.filter {
-                        it.notifKey !in new.visibleKeys.asSequence().map { it.notifKey }
-                    }
-                val groupsToShow: Set<IconGroupInfo> =
-                    new.visibleKeys.asSequence().map { it.groupInfo }.toSet()
-                val replacements: ArrayMap<String, IconInfo> =
-                    removed
-                        .asSequence()
-                        .filter { keyToRemove -> keyToRemove.groupInfo in groupsToShow }
-                        .groupBy { it.groupInfo.groupKey }
-                        .mapValuesNotNullTo(ArrayMap()) { (_, vs) ->
-                            vs.takeIf { it.size == 1 }?.get(0)
-                        }
-                return Diff(added, removed.map { it.notifKey }, replacements)
-            }
-        }
-    }
-
-    /** An Icon, and keys for unique identification. */
-    data class IconInfo(
-        val sourceIcon: Icon,
-        val notifKey: String,
-        val groupKey: String,
-    )
-}
-
-/**
- * Construct an [IconInfo] out of an [ActiveNotificationModel], or return `null` if one cannot be
- * created due to missing information.
- */
-fun ActiveNotificationModel.toIconInfo(sourceIcon: Icon?): IconInfo? {
-    return sourceIcon?.let {
-        groupKey?.let { groupKey ->
-            IconInfo(
-                sourceIcon = sourceIcon,
-                notifKey = key,
-                groupKey = groupKey,
-            )
-        }
-    }
-}
-
-private val IconInfo.groupInfo: IconGroupInfo
-    get() = IconGroupInfo(sourceIcon, groupKey)
-
-private data class IconGroupInfo(
-    val sourceIcon: Icon,
-    val groupKey: String,
-) {
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (javaClass != other?.javaClass) return false
-
-        other as IconGroupInfo
-
-        if (groupKey != other.groupKey) return false
-        return sourceIcon.sameAs(other.sourceIcon)
-    }
-
-    override fun hashCode(): Int {
-        var result = groupKey.hashCode()
-        result = 31 * result + sourceIcon.type.hashCode()
-        when (sourceIcon.type) {
-            Icon.TYPE_BITMAP,
-            Icon.TYPE_ADAPTIVE_BITMAP -> {
-                result = 31 * result + sourceIcon.bitmap.hashCode()
-            }
-            Icon.TYPE_DATA -> {
-                result = 31 * result + sourceIcon.dataLength.hashCode()
-                result = 31 * result + sourceIcon.dataOffset.hashCode()
-            }
-            Icon.TYPE_RESOURCE -> {
-                result = 31 * result + sourceIcon.resId.hashCode()
-                result = 31 * result + sourceIcon.resPackage.hashCode()
-            }
-            Icon.TYPE_URI,
-            Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
-                result = 31 * result + sourceIcon.uriString.hashCode()
-            }
-        }
-        return result
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
new file mode 100644
index 0000000..867be84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar.notification.icon.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import androidx.collection.ArrayMap
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.util.kotlin.mapValuesNotNullTo
+
+/** Encapsulates the collection of notification icons present on the device. */
+data class NotificationIconsViewData(
+    /** Icons that are visible in the container. */
+    val visibleKeys: List<NotificationIconInfo> = emptyList(),
+    /** Keys of icons that are "behind" the overflow dot. */
+    val collapsedKeys: Set<String> = emptySet(),
+    /** Whether the overflow dot should be shown regardless if [collapsedKeys] is empty. */
+    val forceShowDot: Boolean = false,
+) {
+    /** The difference between two [NotificationIconsViewData]s. */
+    data class Diff(
+        /** Icons added in the newer dataset. */
+        val added: List<NotificationIconInfo> = emptyList(),
+        /** Icons removed from the older dataset. */
+        val removed: List<String> = emptyList(),
+        /**
+         * Groups whose icon was replaced with a single new notification icon. The key of the [Map]
+         * is the notification group key, and the value is the new icon.
+         *
+         * Specifically, this models a difference where the older dataset had notification groups
+         * with a single icon in the set, and the newer dataset has a single, different icon for the
+         * same group. A view binder can use this information for special animations for this
+         * specific change.
+         */
+        val groupReplacements: Map<String, NotificationIconInfo> = emptyMap(),
+    )
+
+    companion object {
+        /**
+         * Returns an [NotificationIconsViewData.Diff] calculated from a [new] and [previous][prev]
+         * [NotificationIconsViewData] state.
+         */
+        fun computeDifference(
+            new: NotificationIconsViewData,
+            prev: NotificationIconsViewData
+        ): Diff {
+            val added: List<NotificationIconInfo> =
+                new.visibleKeys.filter {
+                    it.notifKey !in prev.visibleKeys.asSequence().map { it.notifKey }
+                }
+            val removed: List<NotificationIconInfo> =
+                prev.visibleKeys.filter {
+                    it.notifKey !in new.visibleKeys.asSequence().map { it.notifKey }
+                }
+            val groupsToShow: Set<IconGroupInfo> =
+                new.visibleKeys.asSequence().map { it.groupInfo }.toSet()
+            val replacements: ArrayMap<String, NotificationIconInfo> =
+                removed
+                    .asSequence()
+                    .filter { keyToRemove -> keyToRemove.groupInfo in groupsToShow }
+                    .groupBy { it.groupInfo.groupKey }
+                    .mapValuesNotNullTo(ArrayMap()) { (_, vs) ->
+                        vs.takeIf { it.size == 1 }?.get(0)
+                    }
+            return Diff(added, removed.map { it.notifKey }, replacements)
+        }
+    }
+}
+
+/** An Icon, and keys for unique identification. */
+data class NotificationIconInfo(
+    val sourceIcon: Icon,
+    val notifKey: String,
+    val groupKey: String,
+)
+
+/**
+ * Construct an [NotificationIconInfo] out of an [ActiveNotificationModel], or return `null` if one
+ * cannot be created due to missing information.
+ */
+fun ActiveNotificationModel.toIconInfo(sourceIcon: Icon?): NotificationIconInfo? {
+    return sourceIcon?.let {
+        groupKey?.let { groupKey ->
+            NotificationIconInfo(
+                sourceIcon = sourceIcon,
+                notifKey = key,
+                groupKey = groupKey,
+            )
+        }
+    }
+}
+
+private val NotificationIconInfo.groupInfo: IconGroupInfo
+    get() = IconGroupInfo(sourceIcon, groupKey)
+
+private data class IconGroupInfo(
+    val sourceIcon: Icon,
+    val groupKey: String,
+) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as IconGroupInfo
+
+        if (groupKey != other.groupKey) return false
+        return sourceIcon.sameAs(other.sourceIcon)
+    }
+
+    override fun hashCode(): Int {
+        var result = groupKey.hashCode()
+        result = 31 * result + sourceIcon.type.hashCode()
+        when (sourceIcon.type) {
+            Icon.TYPE_BITMAP,
+            Icon.TYPE_ADAPTIVE_BITMAP -> {
+                result = 31 * result + sourceIcon.bitmap.hashCode()
+            }
+            Icon.TYPE_DATA -> {
+                result = 31 * result + sourceIcon.dataLength.hashCode()
+                result = 31 * result + sourceIcon.dataOffset.hashCode()
+            }
+            Icon.TYPE_RESOURCE -> {
+                result = 31 * result + sourceIcon.resId.hashCode()
+                result = 31 * result + sourceIcon.resPackage.hashCode()
+            }
+            Icon.TYPE_URI,
+            Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+                result = 31 * result + sourceIcon.uriString.hashCode()
+            }
+        }
+        return result
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 4a823a4..2fffd37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -239,11 +239,11 @@
         })
     }
 
-    fun logNoPulsingNotificationHidden(entry: NotificationEntry) {
+    fun logNoPulsingNotificationHiddenOverride(entry: NotificationEntry) {
         buffer.log(TAG, DEBUG, {
             str1 = entry.logKey
         }, {
-            "No pulsing: notification hidden on lock screen: $str1"
+            "No pulsing: notification hidden on lock screen by override: $str1"
         })
     }
 
@@ -290,11 +290,11 @@
         })
     }
 
-    fun keyguardHideNotification(entry: NotificationEntry) {
+    fun logNoAlertingNotificationHidden(entry: NotificationEntry) {
         buffer.log(TAG, DEBUG, {
             str1 = entry.logKey
         }, {
-            "Keyguard Hide Notification: $str1"
+            "No alerting: notification hidden on lock screen: $str1"
         })
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 6ec9dbe..b0155f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -180,4 +180,9 @@
      * Add a component that can suppress visual interruptions.
      */
     void addSuppressor(NotificationInterruptSuppressor suppressor);
+
+    /**
+     * Remove a component that can suppress visual interruptions.
+     */
+    void removeSuppressor(NotificationInterruptSuppressor suppressor);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 3819843..301ddbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -175,6 +175,11 @@
     }
 
     @Override
+    public void removeSuppressor(NotificationInterruptSuppressor suppressor) {
+        mSuppressors.remove(suppressor);
+    }
+
+    @Override
     public boolean shouldBubbleUp(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.getSbn();
 
@@ -505,7 +510,7 @@
 
         if (entry.getRanking().getLockscreenVisibilityOverride()
                 == Notification.VISIBILITY_PRIVATE) {
-            if (log) mLogger.logNoPulsingNotificationHidden(entry);
+            if (log) mLogger.logNoPulsingNotificationHiddenOverride(entry);
             return false;
         }
 
@@ -536,7 +541,7 @@
         }
 
         if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
-            if (log) mLogger.keyguardHideNotification(entry);
+            if (log) mLogger.logNoAlertingNotificationHidden(entry);
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
index ebdeded..d7f0baf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
@@ -58,6 +58,10 @@
         wrapped.addSuppressor(suppressor)
     }
 
+    override fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
+        wrapped.removeSuppressor(suppressor)
+    }
+
     override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision =
         wrapped.checkHeadsUp(entry, /* log= */ false).let { DecisionImpl.of(it) }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index 454ba02..920bbe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -60,6 +60,13 @@
     fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor)
 
     /**
+     * Removes a [component][suppressor] that can suppress visual interruptions.
+     *
+     * @param[suppressor] the suppressor to remove
+     */
+    fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor)
+
+    /**
      * Decides whether a [notification][entry] should display as heads-up or not, but does not log
      * that decision.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
new file mode 100644
index 0000000..4ef80e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar.notification.interruption
+
+import com.android.internal.logging.UiEventLogger.UiEventEnum
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * A reason why visual interruptions might be suppressed.
+ *
+ * @see VisualInterruptionCondition
+ * @see VisualInterruptionFilter
+ */
+enum class VisualInterruptionType {
+    /* HUN when awake */
+    PEEK,
+
+    /* HUN when dozing */
+    PULSE,
+
+    /* Bubble */
+    BUBBLE
+}
+
+/**
+ * A reason why visual interruptions might be suppressed.
+ *
+ * @see VisualInterruptionCondition
+ * @see VisualInterruptionFilter
+ */
+sealed interface VisualInterruptionSuppressor {
+    /** The type(s) of interruption that this suppresses. */
+    val types: Set<VisualInterruptionType>
+
+    /** A human-readable string to be logged to explain why this suppressed an interruption. */
+    val reason: String
+
+    /** An optional UiEvent ID to be recorded when this suppresses an interruption. */
+    val uiEventId: UiEventEnum?
+}
+
+/** A reason why visual interruptions might be suppressed regardless of the notification. */
+abstract class VisualInterruptionCondition(
+    override val types: Set<VisualInterruptionType>,
+    override val reason: String,
+    override val uiEventId: UiEventEnum? = null
+) : VisualInterruptionSuppressor {
+    /** @return true if these interruptions should be suppressed right now. */
+    abstract fun shouldSuppress(): Boolean
+}
+
+/** A reason why visual interruptions might be suppressed based on the notification. */
+abstract class VisualInterruptionFilter(
+    override val types: Set<VisualInterruptionType>,
+    override val reason: String,
+    override val uiEventId: UiEventEnum? = null
+) : VisualInterruptionSuppressor {
+    /**
+     * @param entry the notification to consider suppressing
+     * @return true if these interruptions should be suppressed for this notification right now
+     */
+    abstract fun shouldSuppress(entry: NotificationEntry): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 661768d..8f1e59d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -121,7 +121,7 @@
 
     private void updateColors() {
         mNormalColor = Utils.getColorAttrDefaultColor(mContext,
-                com.android.internal.R.attr.colorSurface);
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
         mTintedRippleColor = mContext.getColor(
                 R.color.notification_ripple_tinted_color);
         mNormalRippleColor = mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index 892a635..da8c4dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -28,7 +28,6 @@
 
 import androidx.annotation.ColorInt;
 
-import com.android.internal.util.ContrastColorUtil;
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
@@ -98,8 +97,8 @@
     private void resolveThemeTextColors() {
         try (TypedArray ta = mContext.getTheme().obtainStyledAttributes(
                 android.R.style.Theme_DeviceDefault_DayNight, new int[]{
-                        android.R.attr.textColorPrimary,
-                        android.R.attr.textColorSecondary
+                        com.android.internal.R.attr.materialColorOnSurface,
+                        com.android.internal.R.attr.materialColorOnSurfaceVariant
                 })) {
             if (ta != null) {
                 mPrimaryTextColor = ta.getColor(0, mPrimaryTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 62b268b..14593cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -65,10 +65,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -336,10 +336,11 @@
         Drawable person =  mIconFactory.getBaseIconDrawable(mShortcutInfo);
         if (person == null) {
             person = mContext.getDrawable(R.drawable.ic_person).mutate();
-            TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
-            int colorAccent = ta.getColor(0, 0);
+            TypedArray ta = mContext.obtainStyledAttributes(
+                    new int[]{com.android.internal.R.attr.materialColorPrimary});
+            int colorPrimary = ta.getColor(0, 0);
             ta.recycle();
-            person.setTint(colorAccent);
+            person.setTint(colorPrimary);
         }
         ImageView image = findViewById(R.id.conversation_icon);
         image.setImageDrawable(person);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index ff5b9cb..cdf178e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -326,7 +326,8 @@
         if (customBackgroundColor != 0) {
             return customBackgroundColor;
         }
-        return Utils.getColorAttr(mView.getContext(), android.R.attr.colorBackground)
+        return Utils.getColorAttr(mView.getContext(),
+                        com.android.internal.R.attr.materialColorSurfaceContainerHigh)
                 .getDefaultColor();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationIconContainerRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationIconContainerRefactor.kt
new file mode 100644
index 0000000..dee609c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationIconContainerRefactor.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the NotificationIconContainer refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationIconContainerRefactor {
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_ICON_CONTAINER_REFACTOR
+
+    /** Is the refactor enabled? */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.notificationsIconContainerRefactor()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index b92c51f..d35e4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -19,19 +19,23 @@
 import android.view.View
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.policy.ConfigurationController
 import javax.inject.Inject
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.launch
@@ -75,14 +79,25 @@
     fun bind(
         shelf: NotificationShelf,
         viewModel: NotificationShelfViewModel,
+        configuration: ConfigurationState,
+        configurationController: ConfigurationController,
         falsingManager: FalsingManager,
-        featureFlags: FeatureFlags,
         notificationIconAreaController: NotificationIconAreaController,
+        shelfIconViewStore: ShelfNotificationIconViewStore,
     ) {
         ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
         shelf.apply {
-            // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
-            notificationIconAreaController.setShelfIcons(shelfIcons)
+            if (NotificationIconContainerRefactor.isEnabled) {
+                NotificationIconContainerViewBinder.bind(
+                    shelfIcons,
+                    viewModel.icons,
+                    configuration,
+                    configurationController,
+                    shelfIconViewStore,
+                )
+            } else {
+                notificationIconAreaController.setShelfIcons(shelfIcons)
+            }
             repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
                     launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index 5ca8b53..64b5b62c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
 import javax.inject.Inject
@@ -31,6 +32,7 @@
 constructor(
     private val interactor: NotificationShelfInteractor,
     activatableViewModel: ActivatableNotificationViewModel,
+    val icons: NotificationIconContainerShelfViewModel,
 ) : ActivatableNotificationViewModel by activatableViewModel {
     /** Is the shelf allowed to be clickable when it has content? */
     val isClickable: Flow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index a929e4f..0236fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1362,7 +1362,7 @@
         Resources.Theme theme = new ContextThemeWrapper(mContext,
                 com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
         try (TypedArray ta = theme.obtainStyledAttributes(
-                new int[]{com.android.internal.R.attr.colorAccent})) {
+                new int[]{com.android.internal.R.attr.materialColorPrimary})) {
             color = ta.getColor(0, color);
         }
         mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, color);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index fd064ee..cfc433a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -244,10 +244,10 @@
         }
     }
 
-    fun setHeaderForegroundColor(@ColorInt color: Int) {
-        peopleHeaderView?.setForegroundColor(color)
-        silentHeaderView?.setForegroundColor(color)
-        alertingHeaderView?.setForegroundColor(color)
+    fun setHeaderForegroundColors(@ColorInt onSurface: Int, @ColorInt onSurfaceVariant: Int) {
+        peopleHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+        silentHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+        alertingHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1e9cfa8..8babcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -344,7 +344,7 @@
     private boolean mAnimateNextBackgroundTop;
     private boolean mAnimateNextBackgroundBottom;
     private boolean mAnimateNextSectionBoundsChange;
-    private int mBgColor;
+    private @ColorInt int mBgColor;
     private float mDimAmount;
     private ValueAnimator mDimAnimator;
     private final ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
@@ -647,8 +647,8 @@
         mSections = mSectionsManager.createSectionsForBuckets();
 
         mAmbientState = Dependency.get(AmbientState.class);
-        mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
-                .getDefaultColor();
+        mBgColor = Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh).getDefaultColor();
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
         mSplitShadeMinContentHeight = res.getDimensionPixelSize(
@@ -790,8 +790,8 @@
     }
 
     void updateBgColor() {
-        mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
-                .getDefaultColor();
+        mBgColor = Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh).getDefaultColor();
         updateBackgroundDimming();
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
@@ -4421,14 +4421,19 @@
     }
 
     /**
-     * Update colors of "dismiss" and "empty shade" views.
+     * Update colors of section headers, shade footer, and empty shade views.
      */
     void updateDecorViews() {
-        final @ColorInt int textColor =
-                Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
-        mSectionsManager.setHeaderForegroundColor(textColor);
+        final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(
+                mContext, com.android.internal.R.attr.materialColorOnSurface);
+        final @ColorInt int onSurfaceVariant = Utils.getColorAttrDefaultColor(
+                mContext, com.android.internal.R.attr.materialColorOnSurfaceVariant);
+
+        mSectionsManager.setHeaderForegroundColors(onSurface, onSurfaceVariant);
+
         mFooterView.updateColors();
-        mEmptyShadeView.setTextColor(textColor);
+
+        mEmptyShadeView.setTextColors(onSurface, onSurfaceVariant);
     }
 
     void goToFullShade(long delay) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 79448b4..21efd63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -59,6 +59,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
@@ -107,6 +108,7 @@
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -117,10 +119,12 @@
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -212,6 +216,8 @@
     private final SecureSettings mSecureSettings;
     private final NotificationDismissibilityProvider mDismissibilityProvider;
     private final ActivityStarter mActivityStarter;
+    private final ConfigurationState mConfigurationState;
+    private final ShelfNotificationIconViewStore mShelfIconViewStore;
 
     private View mLongPressedView;
 
@@ -674,7 +680,9 @@
             SecureSettings secureSettings,
             NotificationDismissibilityProvider dismissibilityProvider,
             ActivityStarter activityStarter,
-            SplitShadeStateController splitShadeStateController) {
+            SplitShadeStateController splitShadeStateController,
+            ConfigurationState configurationState,
+            ShelfNotificationIconViewStore shelfIconViewStore) {
         mView = view;
         mKeyguardTransitionRepo = keyguardTransitionRepo;
         mStackStateLogger = stackLogger;
@@ -724,6 +732,8 @@
         mSecureSettings = secureSettings;
         mDismissibilityProvider = dismissibilityProvider;
         mActivityStarter = activityStarter;
+        mConfigurationState = configurationState;
+        mShelfIconViewStore = shelfIconViewStore;
         mView.passSplitShadeStateController(splitShadeStateController);
         updateResources();
         setUpView();
@@ -832,8 +842,10 @@
 
         mViewModel.ifPresent(
                 vm -> NotificationListViewBinder
-                        .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController,
-                                mConfigurationController));
+                        .bind(mView, vm, mConfigurationState, mConfigurationController,
+                                mFalsingManager,
+                                mNotifIconAreaController,
+                                mShelfIconViewStore));
 
         collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
                 this::onKeyguardTransitionChanged);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 722c28c..5c1149b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -132,8 +132,8 @@
         mLabelView.setText(resId);
     }
 
-    void setForegroundColor(@ColorInt int color) {
-        mLabelView.setTextColor(color);
-        mClearAllButton.setImageTintList(ColorStateList.valueOf(color));
+    void setForegroundColors(@ColorInt int onSurface, @ColorInt int onSurfaceVariant) {
+        mLabelView.setTextColor(onSurface);
+        mClearAllButton.setImageTintList(ColorStateList.valueOf(onSurfaceVariant));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index a3792cf..95b467f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -17,23 +17,21 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
 import android.view.LayoutInflater
-import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.common.ui.reinflateAndBindLatest
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
 import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
-import com.android.systemui.statusbar.policy.onThemeChanged
 import com.android.systemui.util.traceSection
-import com.android.systemui.util.view.reinflateAndBindLatest
-import kotlinx.coroutines.flow.merge
 
 /** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
 object NotificationListViewBinder {
@@ -41,10 +39,11 @@
     fun bind(
         view: NotificationStackScrollLayout,
         viewModel: NotificationListViewModel,
-        falsingManager: FalsingManager,
-        featureFlags: FeatureFlagsClassic,
-        iconAreaController: NotificationIconAreaController,
+        configuration: ConfigurationState,
         configurationController: ConfigurationController,
+        falsingManager: FalsingManager,
+        iconAreaController: NotificationIconAreaController,
+        shelfIconViewStore: ShelfNotificationIconViewStore,
     ) {
         val shelf =
             LayoutInflater.from(view.context)
@@ -52,28 +51,24 @@
         NotificationShelfViewBinder.bind(
             shelf,
             viewModel.shelf,
+            configuration,
+            configurationController,
             falsingManager,
-            featureFlags,
-            iconAreaController
+            iconAreaController,
+            shelfIconViewStore,
         )
         view.setShelf(shelf)
 
         viewModel.footer.ifPresent { footerViewModel ->
             // The footer needs to be re-inflated every time the theme or the font size changes.
             view.repeatWhenAttached {
-                LayoutInflater.from(view.context).reinflateAndBindLatest(
+                configuration.reinflateAndBindLatest(
                     R.layout.status_bar_notification_footer,
                     view,
                     attachToRoot = false,
-                    // TODO(b/305930747): This may lead to duplicate invocations if both flows emit,
-                    // find a solution to only emit one event.
-                    merge(
-                        configurationController.onThemeChanged,
-                        configurationController.onDensityOrFontScaleChanged,
-                    ),
-                ) { view ->
+                ) { footerView: FooterView ->
                     traceSection("bind FooterView") {
-                        FooterViewBinder.bind(view as FooterView, footerViewModel)
+                        FooterViewBinder.bind(footerView, footerViewModel)
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index daa4f18..cbe9d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -733,8 +733,8 @@
 
         // Suppress all face auth errors if fingerprint can be used to authenticate
         if ((biometricSourceType == BiometricSourceType.FACE
-                && !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
-                mSelectedUserInteractor.get().getSelectedUserId()))
+                && !mUpdateMonitor.isUnlockWithFingerprintPossible(
+                    mSelectedUserInteractor.get().getSelectedUserId()))
                 || (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
             mHapticsInteractor.vibrateError();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index fa2e3a4..897bb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -206,6 +206,7 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -236,8 +237,6 @@
 
 import dalvik.annotation.optimization.NeverCompile;
 
-import dagger.Lazy;
-
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.List;
@@ -249,6 +248,8 @@
 import javax.inject.Named;
 import javax.inject.Provider;
 
+import dagger.Lazy;
+
 /**
  * A class handling initialization and coordination between some of the key central surfaces in
  * System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -809,8 +810,6 @@
                 mShadeExpansionStateManager.addExpansionListener(shadeExpansionListener);
         shadeExpansionListener.onPanelExpansionChanged(currentState);
 
-        mShadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
-
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
         mActivityLaunchAnimator = activityLaunchAnimator;
 
@@ -1397,20 +1396,6 @@
         }
     }
 
-    @VisibleForTesting
-    void onShadeExpansionFullyChanged(Boolean isExpanded) {
-        if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
-            if (DEBUG) {
-                Log.v(TAG, "clearing notification effects from Height");
-            }
-            clearNotificationEffects();
-        }
-
-        if (!isExpanded) {
-            mRemoteInputManager.onPanelCollapsed();
-        }
-    }
-
     @NonNull
     @Override
     public Lifecycle getLifecycle() {
@@ -2526,7 +2511,7 @@
                                     && mFingerprintManager.get() != null
                                     && mFingerprintManager.get().isPowerbuttonFps()
                                     && mKeyguardUpdateMonitor
-                                    .getCachedIsUnlockWithFingerprintPossible(
+                                    .isUnlockWithFingerprintPossible(
                                             mUserTracker.getUserId())
                                     && !touchToUnlockAnytime;
                     if (DEBUG_WAKEUP_DELAY) {
@@ -2635,7 +2620,7 @@
                 !mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive());
 
         mShadeSurface.setTouchAndAnimationDisabled(disabled);
-        if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (!NotificationIconContainerRefactor.isEnabled()) {
             mNotificationIconAreaController.setAnimationsEnabled(!disabled);
         }
     }
@@ -3102,7 +3087,7 @@
             }
             // TODO: Bring these out of CentralSurfaces.
             mUserInfoControllerImpl.onDensityOrFontScaleChanged();
-            if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+            if (!NotificationIconContainerRefactor.isEnabled()) {
                 mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
             }
         }
@@ -3122,7 +3107,7 @@
             if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
                 ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
             }
-            if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+            if (!NotificationIconContainerRefactor.isEnabled()) {
                 mNotificationIconAreaController.onThemeChanged();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 66341ba..600d4af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -38,7 +38,6 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -49,18 +48,18 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.util.Assert;
 
-import dagger.Lazy;
-
 import java.util.ArrayList;
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 /**
@@ -178,7 +177,7 @@
 
     void fireNotificationPulse(NotificationEntry entry) {
         Runnable pulseSuppressedListener = () -> {
-            if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+            if (NotificationIconContainerRefactor.isEnabled()) {
                 mHeadsUpManager.removeNotification(
                         entry.getKey(), /* releaseImmediately= */ true, /* animate= */ false);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 8fee5c0..beeee1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -27,7 +27,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
@@ -42,6 +41,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
@@ -179,7 +179,7 @@
         mHeadsUpManager.addListener(this);
         mView.setOnDrawingRectChangedListener(
                 () -> updateIsolatedIconLocation(true /* requireUpdate */));
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             updateIsolatedIconLocation(true);
         }
         mWakeUpCoordinator.addListener(this);
@@ -197,7 +197,7 @@
     protected void onViewDetached() {
         mHeadsUpManager.removeListener(this);
         mView.setOnDrawingRectChangedListener(null);
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             mHeadsUpNotificationIconInteractor.setIsolatedIconLocation(null);
         }
         mWakeUpCoordinator.removeListener(this);
@@ -208,7 +208,7 @@
     }
 
     private void updateIsolatedIconLocation(boolean requireStateUpdate) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             mHeadsUpNotificationIconInteractor
                     .setIsolatedIconLocation(mView.getIconDrawingRect());
         } else {
@@ -250,7 +250,7 @@
                 setShown(true);
                 animateIsolation = !isExpanded();
             }
-            if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+            if (NotificationIconContainerRefactor.isEnabled()) {
                 mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
                         newEntry == null ? null : newEntry.getKey());
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index f4862c7..3a95e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -34,7 +34,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.res.R;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -105,7 +106,8 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  Constructor:
     @Inject
-    public HeadsUpManagerPhone(@NonNull final Context context,
+    public HeadsUpManagerPhone(
+            @NonNull final Context context,
             HeadsUpManagerLogger logger,
             StatusBarStateController statusBarStateController,
             KeyguardBypassController bypassController,
@@ -115,7 +117,8 @@
             @Main Handler handler,
             AccessibilityManagerWrapper accessibilityManagerWrapper,
             UiEventLogger uiEventLogger,
-            ShadeExpansionStateManager shadeExpansionStateManager) {
+            JavaAdapter javaAdapter,
+            ShadeInteractor shadeInteractor) {
         super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
         Resources resources = mContext.getResources();
         mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
@@ -136,8 +139,7 @@
                 updateResources();
             }
         });
-
-        shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+        javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
     }
 
     public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -230,7 +232,7 @@
         mTrackingHeadsUp = trackingHeadsUp;
     }
 
-    private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+    private void onShadeOrQsExpanded(Boolean isExpanded) {
         if (isExpanded != mIsExpanded) {
             mIsExpanded = isExpanded;
             if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 63591d7..b0183d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -58,7 +58,7 @@
     private var pendingUnlock: PendingUnlock? = null
     private val listeners = mutableListOf<OnBypassStateChangedListener>()
     private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
-        override fun onFaceAuthEnabledChanged() = notifyListeners()
+        override fun onFaceEnrolledChanged() = notifyListeners()
     }
 
     @IntDef(
@@ -98,7 +98,7 @@
                 FACE_UNLOCK_BYPASS_NEVER -> false
                 else -> field
             }
-            return enabled && mKeyguardStateController.isFaceAuthEnabled &&
+            return enabled && mKeyguardStateController.isFaceEnrolled &&
                     isPostureAllowedForFaceAuth()
         }
         private set(value) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 109e77e..3329844 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -117,9 +117,7 @@
         val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
                 !statusBarStateController.isDozing
 
-        val userId = selectedUserInteractor.getSelectedUserId()
-        val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId)
-        val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
+        val shouldListen = (onKeyguard || bouncerVisible) && keyguardUpdateMonitor.isFaceEnrolled
         if (shouldListen != isListening) {
             isListening = shouldListen
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index 4284c96c..dd32434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -481,7 +481,7 @@
             hostLayout.addView(expected, i);
         }
         hostLayout.setChangingViewPositions(false);
-        hostLayout.setReplacingIcons(null);
+        hostLayout.setReplacingIconsLegacy(null);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt
index 0079f7c..2823b28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt
@@ -28,7 +28,7 @@
  */
 interface NotificationIconAreaController {
     /** Called by the Keyguard*ViewController whose view contains the aod icons. */
-    fun setupAodIcons(aodIcons: NotificationIconContainer)
+    fun setupAodIcons(aodIcons: NotificationIconContainer?)
     fun setupShelf(notificationShelfController: NotificationShelfController)
     fun setShelfIcons(icons: NotificationIconContainer)
     fun onDensityOrFontScaleChanged(context: Context)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
index d1ddd51..ba69370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
@@ -15,9 +15,8 @@
  */
 package com.android.systemui.statusbar.phone
 
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconAreaControllerViewBinderWrapperImpl
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import dagger.Module
 import dagger.Provides
 import javax.inject.Provider
@@ -26,11 +25,10 @@
 object NotificationIconAreaControllerModule {
     @Provides
     fun provideNotificationIconAreaControllerImpl(
-        featureFlags: FeatureFlags,
         legacyProvider: Provider<LegacyNotificationIconAreaControllerImpl>,
         newProvider: Provider<NotificationIconAreaControllerViewBinderWrapperImpl>,
     ): NotificationIconAreaController =
-        if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled) {
             newProvider.get()
         } else {
             legacyProvider.get()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 535f6ac..efb8e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -40,10 +40,9 @@
 import com.android.app.animation.Interpolators;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.settingslib.Utils;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.RefactorFlag;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.notification.stack.AnimationFilter;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -133,9 +132,6 @@
         }
     }.setDuration(CONTENT_FADE_DURATION);
 
-    private final RefactorFlag mIconContainerRefactorFlag =
-            RefactorFlag.forView(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
-
     /* Maximum number of icons on AOD when also showing overflow dot. */
     private int mMaxIconsOnAod;
 
@@ -352,7 +348,7 @@
         StatusBarIconView iconView = (StatusBarIconView) child;
         Icon sourceIcon = iconView.getSourceIcon();
         String groupKey = iconView.getNotification().getGroupKey();
-        if (mIconContainerRefactorFlag.isEnabled()) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             if (mReplacingIcons == null) {
                 return false;
             }
@@ -695,18 +691,18 @@
     }
 
     public void setReplacingIconsLegacy(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) {
-        mIconContainerRefactorFlag.assertInLegacyMode();
+        NotificationIconContainerRefactor.assertInLegacyMode();
         mReplacingIconsLegacy = replacingIcons;
     }
 
     public void setReplacingIcons(ArrayMap<String, StatusBarIcon> replacingIcons) {
-        if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return;
+        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
         mReplacingIcons = replacingIcons;
     }
 
     @Deprecated
     public void showIconIsolated(StatusBarIconView icon, boolean animated) {
-        mIconContainerRefactorFlag.assertInLegacyMode();
+        NotificationIconContainerRefactor.assertInLegacyMode();
         if (animated) {
             showIconIsolatedAnimated(icon, null);
         } else {
@@ -716,14 +712,14 @@
 
     public void showIconIsolatedAnimated(StatusBarIconView icon,
             @Nullable Runnable onAnimationEnd) {
-        if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return;
+        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
         mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
         mIsolatedIconAnimationEndRunnable = onAnimationEnd;
         showIconIsolated(icon);
     }
 
     public void showIconIsolated(StatusBarIconView icon) {
-        if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return;
+        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
         mIsolatedIcon = icon;
         updateState();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 744d70e..3e753a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1505,8 +1505,9 @@
     private void updateThemeColors() {
         if (mScrimBehind == null) return;
         int background = Utils.getColorAttr(mScrimBehind.getContext(),
-                android.R.attr.colorBackgroundFloating).getDefaultColor();
-        int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor();
+                com.android.internal.R.attr.materialColorSurfaceDim).getDefaultColor();
+        int accent = Utils.getColorAttr(mScrimBehind.getContext(),
+                com.android.internal.R.attr.materialColorPrimary).getDefaultColor();
         mColors.setMainColor(background);
         mColors.setSecondaryColor(accent);
         final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
index 4d9de09..fa6d279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -3,12 +3,15 @@
 import android.app.StatusBarManager
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -25,14 +28,14 @@
  */
 @SysUISingleton
 class StatusBarHideIconsForBouncerManager @Inject constructor(
-        private val commandQueue: CommandQueue,
-        @Main private val mainExecutor: DelayableExecutor,
-        statusBarWindowStateController: StatusBarWindowStateController,
-        shadeExpansionStateManager: ShadeExpansionStateManager,
-        dumpManager: DumpManager
+    @Application private val scope: CoroutineScope,
+    private val commandQueue: CommandQueue,
+    @Main private val mainExecutor: DelayableExecutor,
+    statusBarWindowStateController: StatusBarWindowStateController,
+    val shadeInteractor: ShadeInteractor,
+    dumpManager: DumpManager
 ) : Dumpable {
     // State variables set by external classes.
-    private var panelExpanded: Boolean = false
     private var isOccluded: Boolean = false
     private var bouncerShowing: Boolean = false
     private var topAppHidesStatusBar: Boolean = false
@@ -49,10 +52,9 @@
         statusBarWindowStateController.addListener {
                 state -> setStatusBarStateAndTriggerUpdate(state)
         }
-        shadeExpansionStateManager.addFullExpansionListener { isExpanded ->
-            if (panelExpanded != isExpanded) {
-                panelExpanded = isExpanded
-                updateHideIconsForBouncer(animate = false)
+        scope.launch {
+            shadeInteractor.isAnyExpanded.collect {
+                updateHideIconsForBouncer(false)
             }
         }
     }
@@ -101,7 +103,7 @@
             topAppHidesStatusBar &&
                     isOccluded &&
                     (statusBarWindowHidden || bouncerShowing)
-        val hideBecauseKeyguard = !panelExpanded && !isOccluded && bouncerShowing
+        val hideBecauseKeyguard = !isShadeOrQsExpanded() && !isOccluded && bouncerShowing
         val shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard
         if (hideIconsForBouncer != shouldHideIconsForBouncer) {
             hideIconsForBouncer = shouldHideIconsForBouncer
@@ -125,9 +127,13 @@
         }
     }
 
+    private fun isShadeOrQsExpanded(): Boolean {
+        return shadeInteractor.isAnyExpanded.value
+    }
+
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println("---- State variables set externally ----")
-        pw.println("panelExpanded=$panelExpanded")
+        pw.println("isShadeOrQsExpanded=${isShadeOrQsExpanded()}")
         pw.println("isOccluded=$isOccluded")
         pw.println("bouncerShowing=$bouncerShowing")
         pw.println("topAppHideStatusBar=$topAppHidesStatusBar")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index ba73c10..6d8ec44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -39,6 +39,7 @@
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.SceneContainerFlags;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -86,8 +87,9 @@
             ConfigurationController configurationController,
             HeadsUpManager headsUpManager,
             ShadeExpansionStateManager shadeExpansionStateManager,
+            ShadeInteractor shadeInteractor,
             Provider<SceneInteractor> sceneInteractor,
-            Provider<JavaAdapter> javaAdapter,
+            JavaAdapter javaAdapter,
             SceneContainerFlags sceneContainerFlags,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             PrimaryBouncerInteractor primaryBouncerInteractor,
@@ -126,12 +128,12 @@
         });
 
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+        javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
 
         if (sceneContainerFlags.isEnabled()) {
-            javaAdapter.get().alwaysCollectFlow(
+            javaAdapter.alwaysCollectFlow(
                     sceneInteractor.get().isVisible(),
-                    this::onShadeExpansionFullyChanged);
+                    this::onShadeOrQsExpanded);
         }
 
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
@@ -151,7 +153,7 @@
         pw.println(mTouchableRegion);
     }
 
-    private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+    private void onShadeOrQsExpanded(Boolean isExpanded) {
         if (isExpanded != mIsStatusBarExpanded) {
             mIsStatusBarExpanded = isExpanded;
             if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
index a033e1d..66ac17e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.annotation.ColorInt;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -28,6 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.res.R;
 import com.android.wm.shell.animation.Interpolators;
 
@@ -49,8 +51,9 @@
     }
 
     void updateColor() {
-        int textColor = getResources().getColor(R.color.notif_pill_text, mContext.getTheme());
-        setTextColor(textColor);
+        final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.materialColorOnSurface);
+        setTextColor(onSurface);
         setBackground(getResources().getDrawable(R.drawable.rounded_bg_full, mContext.getTheme()));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index e2a4714..3921e69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -44,7 +44,6 @@
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -59,12 +58,10 @@
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel;
-import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -86,6 +83,8 @@
 import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
 import com.android.systemui.util.settings.SecureSettings;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -96,8 +95,6 @@
 
 import javax.inject.Inject;
 
-import kotlin.Unit;
-
 /**
  * Contains the collapsed status bar and handles hiding/showing based on disable flags
  * and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -155,12 +152,10 @@
     private final DumpManager mDumpManager;
     private final StatusBarWindowStateController mStatusBarWindowStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final NotificationIconContainerViewModel mStatusBarIconsViewModel;
+    private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel;
     private final ConfigurationState mConfigurationState;
     private final ConfigurationController mConfigurationController;
-    private final DozeParameters mDozeParameters;
-    private final ScreenOffAnimationController mScreenOffAnimationController;
-    private final NotificationIconContainerViewBinder.IconViewStore mStatusBarIconViewStore;
+    private final StatusBarNotificationIconViewStore mStatusBarIconViewStore;
     private final DemoModeController mDemoModeController;
 
     private List<String> mBlockedIcons = new ArrayList<>();
@@ -252,8 +247,6 @@
             NotificationIconContainerStatusBarViewModel statusBarIconsViewModel,
             ConfigurationState configurationState,
             ConfigurationController configurationController,
-            DozeParameters dozeParameters,
-            ScreenOffAnimationController screenOffAnimationController,
             StatusBarNotificationIconViewStore statusBarIconViewStore,
             DemoModeController demoModeController) {
         mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
@@ -283,8 +276,6 @@
         mStatusBarIconsViewModel = statusBarIconsViewModel;
         mConfigurationState = configurationState;
         mConfigurationController = configurationController;
-        mDozeParameters = dozeParameters;
-        mScreenOffAnimationController = screenOffAnimationController;
         mStatusBarIconViewStore = statusBarIconViewStore;
         mDemoModeController = demoModeController;
     }
@@ -317,7 +308,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener);
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             mDemoModeController.addCallback(mDemoModeCallback);
         }
     }
@@ -326,7 +317,7 @@
     public void onDestroy() {
         super.onDestroy();
         mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener);
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             mDemoModeController.removeCallback(mDemoModeCallback);
         }
     }
@@ -469,7 +460,7 @@
     /** Initializes views related to the notification icon area. */
     public void initNotificationIconArea() {
         ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area);
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+        if (NotificationIconContainerRefactor.isEnabled()) {
             mNotificationIconAreaInner =
                 LayoutInflater.from(getContext())
                         .inflate(R.layout.notification_icon_area, notificationIconArea, true);
@@ -480,9 +471,6 @@
                     mStatusBarIconsViewModel,
                     mConfigurationState,
                     mConfigurationController,
-                    mDozeParameters,
-                    mFeatureFlags,
-                    mScreenOffAnimationController,
                     mStatusBarIconViewStore);
         } else {
             mNotificationIconAreaInner =
@@ -606,7 +594,7 @@
 
         // Hide notifications if the disable flag is set or we have an ongoing call.
         if (disableNotifications || hasOngoingCall) {
-            hideNotificationIconArea(animate);
+            hideNotificationIconArea(animate && !hasOngoingCall);
         } else {
             showNotificationIconArea(animate);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 8929e02..52133ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -130,7 +130,7 @@
     /**
      * If there are faces enrolled and user enabled face auth on keyguard.
      */
-    default boolean isFaceAuthEnabled() {
+    default boolean isFaceEnrolled() {
         return false;
     }
 
@@ -265,9 +265,9 @@
 
         /**
          * Triggered when face auth becomes available or unavailable. Value should be queried with
-         * {@link KeyguardStateController#isFaceAuthEnabled()}.
+         * {@link KeyguardStateController#isFaceEnrolled()}.
          */
-        default void onFaceAuthEnabledChanged() {}
+        default void onFaceEnrolledChanged() {}
 
         /**
          * Triggered when the notification panel is starting or has finished
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index c624518..8cc7e7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -85,7 +85,7 @@
     private boolean mTrustManaged;
     private boolean mTrusted;
     private boolean mDebugUnlocked = false;
-    private boolean mFaceAuthEnabled;
+    private boolean mFaceEnrolled;
 
     private float mDismissAmount = 0f;
     private boolean mDismissingFromTouch = false;
@@ -216,7 +216,7 @@
 
     private void notifyKeyguardFaceAuthEnabledChanged() {
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(Callback::onFaceAuthEnabledChanged);
+        new ArrayList<>(mCallbacks).forEach(Callback::onFaceEnrolledChanged);
     }
 
     private void notifyUnlockedChanged() {
@@ -260,16 +260,16 @@
                 || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
         boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
         boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
-        boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
+        boolean faceEnrolled = mKeyguardUpdateMonitor.isFaceEnrolled(user);
         boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen
                 || trustManaged != mTrustManaged || mTrusted != trusted
-                || mFaceAuthEnabled != faceAuthEnabled;
+                || mFaceEnrolled != faceEnrolled;
         if (changed || updateAlways) {
             mSecure = secure;
             mCanDismissLockScreen = canDismissLockScreen;
             mTrusted = trusted;
             mTrustManaged = trustManaged;
-            mFaceAuthEnabled = faceAuthEnabled;
+            mFaceEnrolled = faceEnrolled;
             mLogger.logKeyguardStateUpdate(
                     mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged);
             notifyUnlockedChanged();
@@ -290,8 +290,8 @@
     }
 
     @Override
-    public boolean isFaceAuthEnabled() {
-        return mFaceAuthEnabled;
+    public boolean isFaceEnrolled() {
+        return mFaceEnrolled;
     }
 
     @Override
@@ -416,7 +416,7 @@
         pw.println("  mTrustManaged: " + mTrustManaged);
         pw.println("  mTrusted: " + mTrusted);
         pw.println("  mDebugUnlocked: " + mDebugUnlocked);
-        pw.println("  mFaceAuthEnabled: " + mFaceAuthEnabled);
+        pw.println("  mFaceEnrolled: " + mFaceEnrolled);
         pw.println("  isKeyguardFadingAway: " + isKeyguardFadingAway());
         pw.println("  isKeyguardGoingAway: " + isKeyguardGoingAway());
         pw.println("  isLaunchTransitionFadingAway: " + isLaunchTransitionFadingAway());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index ceed81a..4864fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -130,7 +130,7 @@
     private ImageView mDelete;
     private ImageView mDeleteBg;
     private boolean mColorized;
-    private int mTint;
+    private int mLastBackgroundColor;
     private boolean mResetting;
     @Nullable
     private RevealParams mRevealParams;
@@ -181,10 +181,9 @@
         mEditorActionHandler = new EditorActionHandler();
         mUiEventLogger = Dependency.get(UiEventLogger.class);
         TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{
-                com.android.internal.R.attr.colorAccent,
-                com.android.internal.R.attr.colorSurface,
+                com.android.internal.R.attr.materialColorSurfaceDim,
         });
-        mTint = ta.getColor(0, 0);
+        mLastBackgroundColor = ta.getColor(0, 0);
         ta.recycle();
     }
 
@@ -210,9 +209,9 @@
      * @param backgroundColor colorized notification color
      */
     public void setBackgroundTintColor(final int backgroundColor, boolean colorized) {
-        if (colorized == mColorized && backgroundColor == mTint) return;
+        if (colorized == mColorized && backgroundColor == mLastBackgroundColor) return;
         mColorized = colorized;
-        mTint = backgroundColor;
+        mLastBackgroundColor = backgroundColor;
         final int editBgColor;
         final int deleteBgColor;
         final int deleteFgColor;
@@ -237,8 +236,8 @@
             hintColor = mContext.getColor(R.color.remote_input_hint);
             deleteFgColor = textColor.getDefaultColor();
             try (TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{
-                    com.android.internal.R.attr.colorSurfaceHighlight,
-                    com.android.internal.R.attr.colorSurfaceVariant
+                    com.android.internal.R.attr.materialColorSurfaceDim,
+                    com.android.internal.R.attr.materialColorSurfaceVariant
             })) {
                 editBgColor = ta.getColor(0, backgroundColor);
                 deleteBgColor = ta.getColor(1, Color.GRAY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
index eaae0f0..fa6ea4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -30,10 +30,13 @@
 import android.util.Log
 import android.view.View.MeasureSpec
 import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.Dependency
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.shade.ShadeLogger
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.util.ViewController
 import com.android.systemui.util.time.SystemClock
 import java.text.FieldPosition
@@ -83,7 +86,7 @@
 class VariableDateViewController(
     private val systemClock: SystemClock,
     private val broadcastDispatcher: BroadcastDispatcher,
-    private val shadeExpansionStateManager: ShadeExpansionStateManager,
+    private val shadeInteractor: ShadeInteractor,
     private val shadeLogger: ShadeLogger,
     private val timeTickHandler: Handler,
     view: VariableDateView
@@ -174,8 +177,11 @@
 
         broadcastDispatcher.registerReceiver(intentReceiver, filter,
                 HandlerExecutor(timeTickHandler), UserHandle.SYSTEM)
-
-        shadeExpansionStateManager.addQsExpansionFractionListener(::onQsExpansionFractionChanged)
+        mView.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                shadeInteractor.qsExpansion.collect(::onQsExpansionFractionChanged)
+            }
+        }
         post(::updateClock)
         mView.onAttach(onMeasureListener)
     }
@@ -183,7 +189,6 @@
     override fun onViewDetached() {
         dateFormat = null
         mView.onAttach(null)
-        shadeExpansionStateManager.removeQsExpansionFractionListener(::onQsExpansionFractionChanged)
         broadcastDispatcher.unregisterReceiver(intentReceiver)
     }
 
@@ -237,18 +242,18 @@
     class Factory @Inject constructor(
         private val systemClock: SystemClock,
         private val broadcastDispatcher: BroadcastDispatcher,
-        private val shadeExpansionStateManager: ShadeExpansionStateManager,
+        private val shadeInteractor: ShadeInteractor,
         private val shadeLogger: ShadeLogger,
         @Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
     ) {
         fun create(view: VariableDateView): VariableDateViewController {
             return VariableDateViewController(
-                    systemClock,
-                    broadcastDispatcher,
-                    shadeExpansionStateManager,
-                    shadeLogger,
-                    handler,
-                    view
+                systemClock,
+                broadcastDispatcher,
+                shadeInteractor,
+                shadeLogger,
+                handler,
+                view
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/util/view/DisposableHandleExt.kt b/packages/SystemUI/src/com/android/systemui/util/view/DisposableHandleExt.kt
new file mode 100644
index 0000000..d3653b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/view/DisposableHandleExt.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.util.view
+
+import android.view.View
+import com.android.systemui.util.kotlin.awaitCancellationThenDispose
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+
+/**
+ * Use the [bind] method to bind the view every time this flow emits, and suspend to await for more
+ * updates. New emissions lead to the previous binding call being cancelled if not completed.
+ * Dispose of the [DisposableHandle] returned by [bind] when done.
+ */
+suspend fun <T : View> Flow<T>.bindLatest(bind: (T) -> DisposableHandle?) {
+    this.collectLatest { view ->
+        val disposableHandle = bind(view)
+        disposableHandle?.awaitCancellationThenDispose()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/view/LayoutInflaterExt.kt b/packages/SystemUI/src/com/android/systemui/util/view/LayoutInflaterExt.kt
deleted file mode 100644
index 6d45d23..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/view/LayoutInflaterExt.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2023 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.systemui.util.view
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.util.kotlin.awaitCancellationThenDispose
-import com.android.systemui.util.kotlin.stateFlow
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Perform an inflation right away, then re-inflate whenever the [flow] emits, and call [onInflate]
- * on the resulting view each time. Dispose of the [DisposableHandle] returned by [onInflate] when
- * done.
- *
- * This never completes unless cancelled, it just suspends and waits for updates.
- *
- * For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate].
- *
- * An example use-case of this is when a view needs to be re-inflated whenever a configuration
- * change occurs, which would require the ViewBinder to then re-bind the new view. For example, the
- * code in the parent view's binder would look like:
- * ```
- * parentView.repeatWhenAttached {
- *     LayoutInflater.from(parentView.context)
- *         .reinflateOnChange(
- *             R.layout.my_layout,
- *             parentView,
- *             attachToRoot = false,
- *             coroutineScope = lifecycleScope,
- *             configurationController.onThemeChanged,
- *             ),
- *     ) { view ->
- *         ChildViewBinder.bind(view as ChildView, childViewModel)
- *     }
- * }
- * ```
- *
- * In turn, the bind method (passed through [onInflate]) uses [repeatWhenAttached], which returns a
- * [DisposableHandle].
- */
-suspend fun LayoutInflater.reinflateAndBindLatest(
-    resource: Int,
-    root: ViewGroup?,
-    attachToRoot: Boolean,
-    flow: Flow<Unit>,
-    onInflate: (View) -> DisposableHandle?,
-) = coroutineScope {
-    val viewFlow: Flow<View> = stateFlow(flow) { inflate(resource, root, attachToRoot) }
-    viewFlow.bindLatest(onInflate)
-}
-
-/**
- * Use the [bind] method to bind the view every time this flow emits, and suspend to await for more
- * updates. New emissions lead to the previous binding call being cancelled if not completed.
- * Dispose of the [DisposableHandle] returned by [bind] when done.
- */
-suspend fun Flow<View>.bindLatest(bind: (View) -> DisposableHandle?) {
-    this.collectLatest { view ->
-        val disposableHandle = bind(view)
-        disposableHandle?.awaitCancellationThenDispose()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0ff308e..280c66a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -99,7 +99,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -256,7 +255,6 @@
     private CaptionsToggleImageButton mODICaptionsIcon;
     private View mSettingsView;
     private ImageButton mSettingsIcon;
-    private FrameLayout mZenIcon;
     private final List<VolumeRow> mRows = new ArrayList<>();
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -633,7 +631,6 @@
         mRinger = mDialog.findViewById(R.id.ringer);
         if (mRinger != null) {
             mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
-            mZenIcon = mRinger.findViewById(R.id.dnd_icon);
         }
 
         mSelectedRingerIcon = mDialog.findViewById(R.id.volume_new_ringer_active_icon);
@@ -847,7 +844,6 @@
         if (stream == STREAM_ACCESSIBILITY) {
             row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
         }
-        row.dndIcon = row.view.findViewById(R.id.dnd_icon);
         row.slider = row.view.findViewById(R.id.volume_row_slider);
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.number = row.view.findViewById(R.id.volume_number);
@@ -1791,27 +1787,13 @@
     }
 
     /**
-     * Toggles enable state of views in a VolumeRow (not including seekbar or icon)
-     * Hides/shows zen icon
-     * @param enable whether to enable volume row views and hide dnd icon
-     */
-    private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
-        boolean showDndIcon = !enable;
-        row.dndIcon.setVisibility(showDndIcon ? VISIBLE : GONE);
-    }
-
-    /**
      * Toggles enable state of footer/ringer views
-     * Hides/shows zen icon
-     * @param enable whether to enable ringer views and hide dnd icon
+     * @param enable whether to enable ringer views
      */
     private void enableRingerViewsH(boolean enable) {
         if (mRingerIcon != null) {
             mRingerIcon.setEnabled(enable);
         }
-        if (mZenIcon != null) {
-            mZenIcon.setVisibility(enable ? GONE : VISIBLE);
-        }
     }
 
     private void trimObsoleteH() {
@@ -1937,9 +1919,11 @@
         // update icon
         final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
         final int iconRes;
-        if (isRingVibrate) {
+        if (zenMuted) {
+            iconRes = com.android.internal.R.drawable.ic_qs_dnd;
+        } else if (isRingVibrate) {
             iconRes = R.drawable.ic_volume_ringer_vibrate;
-        } else if (isRingSilent || zenMuted) {
+        } else if (isRingSilent) {
             iconRes = row.iconMuteRes;
         } else if (ss.routedToBluetooth) {
             if (isVoiceCallStream) {
@@ -2011,7 +1995,6 @@
         if (zenMuted) {
             row.tracking = false;
         }
-        enableVolumeRowViewsH(row, !zenMuted);
 
         // update slider
         final boolean enableSlider = !zenMuted;
@@ -2582,7 +2565,6 @@
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
-        private FrameLayout dndIcon;
 
         void setIcon(int iconRes, Resources.Theme theme) {
             if (icon != null) {
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
index f49ba64..0cb913b 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager
 import android.os.UserManager
 import android.util.DisplayMetrics
+import android.view.LayoutInflater
 import com.android.internal.logging.MetricsLogger
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -37,6 +38,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationListener
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationMediaManager
@@ -75,6 +77,9 @@
     @get:Provides val keyguardBypassController: KeyguardBypassController = mock(),
     @get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(),
     @get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(),
+    @get:Provides val layoutInflater: LayoutInflater = mock(),
+    @get:Provides
+    val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock(),
     @get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(),
     @get:Provides val notifCollection: NotifCollection = mock(),
     @get:Provides val notificationListener: NotificationListener = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index b31f630a4..e429446 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -262,7 +262,7 @@
         // GIVEN fingerprint and face are NOT enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
         `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
-        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // WHEN unlock intent is allowed when NO biometrics are enrolled (0)
 
@@ -292,7 +292,7 @@
         // GIVEN fingerprint and face are both enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
         `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
-        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
 
         // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
         // are enrolled
@@ -315,7 +315,7 @@
 
         // WHEN fingerprint ONLY enrolled
         `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
-        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
 
         // THEN active unlock triggers allowed on unlock intent
         assertTrue(
@@ -326,7 +326,7 @@
 
         // WHEN face ONLY enrolled
         `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
-        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // THEN active unlock triggers allowed on unlock intent
         assertTrue(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 5273e0e5..603d548 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -35,8 +35,8 @@
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -50,12 +50,18 @@
 import com.android.systemui.plugins.ClockFaceEvents;
 import com.android.systemui.plugins.ClockTickRate;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.shared.clocks.AnimatableClockView;
 import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -176,12 +182,18 @@
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
                 mSmartspaceController,
+                mock(ConfigurationController.class),
+                mock(ScreenOffAnimationController.class),
                 mKeyguardUnlockAnimationController,
                 mSecureSettings,
                 mExecutor,
                 mDumpManager,
                 mClockEventController,
                 mLogBuffer,
+                mock(NotificationIconContainerAlwaysOnDisplayViewModel.class),
+                mock(ConfigurationState.class),
+                mock(DozeParameters.class),
+                mock(AlwaysOnDisplayNotificationIconViewStore.class),
                 KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
                 mFakeFeatureFlags
         );
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index aabdcb7..efb0887 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -414,6 +414,7 @@
     }
 
     private void setupFingerprintAuth(boolean isClass3) throws RemoteException {
+        when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true);
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
         when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
         mFingerprintSensorProperties = List.of(
@@ -2898,18 +2899,22 @@
 
     @Test
     public void testFaceSensorProperties() throws RemoteException {
+        // GIVEN no face sensor properties
+        when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
         mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>());
 
-        assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
+        // THEN face is not possible
+        assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
                 mSelectedUserInteractor.getSelectedUserId())).isFalse();
 
+        // WHEN there are face sensor properties
         mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
-        biometricsEnabledForCurrentUser();
 
+        // THEN face is possible but face does NOT start listening immediately
+        assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
+                mSelectedUserInteractor.getSelectedUserId())).isTrue();
         verifyFaceAuthenticateNeverCalled();
         verifyFaceDetectNeverCalled();
-        assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
-                mSelectedUserInteractor.getSelectedUserId())).isTrue();
     }
 
     @Test
@@ -3167,10 +3172,6 @@
     }
 
     private void mockCanBypassLockscreen(boolean canBypass) {
-        // force update the isFaceEnrolled cache:
-        mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
-                mSelectedUserInteractor.getSelectedUserId());
-
         mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
         when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
deleted file mode 100644
index 89389b0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2022 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.systemui.accessibility;
-
-import android.os.RemoteException;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class MockMagnificationAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub {
-
-    private final CountDownLatch mCountDownLatch;
-    private final AtomicInteger mSuccessCount;
-    private final AtomicInteger mFailedCount;
-
-    MockMagnificationAnimationCallback(CountDownLatch countDownLatch) {
-        mCountDownLatch = countDownLatch;
-        mSuccessCount = new AtomicInteger();
-        mFailedCount = new AtomicInteger();
-    }
-
-    public int getSuccessCount() {
-        return mSuccessCount.get();
-    }
-
-    public int getFailedCount() {
-        return mFailedCount.get();
-    }
-
-    @Override
-    public void onResult(boolean success) throws RemoteException {
-        if (success) {
-            mSuccessCount.getAndIncrement();
-        } else {
-            mFailedCount.getAndIncrement();
-        }
-        // It should be put at the last line to avoid making CountDownLatch#await passed without
-        // updating values.
-        mCountDownLatch.countDown();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index f15164e..284c273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -35,7 +35,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
@@ -68,7 +67,6 @@
 
 @LargeTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
 public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
 
     @Rule
@@ -135,7 +133,9 @@
 
     @After
     public void tearDown() throws Exception {
-        mController.deleteWindowMagnification();
+        mInstrumentation.runOnMainSync(() -> {
+            mController.deleteWindowMagnification();
+        });
     }
 
     @Test
@@ -170,8 +170,7 @@
         final float targetCenterX = DEFAULT_CENTER_X + 100;
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, null);
+        enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
 
         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
     }
@@ -179,10 +178,11 @@
     @Test
     public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
             throws RemoteException {
-        mWindowMagnificationAnimationController.enableWindowMagnification(1,
+        enableWindowMagnificationAndWaitAnimating(
+                mWaitAnimationDuration, /* targetScale= */ 1.0f,
                 DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback);
 
-        verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X,
+        verify(mSpyController).enableWindowMagnificationInternal(1.0f, DEFAULT_CENTER_X,
                 DEFAULT_CENTER_Y, 0f, 0f);
         verify(mAnimationCallback).onResult(true);
     }
@@ -196,13 +196,15 @@
         final float targetCenterX = DEFAULT_CENTER_X + 100;
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
-        Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, mAnimationCallback2);
-        mCurrentScale.set(mController.getScale());
-        mCurrentCenterX.set(mController.getCenterX());
-        mCurrentCenterY.set(mController.getCenterY());
-        advanceTimeBy(mWaitAnimationDuration);
+        resetMockObjects();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY, mAnimationCallback2);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
@@ -224,9 +226,8 @@
         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
                 mAnimationCallback);
 
-        mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+        enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, Float.NaN,
                 Float.NaN, Float.NaN, mAnimationCallback2);
-        advanceTimeBy(mWaitAnimationDuration);
 
         // The callback in 2nd enableWindowMagnification will return true
         verify(mAnimationCallback2).onResult(true);
@@ -245,12 +246,14 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, mAnimationCallback);
-        mCurrentScale.set(mController.getScale());
-        mCurrentCenterX.set(mController.getCenterX());
-        mCurrentCenterY.set(mController.getCenterY());
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY, mAnimationCallback);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
@@ -279,12 +282,14 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, mAnimationCallback);
-        mCurrentScale.set(mController.getScale());
-        mCurrentCenterX.set(mController.getCenterX());
-        mCurrentCenterY.set(mController.getCenterY());
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY, mAnimationCallback);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
@@ -314,8 +319,7 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, null);
+        enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
 
         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
         assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
@@ -333,8 +337,7 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, null);
+        enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
 
         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
         verify(mAnimationCallback).onResult(false);
@@ -347,9 +350,8 @@
                 mAnimationCallback);
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
-                Float.NaN, Float.NaN, mAnimationCallback2);
-        advanceTimeBy(mWaitAnimationDuration);
+        enableWindowMagnificationAndWaitAnimating(
+                mWaitAnimationDuration, Float.NaN, Float.NaN, Float.NaN, mAnimationCallback2);
 
         verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
                 anyFloat());
@@ -368,20 +370,25 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, mAnimationCallback2);
-        mCurrentScale.set(mController.getScale());
-        mCurrentCenterX.set(mController.getCenterX());
-        mCurrentCenterY.set(mController.getCenterY());
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY, mAnimationCallback2);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+        });
 
         // Current spec shouldn't match given spec.
         verify(mAnimationCallback2, never()).onResult(anyBoolean());
         verify(mAnimationCallback).onResult(false);
-        // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it is
-        // using SystemClock in reverse() (b/305731398). Therefore, we call end() on the animator
-        // directly to verify the result of animation is correct instead of querying the animation
-        // frame at a specific timing.
-        mValueAnimator.end();
+
+        mInstrumentation.runOnMainSync(() -> {
+            // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
+            // is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
+            // animator directly to verify the result of animation is correct instead of querying
+            // the animation frame at a specific timing.
+            mValueAnimator.end();
+        });
 
         verify(mSpyController).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
@@ -410,8 +417,7 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, null);
+        enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
 
         verify(mAnimationCallback).onResult(false);
         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
@@ -425,9 +431,8 @@
                 mAnimationCallback);
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+        enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, Float.NaN,
                 Float.NaN, Float.NaN, mAnimationCallback2);
-        advanceTimeBy(mWaitAnimationDuration);
 
         verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
                 anyFloat());
@@ -445,12 +450,14 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                targetCenterX, targetCenterY, mAnimationCallback2);
-        mCurrentScale.set(mController.getScale());
-        mCurrentCenterX.set(mController.getCenterX());
-        mCurrentCenterY.set(mController.getCenterY());
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY, mAnimationCallback2);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
@@ -471,25 +478,26 @@
         final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
-                windowBounds.exactCenterX(), windowBounds.exactCenterY(),
-                offsetRatio, offsetRatio, mAnimationCallback);
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+                    windowBounds.exactCenterX(), windowBounds.exactCenterY(),
+                    offsetRatio, offsetRatio, mAnimationCallback);
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
-        // We delay the time of verifying to wait for the measurement and layout of the view
-        mHandler.postDelayed(() -> {
-            final View attachedView = mWindowManager.getAttachedView();
-            assertNotNull(attachedView);
-            final Rect mirrorViewBound = new Rect();
-            final View mirrorView = attachedView.findViewById(R.id.surface_view);
-            assertNotNull(mirrorView);
-            mirrorView.getBoundsOnScreen(mirrorViewBound);
+        // Wait for Rects update
+        waitForIdleSync();
+        final View attachedView = mWindowManager.getAttachedView();
+        assertNotNull(attachedView);
+        final Rect mirrorViewBound = new Rect();
+        final View mirrorView = attachedView.findViewById(R.id.surface_view);
+        assertNotNull(mirrorView);
+        mirrorView.getBoundsOnScreen(mirrorViewBound);
 
-            assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2),
-                    (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX()));
-            assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2),
-                    (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY()));
-        }, 100);
+        assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2),
+                (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX()));
+        assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2),
+                (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY()));
     }
 
     @Test
@@ -498,9 +506,11 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
         enableWindowMagnificationWithoutAnimation();
 
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                targetCenterX, targetCenterY, mAnimationCallback);
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    targetCenterX, targetCenterY, mAnimationCallback);
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         verify(mAnimationCallback).onResult(true);
         verify(mAnimationCallback, never()).onResult(false);
@@ -512,15 +522,17 @@
             throws RemoteException {
         enableWindowMagnificationWithoutAnimation();
 
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback);
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, mAnimationCallback);
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, mAnimationCallback);
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, mAnimationCallback2);
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback);
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, mAnimationCallback);
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, mAnimationCallback);
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, mAnimationCallback2);
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         // only the last one callback will return true
         verify(mAnimationCallback2).onResult(true);
@@ -538,9 +550,11 @@
         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
                 mAnimationCallback);
 
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                targetCenterX, targetCenterY, mAnimationCallback2);
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    targetCenterX, targetCenterY, mAnimationCallback2);
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         // The callback in moveWindowMagnifierToPosition will return true
         verify(mAnimationCallback2).onResult(true);
@@ -556,9 +570,11 @@
         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
                 mAnimationCallback);
 
-        mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
-                Float.NaN, Float.NaN, mAnimationCallback2);
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+                    Float.NaN, Float.NaN, mAnimationCallback2);
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         // The callback in moveWindowMagnifierToPosition will return true
         verify(mAnimationCallback2).onResult(true);
@@ -584,6 +600,7 @@
             throws RemoteException {
         enableWindowMagnificationWithoutAnimation();
 
+        resetMockObjects();
         deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
 
         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
@@ -625,16 +642,18 @@
                 mAnimationCallback);
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.deleteWindowMagnification(
-                mAnimationCallback2);
-        mCurrentScale.set(mController.getScale());
-        mCurrentCenterX.set(mController.getCenterX());
-        mCurrentCenterY.set(mController.getCenterY());
-        // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it is
-        // using SystemClock in reverse() (b/305731398). Therefore, we call end() on the animator
-        // directly to verify the result of animation is correct instead of querying the animation
-        // frame at a specific timing.
-        mValueAnimator.end();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.deleteWindowMagnification(
+                    mAnimationCallback2);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+            // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
+            // is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
+            // animator directly to verify the result of animation is correct instead of querying
+            // the animation frame at a specific timing.
+            mValueAnimator.end();
+        });
 
         verify(mSpyController).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
@@ -661,7 +680,7 @@
                 mAnimationCallback);
 
         Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.deleteWindowMagnification(null);
+        deleteWindowMagnificationWithoutAnimation();
 
         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
         verify(mAnimationCallback).onResult(false);
@@ -673,6 +692,7 @@
         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
                 mAnimationCallback);
 
+        resetMockObjects();
         deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2);
 
         verify(mSpyController).enableWindowMagnificationInternal(
@@ -710,8 +730,7 @@
         final float offsetY =
                 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
                         + 1.0f;
-
-        mController.moveWindowMagnifier(offsetX, offsetY);
+        mInstrumentation.runOnMainSync(()-> mController.moveWindowMagnifier(offsetX, offsetY));
 
         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY);
@@ -726,8 +745,8 @@
         final float offsetY =
                 (float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
                         - 1.0f;
-
-        mController.moveWindowMagnifier(offsetX, offsetY);
+        mInstrumentation.runOnMainSync(() ->
+                mController.moveWindowMagnifier(offsetX, offsetY));
 
         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y);
@@ -742,8 +761,10 @@
                 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE);
         // while diagonal scrolling enabled,
         //  should move with both offsetX and offsetY without regrading offsetY/offsetX
-        mController.setDiagonalScrolling(true);
-        mController.moveWindowMagnifier(offsetX, offsetY);
+        mInstrumentation.runOnMainSync(() -> {
+            mController.setDiagonalScrolling(true);
+            mController.moveWindowMagnifier(offsetX, offsetY);
+        });
 
         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY);
@@ -755,9 +776,11 @@
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
         enableWindowMagnificationWithoutAnimation();
 
-        mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
-                mAnimationCallback);
-        advanceTimeBy(mWaitAnimationDuration);
+        mInstrumentation.runOnMainSync(() -> {
+            mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
+                    mAnimationCallback);
+            advanceTimeBy(mWaitAnimationDuration);
+        });
 
         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
     }
@@ -774,24 +797,49 @@
     }
 
     private void enableWindowMagnificationWithoutAnimation() {
-        Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
-                DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null);
+        enableWindowMagnificationWithoutAnimation(
+                DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+    }
+
+    private void enableWindowMagnificationWithoutAnimation(
+            float targetScale, float targetCenterX, float targetCenterY) {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(
+                    targetScale, targetCenterX, targetCenterY, null);
+        });
     }
 
     private void enableWindowMagnificationAndWaitAnimating(long duration,
             @Nullable IRemoteMagnificationAnimationCallback callback) {
-        Mockito.reset(mSpyController);
-        mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
-                DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
-        advanceTimeBy(duration);
+        enableWindowMagnificationAndWaitAnimating(
+                duration, DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
+    }
+
+    private void enableWindowMagnificationAndWaitAnimating(
+            long duration,
+            float targetScale,
+            float targetCenterX,
+            float targetCenterY,
+            @Nullable IRemoteMagnificationAnimationCallback callback) {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.enableWindowMagnification(
+                    targetScale, targetCenterX, targetCenterY, callback);
+            advanceTimeBy(duration);
+        });
+    }
+
+    private void deleteWindowMagnificationWithoutAnimation() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.deleteWindowMagnification(null);
+        });
     }
 
     private void deleteWindowMagnificationAndWaitAnimating(long duration,
             @Nullable IRemoteMagnificationAnimationCallback callback) {
-        resetMockObjects();
-        mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
-        advanceTimeBy(duration);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
+            advanceTimeBy(duration);
+        });
     }
 
     private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index b8d2bdb..06421db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -45,6 +45,7 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
@@ -53,6 +54,7 @@
 
 import android.animation.ValueAnimator;
 import android.annotation.IdRes;
+import android.annotation.Nullable;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -89,6 +91,7 @@
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.FakeDisplayTracker;
@@ -102,6 +105,7 @@
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
@@ -111,8 +115,6 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @LargeTest
@@ -120,12 +122,10 @@
 @RunWith(AndroidTestingRunner.class)
 public class WindowMagnificationControllerTest extends SysuiTestCase {
 
+    @Rule
+    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
     private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
-    // The duration couldn't too short, otherwise the animation check on bounce effect
-    // won't work in expectation. (b/299537784)
-    private static final int BOUNCE_EFFECT_DURATION_MS = 2000;
-    private static final long ANIMATION_DURATION_MS = 300;
-    private final long mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
     @Mock
     private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
     @Mock
@@ -134,11 +134,16 @@
     private WindowMagnifierCallback mWindowMagnifierCallback;
     @Mock
     IRemoteMagnificationAnimationCallback mAnimationCallback;
+    @Mock
+    IRemoteMagnificationAnimationCallback mAnimationCallback2;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
     @Mock
     private SecureSettings mSecureSettings;
 
+    private long mWaitAnimationDuration;
+    private long mWaitBounceEffectDuration;
+
     private Handler mHandler;
     private TestableWindowManager mWindowManager;
     private SysUiState mSysUiState;
@@ -194,6 +199,13 @@
             mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
         }
 
+        // Using the animation duration in WindowMagnificationAnimationController for testing.
+        mWaitAnimationDuration = mResources.getInteger(
+                com.android.internal.R.integer.config_longAnimTime);
+        // Using the bounce effect duration in WindowMagnificationController for testing.
+        mWaitBounceEffectDuration = mResources.getInteger(
+                com.android.internal.R.integer.config_shortAnimTime);
+
         mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
                 mContext, mValueAnimator);
         mWindowMagnificationController =
@@ -208,7 +220,6 @@
                         mSysUiState,
                         () -> mWindowSessionSpy,
                         mSecureSettings);
-        mWindowMagnificationController.setBounceEffectDuration(BOUNCE_EFFECT_DURATION_MS);
 
         verify(mMirrorWindowControl).setWindowDelegate(
                 any(MirrorWindowControl.MirrorWindowDelegate.class));
@@ -281,9 +292,9 @@
                     /* magnificationFrameOffsetRatioY= */ 0,
                     Mockito.mock(IRemoteMagnificationAnimationCallback.class));
         });
+        advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
 
-        verify(mSfVsyncFrameProvider,
-                timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeast(2)).postFrameCallback(any());
+        verify(mSfVsyncFrameProvider, atLeast(2)).postFrameCallback(any());
     }
 
     @Test
@@ -401,14 +412,12 @@
 
     @Test
     public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
-            throws InterruptedException {
+            throws RemoteException {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN, 0, 0, null);
         });
-        final CountDownLatch countDownLatch = new CountDownLatch(1);
-        final MockMagnificationAnimationCallback animationCallback =
-                new MockMagnificationAnimationCallback(countDownLatch);
+
         final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
                 .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
@@ -417,12 +426,12 @@
 
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    targetCenterX, targetCenterY, animationCallback);
+                    targetCenterX, targetCenterY, mAnimationCallback);
         });
+        advanceTimeBy(mWaitAnimationDuration);
 
-        assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
-        assertEquals(1, animationCallback.getSuccessCount());
-        assertEquals(0, animationCallback.getFailedCount());
+        verify(mAnimationCallback, times(1)).onResult(eq(true));
+        verify(mAnimationCallback, never()).onResult(eq(false));
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
                 .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
         assertEquals(mWindowMagnificationController.getCenterX(),
@@ -435,14 +444,12 @@
 
     @Test
     public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
-            throws InterruptedException {
+            throws RemoteException {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN, 0, 0, null);
         });
-        final CountDownLatch countDownLatch = new CountDownLatch(4);
-        final MockMagnificationAnimationCallback animationCallback =
-                new MockMagnificationAnimationCallback(countDownLatch);
+
         final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
                 .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
@@ -451,20 +458,20 @@
 
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 10, centerY + 10, animationCallback);
+                    centerX + 10, centerY + 10, mAnimationCallback);
             mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 20, centerY + 20, animationCallback);
+                    centerX + 20, centerY + 20, mAnimationCallback);
             mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 30, centerY + 30, animationCallback);
+                    centerX + 30, centerY + 30, mAnimationCallback);
             mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 40, centerY + 40, animationCallback);
+                    centerX + 40, centerY + 40, mAnimationCallback2);
         });
+        advanceTimeBy(mWaitAnimationDuration);
 
-        assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
         // only the last one callback will return true
-        assertEquals(1, animationCallback.getSuccessCount());
+        verify(mAnimationCallback2).onResult(eq(true));
         // the others will return false
-        assertEquals(3, animationCallback.getFailedCount());
+        verify(mAnimationCallback, times(3)).onResult(eq(false));
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
                 .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
         assertEquals(mWindowMagnificationController.getCenterX(),
@@ -1078,27 +1085,16 @@
 
         final View mirrorView = mWindowManager.getAttachedView();
 
-        final long timeout = SystemClock.uptimeMillis() + 5000;
         final AtomicDouble maxScaleX = new AtomicDouble();
-        final Runnable onAnimationFrame = new Runnable() {
-            @Override
-            public void run() {
-                // For some reason the fancy way doesn't compile...
-//                maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
-                final double oldMax = maxScaleX.get();
-                final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
-                assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+        advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
+            // For some reason the fancy way doesn't compile...
+            // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
+            final double oldMax = maxScaleX.get();
+            final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
+            assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+        });
 
-                if (SystemClock.uptimeMillis() < timeout) {
-                    mirrorView.postOnAnimation(this);
-                }
-            }
-        };
-        mirrorView.postOnAnimation(onAnimationFrame);
-
-        waitForIdleSync();
-
-        ReferenceTestUtils.waitForCondition(() -> maxScaleX.get() > 1.0);
+        assertTrue(maxScaleX.get() > 1.0);
     }
 
     @Test
@@ -1278,7 +1274,8 @@
 
         final float magnificationScaleLarge = 2.5f;
         final int initSize = Math.min(bounds.width(), bounds.height()) / 3;
-        final int magnificationSize = (int) (initSize * magnificationScaleLarge);
+        final int magnificationSize = (int) (initSize * magnificationScaleLarge)
+                - (int) (initSize * magnificationScaleLarge) % 2;
 
         final int expectedWindowHeight = magnificationSize;
         final int expectedWindowWidth = magnificationSize;
@@ -1454,4 +1451,23 @@
         return newRotation;
     }
 
+    // advance time based on the device frame refresh rate
+    private void advanceTimeBy(long timeDelta) {
+        advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null);
+    }
+
+    // advance time based on the device frame refresh rate, and trigger runnable on each refresh
+    private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) {
+        final float frameRate = mContext.getDisplay().getRefreshRate();
+        final int timeSlot = (int) (1000 / frameRate);
+        int round = (int) Math.ceil((double) timeDelta / timeSlot);
+        for (; round >= 0; round--) {
+            mInstrumentation.runOnMainSync(() -> {
+                mAnimatorTestRule.advanceTimeBy(timeSlot);
+                if (runnableOnEachRefresh != null) {
+                    runnableOnEachRefresh.run();
+                }
+            });
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index daa6070..3da7261 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -19,8 +19,6 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -89,7 +87,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         MockitoAnnotations.initMocks(this);
         mContextWrapper = new ContextWrapper(mContext) {
             @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
index 5666435..fd258e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -58,7 +56,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
                 mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index ec6ec63..2e75480 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
@@ -77,7 +75,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
@@ -234,10 +231,12 @@
 
         mMenuAnimationController.springMenuWith(DynamicAnimation.TRANSLATION_X, new SpringForce()
                 .setStiffness(stiffness)
-                .setDampingRatio(dampingRatio), velocity, finalPosition);
+                .setDampingRatio(dampingRatio), velocity, finalPosition,
+                /* writeToPosition = */ true);
         mMenuAnimationController.springMenuWith(DynamicAnimation.TRANSLATION_Y, new SpringForce()
                 .setStiffness(stiffness)
-                .setDampingRatio(dampingRatio), velocity, finalPosition);
+                .setDampingRatio(dampingRatio), velocity, finalPosition,
+                /* writeToPosition = */ true);
     }
 
     private void skipAnimationToEnd(DynamicAnimation animation) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index e8192c4..34a2e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.res.Resources;
@@ -45,7 +43,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         mMenuViewAppearance = new MenuViewAppearance(mContext, windowManager);
         mMenuEduTooltipView = new MenuEduTooltipView(mContext, mMenuViewAppearance);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 62cb9a0..4ac18d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -19,8 +19,6 @@
 import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
 import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
@@ -74,7 +72,6 @@
 
     @Before
     public void setUp() {
-        setFlagDefaults(mSetFlagsRule);
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 3248753..f0a497d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -18,8 +18,6 @@
 
 import static android.view.View.OVER_SCROLL_NEVER;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -82,7 +80,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
                 mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 03a4ba7..31824ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -19,8 +19,6 @@
 import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.systemBars;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
@@ -75,7 +73,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         final WindowManager wm = mContext.getSystemService(WindowManager.class);
         doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
                 mWindowManager).getMaximumWindowMetrics();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index aed795a..0f1364d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -23,7 +23,6 @@
 import static android.view.WindowInsets.Type.systemBars;
 
 import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -42,6 +41,10 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -52,8 +55,10 @@
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -98,6 +103,10 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock
     private IAccessibilityFloatingMenu mFloatingMenu;
 
@@ -112,11 +121,11 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         final Rect mDisplayBounds = new Rect();
         mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
                 DISPLAY_WINDOW_HEIGHT);
-        mWindowMetrics = spy(new WindowMetrics(mDisplayBounds, fakeDisplayInsets()));
+        mWindowMetrics = spy(
+                new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
         doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
 
         mMenuViewLayer = new MenuViewLayer(mContext, mStubWindowManager, mStubAccessibilityManager,
@@ -221,7 +230,8 @@
     }
 
     @Test
-    public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme() {
+    @RequiresFlagsDisabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+    public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme_old() {
         mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100));
         final PointF beforePosition = mMenuView.getMenuPosition();
 
@@ -233,15 +243,49 @@
     }
 
     @Test
-    public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition() {
-        final float menuTop = IME_TOP + 200;
-        mMenuAnimationController.moveAndPersistPosition(new PointF(0, menuTop));
+    @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+    public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme() {
+        mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100));
+        final PointF beforePosition = mMenuView.getMenuPosition();
+
         dispatchShowingImeInsets();
+        assertThat(isPositionAnimationRunning()).isTrue();
+        skipPositionAnimations();
+
+        final float menuBottom = mMenuView.getTranslationY() + mMenuView.getMenuHeight();
+
+        assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x);
+        assertThat(menuBottom).isLessThan(beforePosition.y);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+    public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition_old() {
+        mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 200));
+        final PointF beforePosition = mMenuView.getMenuPosition();
 
         dispatchHidingImeInsets();
 
-        assertThat(mMenuView.getTranslationX()).isEqualTo(0);
-        assertThat(mMenuView.getTranslationY()).isEqualTo(menuTop);
+        assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x);
+        assertThat(mMenuView.getTranslationY()).isEqualTo(beforePosition.y);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+    public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition() {
+        mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 200));
+        final PointF beforePosition = mMenuView.getMenuPosition();
+
+        dispatchShowingImeInsets();
+        assertThat(isPositionAnimationRunning()).isTrue();
+        skipPositionAnimations();
+
+        dispatchHidingImeInsets();
+        assertThat(isPositionAnimationRunning()).isTrue();
+        skipPositionAnimations();
+
+        assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x);
+        assertThat(mMenuView.getTranslationY()).isEqualTo(beforePosition.y);
     }
 
     private void setupEnabledAccessibilityServiceList() {
@@ -294,4 +338,21 @@
                         Insets.of(/* left= */ 0, /* top= */ 0, /* right= */ 0, bottom))
                 .build();
     }
+
+    private boolean isPositionAnimationRunning() {
+        return !mMenuAnimationController.mPositionAnimations.values().stream().filter(
+                        (animation) -> animation.isRunning()).findAny().isEmpty();
+    }
+
+    private void skipPositionAnimations() {
+        mMenuAnimationController.mPositionAnimations.values().stream().forEach(
+                (animation) -> {
+                    final SpringAnimation springAnimation = ((SpringAnimation) animation);
+                    // The doAnimationFrame function is used for skipping animation to the end.
+                    springAnimation.doAnimationFrame(500);
+                    springAnimation.skipToEnd();
+                    springAnimation.doAnimationFrame(500);
+                });
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index b9fd5d0f..5cd0fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,8 +18,6 @@
 
 import static android.app.UiModeManager.MODE_NIGHT_YES;
 
-import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -69,7 +67,6 @@
 
     @Before
     public void setUp() throws Exception {
-        setFlagDefaults(mSetFlagsRule);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mNightMode = mUiModeManager.getNightMode();
         mUiModeManager.setNightMode(MODE_NIGHT_YES);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java
deleted file mode 100644
index 2975549..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 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.systemui.accessibility.utils;
-
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
-
-import android.platform.test.flag.junit.SetFlagsRule;
-
-import com.android.systemui.Flags;
-
-public class FlagUtils {
-    /**
-     * Populates a setFlagsRule with every SystemUI a11y feature flag.
-     * This function should be updated when new flags are added.
-     *
-     * @param setFlagsRule set flags rule from the test environment.
-     */
-    public static void setFlagDefaults(SetFlagsRule setFlagsRule) {
-        setFlagDefault(setFlagsRule, Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index f6b284f..d6aa9ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -423,7 +423,7 @@
         mainHandler.setMode(FakeHandler.Mode.QUEUEING)
 
         // GIVEN bouncer should be delayed due to face auth
-        whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true)
+        whenever(keyguardStateController.isFaceEnrolled).thenReturn(true)
         whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
             .thenReturn(true)
         whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(true)
@@ -449,7 +449,7 @@
 
         // GIVEN bouncer should not be delayed because device isn't in the right posture for
         // face auth
-        whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true)
+        whenever(keyguardStateController.isFaceEnrolled).thenReturn(true)
         whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
             .thenReturn(true)
         whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
new file mode 100644
index 0000000..034b802
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 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.systemui.common.ui
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.captureMany
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConfigurationStateTest : SysuiTestCase() {
+
+    private val configurationController: ConfigurationController = mock()
+    private val layoutInflater = TestLayoutInflater()
+
+    val underTest = ConfigurationState(configurationController, context, layoutInflater)
+
+    @Test
+    fun reinflateAndBindLatest_inflatesWithoutEmission() = runTest {
+        var callbackCount = 0
+        backgroundScope.launch {
+            underTest.reinflateAndBindLatest<View>(
+                resource = 0,
+                root = null,
+                attachToRoot = false,
+            ) {
+                callbackCount++
+                null
+            }
+        }
+
+        // Inflates without an emission
+        runCurrent()
+        assertThat(layoutInflater.inflationCount).isEqualTo(1)
+        assertThat(callbackCount).isEqualTo(1)
+    }
+
+    @Test
+    fun reinflateAndBindLatest_reinflatesOnThemeChanged() = runTest {
+        var callbackCount = 0
+        backgroundScope.launch {
+            underTest.reinflateAndBindLatest<View>(
+                resource = 0,
+                root = null,
+                attachToRoot = false,
+            ) {
+                callbackCount++
+                null
+            }
+        }
+        runCurrent()
+
+        val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
+            verify(configurationController, atLeastOnce()).addCallback(capture())
+        }
+
+        listOf(1, 2, 3).forEach { count ->
+            assertThat(layoutInflater.inflationCount).isEqualTo(count)
+            assertThat(callbackCount).isEqualTo(count)
+            configListeners.forEach { it.onThemeChanged() }
+            runCurrent()
+        }
+    }
+
+    @Test
+    fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() = runTest {
+        var callbackCount = 0
+        backgroundScope.launch {
+            underTest.reinflateAndBindLatest<View>(
+                resource = 0,
+                root = null,
+                attachToRoot = false,
+            ) {
+                callbackCount++
+                null
+            }
+        }
+        runCurrent()
+
+        val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
+            verify(configurationController, atLeastOnce()).addCallback(capture())
+        }
+
+        listOf(1, 2, 3).forEach { count ->
+            assertThat(layoutInflater.inflationCount).isEqualTo(count)
+            assertThat(callbackCount).isEqualTo(count)
+            configListeners.forEach { it.onDensityOrFontScaleChanged() }
+            runCurrent()
+        }
+    }
+
+    @Test
+    fun testReinflateAndBindLatest_disposesOnCancel() = runTest {
+        var callbackCount = 0
+        var disposed = false
+        val job = launch {
+            underTest.reinflateAndBindLatest<View>(
+                resource = 0,
+                root = null,
+                attachToRoot = false,
+            ) {
+                callbackCount++
+                DisposableHandle { disposed = true }
+            }
+        }
+
+        runCurrent()
+        job.cancelAndJoin()
+        assertThat(disposed).isTrue()
+    }
+
+    inner class TestLayoutInflater : LayoutInflater(context) {
+
+        var inflationCount = 0
+
+        override fun inflate(resource: Int, root: ViewGroup?, attachToRoot: Boolean): View {
+            inflationCount++
+            return View(context)
+        }
+
+        override fun cloneInContext(p0: Context?): LayoutInflater {
+            // not needed for this test
+            return this
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index 1b2fc93d..2f4fc96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -67,6 +67,7 @@
                 isAttachedToWindow = { isAttachedToWindow },
                 onLongPressDetected = onLongPressDetected,
                 onSingleTapDetected = onSingleTapDetected,
+                longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() }
             )
         underTest.isLongPressHandlingEnabled = true
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
index e16b8d4..f4f05d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
@@ -18,39 +18,7 @@
 
 import android.platform.test.flag.junit.SetFlagsRule
 
-/**
- * Set the given flag's value to the real value for the current build configuration.
- * This prevents test code from crashing because it is reading an unspecified flag value.
- *
- * REMINDER: You should always test your code with your flag in both configurations, so
- * generally you should be explicitly enabling or disabling your flag. This method is for
- * situations where the flag needs to be read (e.g. in the class constructor), but its value
- * shouldn't affect the actual test cases. In those cases, it's mildly safer to use this method
- * than to hard-code `false` or `true` because then at least if you're wrong, and the flag value
- * *does* matter, you'll notice when the flag is flipped and tests start failing.
- */
-fun SetFlagsRule.setFlagDefault(flagName: String) {
-    if (getFlagDefault(flagName)) {
-        enableFlags(flagName)
-    } else {
-        disableFlags(flagName)
-    }
-}
-
-// NOTE: This code uses reflection to gain access to private members of aconfig generated
-//  classes (in the same way SetFlagsRule does internally) because this is the only way to get
-//  at the underlying information and read the current value of the flag.
-// If aconfig had flag constants with accessible default values, this would be unnecessary.
-private fun getFlagDefault(name: String): Boolean {
-    val flagPackage = name.substringBeforeLast(".")
-    val featureFlagsImplClass = Class.forName("$flagPackage.FeatureFlagsImpl")
-    val featureFlagsImpl = featureFlagsImplClass.getConstructor().newInstance()
-    val flagMethodName = name.substringAfterLast(".").snakeToCamelCase()
-    val flagGetter = featureFlagsImplClass.getDeclaredMethod(flagMethodName)
-    return flagGetter.invoke(featureFlagsImpl) as Boolean
-}
-
-private fun String.snakeToCamelCase(): String {
-    val pattern = "_[a-z]".toRegex()
-    return replace(pattern) { it.value.last().uppercase() }
+/** Set the given flag to an explicit value. */
+fun SetFlagsRule.setFlagValue(name: String, value: Boolean) {
+    if (value) enableFlags(name) else disableFlags(name)
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index ef03fdf..6c4bb37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -216,6 +216,29 @@
         }
 
     @Test
+    fun isKeyguardUnlocked() =
+        testScope.runTest {
+            whenever(keyguardStateController.isUnlocked).thenReturn(false)
+            val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardUnlocked)
+
+            runCurrent()
+            assertThat(isKeyguardUnlocked).isFalse()
+
+            val captor = argumentCaptor<KeyguardStateController.Callback>()
+            verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
+
+            whenever(keyguardStateController.isUnlocked).thenReturn(true)
+            captor.value.onUnlockedChanged()
+            runCurrent()
+            assertThat(isKeyguardUnlocked).isTrue()
+
+            whenever(keyguardStateController.isUnlocked).thenReturn(false)
+            captor.value.onKeyguardShowingChanged()
+            runCurrent()
+            assertThat(isKeyguardUnlocked).isFalse()
+        }
+
+    @Test
     fun isDozing() =
         testScope.runTest {
             underTest.setIsDozing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index b32905f..27325d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -68,7 +68,6 @@
             powerInteractor = PowerInteractorFactory.create().powerInteractor,
             featureFlags = featureFlags,
             sceneContainerFlags = testUtils.sceneContainerFlags,
-            deviceEntryRepository = testUtils.deviceEntryRepository,
             bouncerRepository = bouncerRepository,
             configurationRepository = configurationRepository,
             shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index a5d7457..6cd7406 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -46,6 +47,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
+@ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardKeyEventInteractorTest : SysuiTestCase() {
@@ -128,58 +130,62 @@
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_awakeKeyguard_showsPrimaryBouncer() {
         powerInteractor.setAwakeForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_awakeShadeLocked_collapsesShade() {
         powerInteractor.setAwakeForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
-
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_asleepKeyguard_neverCollapsesShade() {
         powerInteractor.setAsleepForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
+        verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
+    fun dispatchKeyEvent_spaceActionUp_awakeKeyguard_collapsesShade() {
         powerInteractor.setAwakeForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
 
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
+    }
 
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+    @Test
+    fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_awakeKeyguard_showsPrimaryBouncer() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
     }
 
     @Test
@@ -249,4 +255,42 @@
             .isFalse()
         verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
     }
+
+    private fun verifyActionUpCollapsesTheShade(keycode: Int) {
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(shadeController).animateCollapseShadeForced()
+    }
+
+    private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
+    }
+
+    private fun verifyActionsDoNothing(keycode: Int) {
+        // action down: does nothing
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+        // action up: doesNothing
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 50ee026..8cfa87d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
 import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
@@ -55,7 +55,7 @@
     private lateinit var underTest: DefaultKeyguardBlueprint
     private lateinit var rootView: KeyguardRootView
     @Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
-    @Mock private lateinit var defaultLockIconSection: DefaultLockIconSection
+    @Mock private lateinit var mDefaultDeviceEntryIconSection: DefaultDeviceEntryIconSection
     @Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
     @Mock
     private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@@ -75,7 +75,7 @@
         underTest =
             DefaultKeyguardBlueprint(
                 defaultIndicationAreaSection,
-                defaultLockIconSection,
+                mDefaultDeviceEntryIconSection,
                 defaultShortcutsSection,
                 defaultAmbientIndicationAreaSection,
                 defaultSettingsPopupMenuSection,
@@ -101,14 +101,14 @@
         val prevBlueprint = mock(KeyguardBlueprint::class.java)
         val someSection = mock(KeyguardSection::class.java)
         whenever(prevBlueprint.sections)
-            .thenReturn(underTest.sections.minus(defaultLockIconSection).plus(someSection))
+            .thenReturn(underTest.sections.minus(mDefaultDeviceEntryIconSection).plus(someSection))
         val constraintLayout = ConstraintLayout(context, null)
         underTest.replaceViews(prevBlueprint, constraintLayout)
-        underTest.sections.minus(defaultLockIconSection).forEach {
+        underTest.sections.minus(mDefaultDeviceEntryIconSection).forEach {
             verify(it, never()).addViews(constraintLayout)
         }
 
-        verify(defaultLockIconSection).addViews(constraintLayout)
+        verify(mDefaultDeviceEntryIconSection).addViews(constraintLayout)
         verify(someSection).removeViews(constraintLayout)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
similarity index 65%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 7495637..5f22c7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -24,14 +24,17 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.LockIconViewController
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationPanelView
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -40,35 +43,54 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
+@ExperimentalCoroutinesApi
 @RunWith(JUnit4::class)
 @SmallTest
-class DefaultLockIconSectionTest : SysuiTestCase() {
+class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var authController: AuthController
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
     @Mock private lateinit var notificationPanelView: NotificationPanelView
-    @Mock private lateinit var featureFlags: FeatureFlags
+    private lateinit var featureFlags: FakeFeatureFlags
     @Mock private lateinit var lockIconViewController: LockIconViewController
-    private lateinit var underTest: DefaultLockIconSection
+    @Mock private lateinit var falsingManager: FalsingManager
+    private lateinit var underTest: DefaultDeviceEntryIconSection
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        featureFlags =
+            FakeFeatureFlagsClassic().apply {
+                set(Flags.MIGRATE_LOCK_ICON, false)
+                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+                set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+            }
         underTest =
-            DefaultLockIconSection(
+            DefaultDeviceEntryIconSection(
                 keyguardUpdateMonitor,
                 authController,
                 windowManager,
                 context,
                 notificationPanelView,
                 featureFlags,
-                lockIconViewController
+                { lockIconViewController },
+                { DeviceEntryIconViewModel() },
+                { falsingManager },
             )
     }
 
     @Test
-    fun addViewsConditionally() {
-        whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(true)
+    fun addViewsConditionally_migrateFlagOn() {
+        featureFlags.set(Flags.MIGRATE_LOCK_ICON, true)
+        val constraintLayout = ConstraintLayout(context, null)
+        underTest.addViews(constraintLayout)
+        assertThat(constraintLayout.childCount).isGreaterThan(0)
+    }
+
+    @Test
+    fun addViewsConditionally_migrateAndRefactorFlagsOn() {
+        featureFlags.set(Flags.MIGRATE_LOCK_ICON, true)
+        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -76,7 +98,8 @@
 
     @Test
     fun addViewsConditionally_migrateFlagOff() {
-        whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(false)
+        featureFlags.set(Flags.MIGRATE_LOCK_ICON, false)
+        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isEqualTo(0)
@@ -87,18 +110,18 @@
         val cs = ConstraintSet()
         underTest.applyConstraints(cs)
 
-        val constraint = cs.getConstraint(R.id.lock_icon_view)
+        val constraint = cs.getConstraint(R.id.device_entry_icon_view)
 
         assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
         assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
     }
 
     @Test
-    fun testCenterLockIcon() {
+    fun testCenterIcon() {
         val cs = ConstraintSet()
-        underTest.centerLockIcon(Point(5, 6), 1F, cs)
+        underTest.centerIcon(Point(5, 6), 1F, cs)
 
-        val constraint = cs.getConstraint(R.id.lock_icon_view)
+        val constraint = cs.getConstraint(R.id.device_entry_icon_view)
 
         assertThat(constraint.layout.mWidth).isEqualTo(2)
         assertThat(constraint.layout.mHeight).isEqualTo(2)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index deefab6..b101acf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -124,10 +124,10 @@
                 context,
                 controllerFactory,
                 lmmFactory,
-                mr2,
+                { mr2 },
                 muteAwaitFactory,
                 configurationController,
-                localBluetoothManager,
+                { localBluetoothManager },
                 fakeFgExecutor,
                 fakeBgExecutor,
                 dumpster,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
index fd1e2c7..da448aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
@@ -74,6 +74,15 @@
     }
 
     @Test
+    fun notifyProjectionCancelled_forwardsToServiceWithMetricsValue() {
+        val hostUid = 123
+
+        logger.notifyProjectionRequestCancelled(hostUid)
+
+        verify(service).notifyPermissionRequestCancelled(hostUid)
+    }
+
+    @Test
     fun notifyAppSelectorDisplayed_forwardsToService() {
         val hostUid = 654
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 5255f71..44798ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -4,7 +4,6 @@
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
@@ -214,7 +213,7 @@
     @Test
     fun init_firstStart_logsAppSelectorDisplayed() {
         val hostUid = 123456789
-        val controller = createController(isFirstStart = true,  hostUid)
+        val controller = createController(isFirstStart = true, hostUid)
 
         controller.init()
 
@@ -231,6 +230,15 @@
         verify(logger, never()).notifyAppSelectorDisplayed(hostUid)
     }
 
+    @Test
+    fun onSelectorDismissed_logsProjectionRequestCancelled() {
+        val hostUid = 123
+
+        createController(hostUid = hostUid).onSelectorDismissed()
+
+        verify(logger).notifyProjectionRequestCancelled(hostUid)
+    }
+
     private fun givenCaptureAllowed(isAllow: Boolean) {
         whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 0e6e4fa..c711806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -373,18 +373,6 @@
     }
 
     @Test
-    fun isVisible() {
-        val underTest = utils.footerActionsViewModel()
-        assertThat(underTest.isVisible.value).isFalse()
-
-        underTest.onVisibilityChangeRequested(visible = true)
-        assertThat(underTest.isVisible.value).isTrue()
-
-        underTest.onVisibilityChangeRequested(visible = false)
-        assertThat(underTest.isVisible.value).isFalse()
-    }
-
-    @Test
     fun alpha_inSplitShade_followsExpansion() {
         val underTest = utils.footerActionsViewModel()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
index a6199c2..2bdc154 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
@@ -83,7 +83,7 @@
     @Test
     fun testDisabledWhenAdminWithNoRestrictions() =
         testScope.runTest {
-            val admin = EnforcedAdmin(TEST_COMPONENT_NAME, UserHandle(TEST_USER))
+            val admin = EnforcedAdmin(TEST_COMPONENT_NAME, TEST_USER)
             whenever(restrictedLockProxy.getEnforcedAdmin(anyInt(), anyString())).thenReturn(admin)
             whenever(restrictedLockProxy.hasBaseUserRestriction(anyInt(), anyString()))
                 .thenReturn(false)
@@ -129,11 +129,11 @@
     }
 
     private companion object {
-        const val TEST_USER = 1
+
         const val TEST_RESTRICTION = "test_restriction"
 
         val TEST_COMPONENT_NAME = ComponentName("test.pkg", "test.cls")
-
-        val ADMIN = EnforcedAdmin(TEST_COMPONENT_NAME, UserHandle(TEST_USER))
+        val TEST_USER = UserHandle(1)
+        val ADMIN = EnforcedAdmin(TEST_COMPONENT_NAME, TEST_USER)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt
index fc2b7a64..479e62d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt
@@ -47,11 +47,12 @@
 
     @Mock private lateinit var bluetoothAdapter: LocalBluetoothAdapter
     @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+    @Mock private lateinit var logger: BluetoothTileDialogLogger
 
     @Before
     fun setUp() {
         bluetoothStateInteractor =
-            BluetoothStateInteractor(localBluetoothManager, testScope.backgroundScope)
+            BluetoothStateInteractor(localBluetoothManager, logger, testScope.backgroundScope)
         `when`(localBluetoothManager.bluetoothAdapter).thenReturn(bluetoothAdapter)
     }
 
@@ -80,6 +81,8 @@
 
             bluetoothStateInteractor.isBluetoothEnabled = true
             verify(bluetoothAdapter).enable()
+            verify(logger)
+                .logBluetoothState(BluetoothStateStage.BLUETOOTH_STATE_VALUE_SET, true.toString())
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 8b66040..3b6bfee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -30,6 +30,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
@@ -60,8 +61,12 @@
 
     @Mock private lateinit var uiEventLogger: UiEventLogger
 
+    @Mock private lateinit var logger: BluetoothTileDialogLogger
+
     private val subtitleResId = R.string.quick_settings_bluetooth_tile_subtitle
 
+    private val fakeSystemClock = FakeSystemClock()
+
     private lateinit var icon: Pair<Drawable, String>
     private lateinit var bluetoothTileDialog: BluetoothTileDialog
     private lateinit var deviceItem: DeviceItem
@@ -73,7 +78,9 @@
                 ENABLED,
                 subtitleResId,
                 bluetoothTileDialogCallback,
+                fakeSystemClock,
                 uiEventLogger,
+                logger,
                 mContext
             )
         icon = Pair(drawable, DEVICE_NAME)
@@ -109,7 +116,9 @@
                 ENABLED,
                 subtitleResId,
                 bluetoothTileDialogCallback,
+                fakeSystemClock,
                 uiEventLogger,
+                logger,
                 mContext
             )
         bluetoothTileDialog.show()
@@ -138,7 +147,9 @@
                     ENABLED,
                     subtitleResId,
                     bluetoothTileDialogCallback,
+                    fakeSystemClock,
                     uiEventLogger,
+                    logger,
                     mContext
                 )
                 .Adapter(bluetoothTileDialogCallback)
@@ -162,7 +173,9 @@
                     ENABLED,
                     subtitleResId,
                     bluetoothTileDialogCallback,
+                    fakeSystemClock,
                     uiEventLogger,
+                    logger,
                     mContext
                 )
                 .Adapter(bluetoothTileDialogCallback)
@@ -182,7 +195,9 @@
                 ENABLED,
                 subtitleResId,
                 bluetoothTileDialogCallback,
+                fakeSystemClock,
                 uiEventLogger,
+                logger,
                 mContext
             )
         bluetoothTileDialog.show()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index dcda005..fb5dd21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -77,6 +77,8 @@
 
     @Mock private lateinit var uiEventLogger: UiEventLogger
 
+    @Mock private lateinit var logger: BluetoothTileDialogLogger
+
     private lateinit var scheduler: TestCoroutineScheduler
     private lateinit var dispatcher: CoroutineDispatcher
     private lateinit var testScope: TestScope
@@ -92,7 +94,9 @@
                 bluetoothStateInteractor,
                 dialogLaunchAnimator,
                 activityStarter,
+                fakeSystemClock,
                 uiEventLogger,
+                logger,
                 testScope.backgroundScope,
                 dispatcher,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index 428f79c..4c173cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -38,6 +39,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
@@ -71,6 +73,10 @@
 
     @Mock private lateinit var uiEventLogger: UiEventLogger
 
+    @Mock private lateinit var logger: BluetoothTileDialogLogger
+
+    private val fakeSystemClock = FakeSystemClock()
+
     private lateinit var interactor: DeviceItemInteractor
 
     private lateinit var dispatcher: CoroutineDispatcher
@@ -87,13 +93,16 @@
                 audioManager,
                 adapter,
                 localBluetoothManager,
+                fakeSystemClock,
                 uiEventLogger,
+                logger,
                 testScope.backgroundScope,
                 dispatcher
             )
 
         `when`(deviceItem1.cachedBluetoothDevice).thenReturn(cachedDevice1)
         `when`(deviceItem2.cachedBluetoothDevice).thenReturn(cachedDevice2)
+        `when`(cachedDevice1.address).thenReturn("ADDRESS")
         `when`(cachedDevice1.device).thenReturn(device1)
         `when`(cachedDevice2.device).thenReturn(device2)
         `when`(bluetoothTileDialogRepository.cachedDevices)
@@ -109,7 +118,7 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
-            interactor.updateDeviceItems(mContext)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(emptyList<DeviceItem>())
         }
@@ -124,7 +133,7 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
-            interactor.updateDeviceItems(mContext)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(emptyList<DeviceItem>())
         }
@@ -139,7 +148,7 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
-            interactor.updateDeviceItems(mContext)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem1))
         }
@@ -154,7 +163,7 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
-            interactor.updateDeviceItems(mContext)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem2))
         }
@@ -180,7 +189,7 @@
             `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
-            interactor.updateDeviceItems(mContext)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1))
         }
@@ -203,12 +212,55 @@
             `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
-            interactor.updateDeviceItems(mContext)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1))
         }
     }
 
+    @Test
+    fun testUpdateDeviceItemOnClick_connectedMedia_setActive() {
+        `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+
+        interactor.updateDeviceItemOnClick(deviceItem1)
+
+        verify(cachedDevice1).setActive()
+        verify(logger)
+            .logDeviceClick(cachedDevice1.address, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+    }
+
+    @Test
+    fun testUpdateDeviceItemOnClick_activeMedia_disconnect() {
+        `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+
+        interactor.updateDeviceItemOnClick(deviceItem1)
+
+        verify(cachedDevice1).disconnect()
+        verify(logger)
+            .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+    }
+
+    @Test
+    fun testUpdateDeviceItemOnClick_connectedOtherDevice_disconnect() {
+        `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+
+        interactor.updateDeviceItemOnClick(deviceItem1)
+
+        verify(cachedDevice1).disconnect()
+        verify(logger)
+            .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+    }
+
+    @Test
+    fun testUpdateDeviceItemOnClick_saved_connect() {
+        `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+
+        interactor.updateDeviceItemOnClick(deviceItem1)
+
+        verify(cachedDevice1).connect()
+        verify(logger).logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+    }
+
     private fun createFactory(
         isFilterMatchFunc: (CachedBluetoothDevice) -> Boolean,
         deviceItem: DeviceItem
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
new file mode 100644
index 0000000..682b2d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QSTileConfigProviderTest : SysuiTestCase() {
+
+    private val underTest =
+        QSTileConfigProviderImpl(
+            mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
+        )
+
+    @Test
+    fun providerReturnsConfig() {
+        assertThat(underTest.getConfig(VALID_SPEC.spec)).isNotNull()
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun throwsForInvalidSpec() {
+        underTest.getConfig(INVALID_SPEC.spec)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun validatesSpecUponCreation() {
+        QSTileConfigProviderImpl(
+            mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = INVALID_SPEC })
+        )
+    }
+
+    private companion object {
+
+        val VALID_SPEC = TileSpec.create("valid_tile_spec")
+        val INVALID_SPEC = TileSpec.create("invalid_tile_spec")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 9b85012..9bf4a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -16,22 +16,21 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
+import android.os.UserHandle
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
-import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
 import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
 import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
 import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -77,9 +76,6 @@
         testScope.runTest {
             assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
 
-            underTest.onLifecycle(QSTileLifecycle.ALIVE)
-            underTest.onUserIdChanged(1)
-
             assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
 
             underTest.state.launchIn(backgroundScope)
@@ -87,20 +83,22 @@
 
             assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
             assertThat(fakeQSTileDataInteractor.dataRequests.first())
-                .isEqualTo(FakeQSTileDataInteractor.DataRequest(1))
+                .isEqualTo(FakeQSTileDataInteractor.DataRequest(UserHandle.of(0)))
         }
 
     private fun createViewModel(
         scope: TestScope,
         config: QSTileConfig = TEST_QS_TILE_CONFIG,
     ): QSTileViewModel =
-        BaseQSTileViewModel(
+        QSTileViewModelImpl(
             config,
-            fakeQSTileUserActionInteractor,
-            fakeQSTileDataInteractor,
-            object : QSTileDataToStateMapper<Any> {
-                override fun map(config: QSTileConfig, data: Any): QSTileState =
-                    QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
+            { fakeQSTileUserActionInteractor },
+            { fakeQSTileDataInteractor },
+            {
+                object : QSTileDataToStateMapper<Any> {
+                    override fun map(config: QSTileConfig, data: Any): QSTileState =
+                        QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
+                }
             },
             fakeDisabledByPolicyInteractor,
             fakeUserRepository,
@@ -114,12 +112,6 @@
 
     private companion object {
 
-        val TEST_QS_TILE_CONFIG =
-            QSTileConfig(
-                TileSpec.create("default"),
-                Icon.Resource(0, null),
-                0,
-                InstanceId.fakeInstanceId(0),
-            )
+        val TEST_QS_TILE_CONFIG = QSTileConfigTestBuilder.build {}
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index e1345d2..c1f2d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -22,11 +22,11 @@
 import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AconfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
 import com.android.systemui.model.SysUiState
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -45,7 +45,7 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.Assume.assumeTrue
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.clearInvocations
@@ -87,6 +87,11 @@
             powerInteractor = powerInteractor,
         )
 
+    @Before
+    fun setUp() {
+        mSetFlagsRule.enableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
+    }
+
     @Test
     fun hydrateVisibility() =
         testScope.runTest {
@@ -520,7 +525,6 @@
         authenticationMethod: AuthenticationMethodModel? = null,
         startsAwake: Boolean = true,
     ): MutableStateFlow<ObservableTransitionState> {
-        assumeTrue(Flags.SCENE_CONTAINER_ENABLED)
         sceneContainerFlags.enabled = true
         utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked)
         utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index f6195aa..0bed4d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -16,7 +16,10 @@
 
 package com.android.systemui.scene.shared.flag
 
+import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.filters.SmallTest
+import com.android.systemui.FakeFeatureFlagsImpl
+import com.android.systemui.Flags as AconfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
@@ -24,8 +27,8 @@
 import com.android.systemui.flags.ResourceBooleanFlag
 import com.android.systemui.flags.UnreleasedFlag
 import com.google.common.truth.Truth.assertThat
-import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -36,27 +39,50 @@
     private val testCase: TestCase,
 ) : SysuiTestCase() {
 
+    @Rule @JvmField val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
     private lateinit var underTest: SceneContainerFlags
 
     @Before
     fun setUp() {
+        // TODO(b/283300105): remove this reflection setting once the hard-coded
+        //  Flags.SCENE_CONTAINER_ENABLED is no longer needed.
+        val field = Flags::class.java.getField("SCENE_CONTAINER_ENABLED")
+        field.isAccessible = true
+        field.set(null, true)
+
         val featureFlags =
             FakeFeatureFlagsClassic().apply {
-                SceneContainerFlagsImpl.flags.forEach { flag ->
-                    when (flag) {
-                        is ResourceBooleanFlag -> set(flag, testCase.areAllFlagsSet)
-                        is ReleasedFlag -> set(flag, testCase.areAllFlagsSet)
-                        is UnreleasedFlag -> set(flag, testCase.areAllFlagsSet)
-                        else -> error("Unsupported flag type ${flag.javaClass}")
+                SceneContainerFlagsImpl.classicFlagTokens.forEach { flagToken ->
+                    when (flagToken) {
+                        is ResourceBooleanFlag -> set(flagToken, testCase.areAllFlagsSet)
+                        is ReleasedFlag -> set(flagToken, testCase.areAllFlagsSet)
+                        is UnreleasedFlag -> set(flagToken, testCase.areAllFlagsSet)
+                        else -> error("Unsupported flag type ${flagToken.javaClass}")
                     }
                 }
             }
-        underTest = SceneContainerFlagsImpl(featureFlags, testCase.isComposeAvailable)
+        // TODO(b/306421592): get the aconfig FeatureFlags from the SetFlagsRule.
+        val aconfigFlags = FakeFeatureFlagsImpl()
+
+        listOf(
+                AconfigFlags.FLAG_SCENE_CONTAINER,
+            )
+            .forEach { flagToken ->
+                setFlagsRule.enableFlags(flagToken)
+                aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet)
+            }
+
+        underTest =
+            SceneContainerFlagsImpl(
+                featureFlagsClassic = featureFlags,
+                featureFlags = aconfigFlags,
+                isComposeAvailable = testCase.isComposeAvailable,
+            )
     }
 
     @Test
     fun isEnabled() {
-        assumeTrue(Flags.SCENE_CONTAINER_ENABLED)
         assertThat(underTest.isEnabled()).isEqualTo(testCase.expectedEnabled)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index c439cfe..49049bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.screenrecord;
 
+import static android.os.Process.myUid;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertFalse;
@@ -31,8 +33,8 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Looper;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -60,6 +62,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 /**
  * Tests for exception handling and  bitmap configuration in adding smart actions to Screenshot
  * Notification.
@@ -117,10 +120,6 @@
     // starting, and notifies listeners.
     @Test
     public void testCancelCountdown() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         mController.startCountdown(100, 10, null, null);
 
         assertTrue(mController.isStarting());
@@ -137,10 +136,6 @@
     // Test that when recording is started, the start intent is sent and listeners are notified.
     @Test
     public void testStartRecording() throws PendingIntent.CanceledException {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         PendingIntent startIntent = Mockito.mock(PendingIntent.class);
         mController.startCountdown(0, 0, startIntent, null);
 
@@ -151,10 +146,6 @@
     // Test that when recording is stopped, the stop intent is sent and listeners are notified.
     @Test
     public void testStopRecording() throws PendingIntent.CanceledException {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         PendingIntent startIntent = Mockito.mock(PendingIntent.class);
         PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
 
@@ -182,10 +173,6 @@
     // Test that broadcast will update state
     @Test
     public void testUpdateStateBroadcast() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         // When a recording has started
         PendingIntent startIntent = Mockito.mock(PendingIntent.class);
         mController.startCountdown(0, 0, startIntent, null);
@@ -211,10 +198,6 @@
     // Test that switching users will stop an ongoing recording
     @Test
     public void testUserChange() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         // If we are recording
         PendingIntent startIntent = Mockito.mock(PendingIntent.class);
         PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
@@ -231,10 +214,6 @@
 
     @Test
     public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
@@ -247,10 +226,6 @@
 
     @Test
     public void testPartialScreenSharingDisabled_returnsLegacyDialog() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, false);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
 
@@ -262,10 +237,6 @@
 
     @Test
     public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
@@ -278,10 +249,6 @@
 
     @Test
     public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
@@ -294,9 +261,6 @@
 
     @Test
     public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
@@ -306,7 +270,7 @@
 
         verify(mMediaProjectionMetricsLogger)
                 .notifyProjectionInitiated(
-                        TEST_USER_ID,
+                        /* hostUid= */ myUid(),
                         SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index bf12d7d..fd38139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
 import com.android.systemui.mediaprojection.permission.SINGLE_APP
@@ -41,7 +42,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -57,6 +57,7 @@
     @Mock private lateinit var userContextProvider: UserContextProvider
     @Mock private lateinit var flags: FeatureFlags
     @Mock private lateinit var onStartRecordingClicked: Runnable
+    @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
 
     private lateinit var dialog: ScreenRecordPermissionDialog
 
@@ -72,7 +73,8 @@
                 controller,
                 starter,
                 userContextProvider,
-                onStartRecordingClicked
+                onStartRecordingClicked,
+                mediaProjectionMetricsLogger,
             )
         dialog.onCreate(null)
         whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
@@ -149,6 +151,28 @@
         assertThat(dialog.isShowing).isFalse()
     }
 
+    @Test
+    fun showDialog_cancelClickedMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
+        dialog.show()
+
+        clickOnCancel()
+        clickOnCancel()
+
+        verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
+    }
+
+    @Test
+    fun dismissDialog_dismissCalledMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
+        dialog.show()
+
+        TestableLooper.get(this).runWithLooper {
+            dialog.dismiss()
+            dialog.dismiss()
+        }
+
+        verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
+    }
+
     private fun clickOnCancel() {
         dialog.requireViewById<View>(android.R.id.button2).performClick()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 6223e25..2ce4b04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -84,6 +84,7 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.view.LongPressHandlingView;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
@@ -120,6 +121,8 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -137,6 +140,7 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -168,6 +172,7 @@
 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
 import com.android.systemui.statusbar.phone.TapAgainViewController;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -176,8 +181,10 @@
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.time.SystemClock;
@@ -197,6 +204,8 @@
 import java.util.Optional;
 
 import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.StateFlowKt;
+import kotlinx.coroutines.test.TestScope;
 
 public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
 
@@ -324,7 +333,6 @@
             mEmptySpaceClickListenerCaptor;
     @Mock protected ActivityStarter mActivityStarter;
     @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
-    @Mock private ShadeInteractor mShadeInteractor;
     @Mock private JavaAdapter mJavaAdapter;
     @Mock private CastController mCastController;
     @Mock private KeyguardRootView mKeyguardRootView;
@@ -335,6 +343,9 @@
     protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
     protected FakeKeyguardRepository mFakeKeyguardRepository;
     protected KeyguardInteractor mKeyguardInteractor;
+    protected SceneTestUtils mUtils = new SceneTestUtils(this);
+    protected TestScope mTestScope = mUtils.getTestScope();
+    protected ShadeInteractor mShadeInteractor;
     protected PowerInteractor mPowerInteractor;
     protected NotificationPanelViewController.TouchHandler mTouchHandler;
     protected ConfigurationController mConfigurationController;
@@ -370,10 +381,31 @@
         mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
         mShadeRepository = new FakeShadeRepository();
         mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
+        when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
+                StateFlowKt.MutableStateFlow(false));
+        mShadeInteractor = new ShadeInteractor(
+                mTestScope.getBackgroundScope(),
+                new FakeDeviceProvisioningRepository(),
+                new FakeDisableFlagsRepository(),
+                mDozeParameters,
+                new FakeSceneContainerFlags(),
+                mUtils::sceneInteractor,
+                mFakeKeyguardRepository,
+                mKeyguardTransitionInteractor,
+                mPowerInteractor,
+                new FakeUserSetupRepository(),
+                mock(UserSwitcherInteractor.class),
+                new SharedNotificationContainerInteractor(
+                        new FakeConfigurationRepository(),
+                        mContext,
+                        new ResourcesSplitShadeStateController()
+                ),
+                mShadeRepository
+        );
 
         SystemClock systemClock = new FakeSystemClock();
-        mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
-                mInteractionJankMonitor, mShadeExpansionStateManager);
+        mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
+                mInteractionJankMonitor, mJavaAdapter, () -> mShadeInteractor);
 
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -532,8 +564,9 @@
                 new NotificationWakeUpCoordinator(
                         mDumpManager,
                         mock(HeadsUpManager.class),
-                        new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
-                                mInteractionJankMonitor, mShadeExpansionStateManager),
+                        new StatusBarStateControllerImpl(new UiEventLoggerFake(),
+                                mInteractionJankMonitor,
+                                mJavaAdapter, () -> mShadeInteractor),
                         mKeyguardBypassController,
                         mDozeParameters,
                         mScreenOffAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index be82bc3..4ba850c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -54,7 +54,6 @@
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -185,7 +184,6 @@
                 powerInteractor,
                 featureFlags,
                 sceneContainerFlags,
-                new FakeDeviceEntryRepository(),
                 new FakeKeyguardBouncerRepository(),
                 configurationRepository,
                 shadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 0fcfaf9..2f45b12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -39,7 +39,6 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.flags.FeatureFlags;
@@ -188,8 +187,8 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
-        mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
-                mInteractionJankMonitor, mShadeExpansionStateManager);
+        mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
+                mInteractionJankMonitor, mock(JavaAdapter.class), () -> mShadeInteractor);
 
         FakeDeviceProvisioningRepository deviceProvisioningRepository =
                 new FakeDeviceProvisioningRepository();
@@ -219,7 +218,6 @@
                 powerInteractor,
                 featureFlags,
                 sceneContainerFlags,
-                new FakeDeviceEntryRepository(),
                 new FakeKeyguardBouncerRepository(),
                 configurationRepository,
                 mShadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 8f06e63..6eabf44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -145,22 +145,15 @@
     }
 
     @Test
-    public void testOnRepeatedlyLoadUnload_PluginFreed() {
+    public void testOnUnloadAfterLoad() {
         mPluginInstance.onCreate();
         mPluginInstance.loadPlugin();
+        assertNotNull(mPluginInstance.getPlugin());
         assertInstances(1, 1);
 
         mPluginInstance.unloadPlugin();
         assertNull(mPluginInstance.getPlugin());
         assertInstances(0, 0);
-
-        mPluginInstance.loadPlugin();
-        assertInstances(1, 1);
-
-        mPluginInstance.unloadPlugin();
-        mPluginInstance.onDestroy();
-        assertNull(mPluginInstance.getPlugin());
-        assertInstances(0, 0);
     }
 
     @Test
@@ -169,7 +162,7 @@
         mPluginInstance.onCreate();
         assertEquals(1, mPluginListener.mAttachedCount);
         assertEquals(0, mPluginListener.mLoadCount);
-        assertEquals(null, mPluginInstance.getPlugin());
+        assertNull(mPluginInstance.getPlugin());
         assertInstances(0, 0);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 2bcad1d..dd3ac92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -73,10 +73,10 @@
 
 import com.android.keyguard.TrustGrantFlags;
 import com.android.settingslib.fuelgauge.BatteryStatus;
-import com.android.systemui.res.R;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardIndication;
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
+import com.android.systemui.res.R;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -1543,7 +1543,7 @@
 
     private void setupFingerprintUnlockPossible(boolean possible) {
         when(mKeyguardUpdateMonitor
-                .getCachedIsUnlockWithFingerprintPossible(getCurrentUser()))
+                .isUnlockWithFingerprintPossible(getCurrentUser()))
                 .thenReturn(possible);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index e8923a5..970a0f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -9,13 +9,18 @@
 import com.android.TestMocksModule
 import com.android.systemui.ExpandHelper
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
 import com.android.systemui.media.controls.ui.MediaHierarchyManager
 import com.android.systemui.plugins.qs.QS
+import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
 import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -26,7 +31,9 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.mock
 import dagger.BindsInstance
 import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,6 +58,7 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 
@@ -64,10 +72,8 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
 
+    private lateinit var transitionController: LockscreenShadeTransitionController
     private lateinit var testComponent: TestComponent
-
-    private val transitionController
-        get() = testComponent.transitionController
     private val configurationController
         get() = testComponent.configurationController
     private val disableFlagsRepository
@@ -85,8 +91,11 @@
     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
     @Mock lateinit var qS: QS
+    @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
     @Mock lateinit var scrimController: ScrimController
     @Mock lateinit var shadeViewController: ShadeViewController
+    @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
+    @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
     @Mock lateinit var stackscroller: NotificationStackScrollLayout
     @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
     @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
@@ -135,6 +144,49 @@
                         )
                 )
 
+        transitionController =
+            LockscreenShadeTransitionController(
+                statusBarStateController = statusbarStateController,
+                logger = mock(),
+                keyguardBypassController = keyguardBypassController,
+                lockScreenUserManager = lockScreenUserManager,
+                falsingCollector = FalsingCollectorFake(),
+                ambientState = mock(),
+                mediaHierarchyManager = mediaHierarchyManager,
+                scrimTransitionController =
+                    LockscreenShadeScrimTransitionController(
+                        scrimController = scrimController,
+                        context = context,
+                        configurationController = configurationController,
+                        dumpManager = mock(),
+                        splitShadeStateController = ResourcesSplitShadeStateController()
+                    ),
+                keyguardTransitionControllerFactory = { notificationPanelController ->
+                    LockscreenShadeKeyguardTransitionController(
+                        mediaHierarchyManager = mediaHierarchyManager,
+                        notificationPanelController = notificationPanelController,
+                        context = context,
+                        configurationController = configurationController,
+                        dumpManager = mock(),
+                        splitShadeStateController = ResourcesSplitShadeStateController()
+                    )
+                },
+                depthController = depthController,
+                context = context,
+                splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
+                singleShadeOverScrollerFactory = { singleShadeOverScroller },
+                activityStarter = mock(),
+                wakefulnessLifecycle = mock(),
+                configurationController = configurationController,
+                falsingManager = FalsingManagerFake(),
+                dumpManager = mock(),
+                qsTransitionControllerFactory = { qsTransitionController },
+                shadeRepository = testComponent.shadeRepository,
+                shadeInteractor = testComponent.shadeInteractor,
+                powerInteractor = testComponent.powerInteractor,
+                splitShadeStateController = ResourcesSplitShadeStateController(),
+            )
+
         transitionController.addCallback(transitionControllerCallback)
         transitionController.shadeViewController = shadeViewController
         transitionController.centralSurfaces = centralSurfaces
@@ -259,7 +311,7 @@
         verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
         verify(transitionControllerCallback, never())
             .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
-        verify(qS, never()).setTransitionToFullShadeProgress(anyBoolean(), anyFloat(), anyFloat())
+        verify(qsTransitionController, never()).dragDownAmount = anyFloat()
     }
 
     @Test
@@ -270,7 +322,7 @@
         verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
         verify(transitionControllerCallback)
             .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
-        verify(qS).setTransitionToFullShadeProgress(eq(true), anyFloat(), anyFloat())
+        verify(qsTransitionController).dragDownAmount = 10f
         verify(depthController).transitionToFullShadeProgress = anyFloat()
     }
 
@@ -473,8 +525,8 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(nsslController).setOverScrollAmount(0)
-        verify(scrimController, never()).setNotificationsOverScrollAmount(anyInt())
+        verify(singleShadeOverScroller).expansionDragDownAmount = 10f
+        verifyZeroInteractions(splitShadeOverScroller)
     }
 
     @Test
@@ -483,8 +535,8 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(nsslController).setOverScrollAmount(0)
-        verify(scrimController).setNotificationsOverScrollAmount(0)
+        verify(splitShadeOverScroller).expansionDragDownAmount = 10f
+        verifyZeroInteractions(singleShadeOverScroller)
     }
 
     @Test
@@ -545,10 +597,11 @@
     )
     interface TestComponent {
 
-        val transitionController: LockscreenShadeTransitionController
-
         val configurationController: FakeConfigurationController
         val disableFlagsRepository: FakeDisableFlagsRepository
+        val powerInteractor: PowerInteractor
+        val shadeInteractor: ShadeInteractor
+        val shadeRepository: FakeShadeRepository
         val testScope: TestScope
 
         @Component.Factory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 2b3fd34..2bee7b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -37,8 +37,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository;
@@ -71,12 +69,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
-        featureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
         mListener = new NotificationListener(
                 mContext,
-                featureFlags,
                 mNotificationManager,
                 new SilentNotificationStatusIconsVisibilityInteractor(
                         new NotificationListenerSettingsRepository()),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 764f7b6..560ebc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -33,9 +33,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -87,7 +88,8 @@
                 new RemoteInputControllerLogger(logcatLogBuffer()),
                 mClickNotifier,
                 new ActionClickLogger(logcatLogBuffer()),
-                mock(DumpManager.class));
+                mock(JavaAdapter.class),
+                mock(ShadeInteractor.class));
         mEntry = new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_NAME)
                 .setOpPkg(TEST_PACKAGE_NAME)
@@ -145,7 +147,8 @@
                 RemoteInputControllerLogger remoteInputControllerLogger,
                 NotificationClickNotifier clickNotifier,
                 ActionClickLogger actionClickLogger,
-                DumpManager dumpManager) {
+                JavaAdapter javaAdapter,
+                ShadeInteractor shadeInteractor) {
             super(
                     context,
                     notifPipelineFlags,
@@ -158,7 +161,8 @@
                     remoteInputControllerLogger,
                     clickNotifier,
                     actionClickLogger,
-                    dumpManager);
+                    javaAdapter,
+                    shadeInteractor);
         }
 
         public void setUpWithPresenterForTest(Callback callback,
@@ -170,3 +174,4 @@
 
     }
 }
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 3327e42..d6dfc5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -23,9 +23,30 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.util.mockito.mock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -40,17 +61,22 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class StatusBarStateControllerImplTest : SysuiTestCase() {
 
+    private val utils = SceneTestUtils(this)
+    private val testScope = utils.testScope
+    private lateinit var shadeInteractor: ShadeInteractor
+    private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
+    private lateinit var fromPrimaryBouncerTransitionInteractor:
+        FromPrimaryBouncerTransitionInteractor
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
-    @Mock private lateinit var mockDarkAnimator: ObjectAnimator
-    @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+    @Mock lateinit var mockDarkAnimator: ObjectAnimator
 
     private lateinit var controller: StatusBarStateControllerImpl
     private lateinit var uiEventLogger: UiEventLoggerFake
@@ -64,11 +90,75 @@
         uiEventLogger = UiEventLoggerFake()
         controller = object : StatusBarStateControllerImpl(
             uiEventLogger,
-            mock(DumpManager::class.java),
-            interactionJankMonitor, shadeExpansionStateManager
+            interactionJankMonitor,
+            mock(),
+            { shadeInteractor }
         ) {
             override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator }
         }
+
+        val powerInteractor = PowerInteractor(
+            FakePowerRepository(),
+            FalsingCollectorFake(),
+            mock(),
+            controller)
+        val keyguardRepository = FakeKeyguardRepository()
+        val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+        val featureFlags = FakeFeatureFlagsClassic()
+        val shadeRepository = FakeShadeRepository()
+        val sceneContainerFlags = FakeSceneContainerFlags()
+        val configurationRepository = FakeConfigurationRepository()
+        val keyguardInteractor = KeyguardInteractor(
+            keyguardRepository,
+            FakeCommandQueue(),
+            powerInteractor,
+            featureFlags,
+            sceneContainerFlags,
+            FakeKeyguardBouncerRepository(),
+            configurationRepository,
+            shadeRepository,
+            utils::sceneInteractor)
+        val keyguardTransitionInteractor = KeyguardTransitionInteractor(
+            testScope.backgroundScope,
+            keyguardTransitionRepository,
+            { keyguardInteractor },
+            { fromLockscreenTransitionInteractor },
+            { fromPrimaryBouncerTransitionInteractor })
+        fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
+            keyguardTransitionRepository,
+            keyguardTransitionInteractor,
+            testScope.backgroundScope,
+            keyguardInteractor,
+            featureFlags,
+            shadeRepository,
+            powerInteractor)
+        fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
+            keyguardTransitionRepository,
+            keyguardTransitionInteractor,
+            testScope.backgroundScope,
+            keyguardInteractor,
+            featureFlags,
+            mock(),
+            mock(),
+            powerInteractor)
+        shadeInteractor = ShadeInteractor(
+            testScope.backgroundScope,
+            FakeDeviceProvisioningRepository(),
+            FakeDisableFlagsRepository(),
+            mock(),
+            sceneContainerFlags,
+            utils::sceneInteractor,
+            keyguardRepository,
+            keyguardTransitionInteractor,
+            powerInteractor,
+            FakeUserSetupRepository(),
+            mock(),
+            SharedNotificationContainerInteractor(
+                configurationRepository,
+                mContext,
+                ResourcesSplitShadeStateController()),
+            shadeRepository,
+        )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
new file mode 100644
index 0000000..7f5384d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index a736182..428574b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -19,8 +19,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.setFlagValue
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -30,6 +29,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
@@ -39,6 +39,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations.initMocks
@@ -59,15 +60,18 @@
     @Mock private lateinit var stackController: NotifStackController
     @Mock private lateinit var section: NotifSection
 
-    val featureFlags =
-        FakeFeatureFlagsClassic().apply { setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR) }
-
     @Before
     fun setUp() {
         initMocks(this)
+        entry = NotificationEntryBuilder().setSection(section).build()
+        setUpWithFlags()
+    }
+
+    private fun setUpWithFlags(vararg flags: Pair<String, Boolean>) {
+        flags.forEach { (name, value) -> mSetFlagsRule.setFlagValue(name, value) }
+        reset(pipeline)
         coordinator =
             StackCoordinator(
-                featureFlags,
                 groupExpansionManagerImpl,
                 notificationIconAreaController,
                 renderListInteractor,
@@ -76,19 +80,18 @@
         afterRenderListListener = withArgCaptor {
             verify(pipeline).addOnAfterRenderListListener(capture())
         }
-        entry = NotificationEntryBuilder().setSection(section).build()
     }
 
     @Test
     fun testUpdateNotificationIcons() {
-        featureFlags.set(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR, false)
+        setUpWithFlags(NotificationIconContainerRefactor.FLAG_NAME to false)
         afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
         verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry)))
     }
 
     @Test
     fun testSetRenderedListOnInteractor() {
-        featureFlags.set(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR, true)
+        setUpWithFlags(NotificationIconContainerRefactor.FLAG_NAME to true)
         afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
         verify(renderListInteractor).setRenderedList(eq(listOf(entry)))
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index cbb0894..947bcfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -2,7 +2,6 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
@@ -19,7 +18,30 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-class NotificationInterruptStateProviderWrapperTest : SysuiTestCase() {
+class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
+    override val provider: VisualInterruptionDecisionProvider
+        get() =
+            NotificationInterruptStateProviderWrapper(
+                NotificationInterruptStateProviderImpl(
+                        context.contentResolver,
+                        powerManager,
+                        ambientDisplayConfiguration,
+                        batteryController,
+                        statusBarStateController,
+                        keyguardStateController,
+                        headsUpManager,
+                        logger,
+                        mainHandler,
+                        flags,
+                        keyguardNotificationVisibilityProvider,
+                        uiEventLogger,
+                        userTracker,
+                        deviceProvisionedController
+                    )
+                    .also { it.mUseHeadsUp = true }
+            )
+
+    // Tests of internals of the wrapper:
 
     @Test
     fun decisionOfTrue() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
new file mode 100644
index 0000000..6f4bbd5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -0,0 +1,221 @@
+package com.android.systemui.statusbar.notification.interruption
+
+import android.app.ActivityManager
+import android.app.Notification
+import android.app.Notification.BubbleMetadata
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_MUTABLE
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.graphics.drawable.Icon
+import android.hardware.display.FakeAmbientDisplayConfiguration
+import android.os.Handler
+import android.os.PowerManager
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.statusbar.FakeStatusBarStateController
+import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
+import com.android.systemui.statusbar.StatusBarState.KEYGUARD
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.utils.leaks.FakeBatteryController
+import com.android.systemui.utils.leaks.LeakCheckedTest
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.`when` as whenever
+
+abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
+    private val leakCheck = LeakCheckedTest.SysuiLeakCheck()
+
+    protected val ambientDisplayConfiguration = FakeAmbientDisplayConfiguration(context)
+    protected val batteryController = FakeBatteryController(leakCheck)
+    protected val deviceProvisionedController: DeviceProvisionedController = mock()
+    protected val flags: NotifPipelineFlags = mock()
+    protected val headsUpManager: HeadsUpManager = mock()
+    protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider =
+        mock()
+    protected val keyguardStateController: KeyguardStateController = mock()
+    protected val logger: NotificationInterruptLogger = mock()
+    protected val mainHandler: Handler = mock()
+    protected val powerManager: PowerManager = mock()
+    protected val statusBarStateController = FakeStatusBarStateController()
+    protected val uiEventLogger = UiEventLoggerFake()
+    protected val userTracker = FakeUserTracker()
+
+    protected abstract val provider: VisualInterruptionDecisionProvider
+
+    @Before
+    fun setUp() {
+        val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0)
+        userTracker.set(listOf(user), /* currentUserIndex = */ 0)
+
+        whenever(headsUpManager.isSnoozed(any())).thenReturn(false)
+        whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
+            .thenReturn(false)
+    }
+
+    @Test
+    fun testShouldPeek() {
+        ensureStateForPeek()
+
+        assertTrue(provider.makeUnloggedHeadsUpDecision(createPeekEntry()).shouldInterrupt)
+    }
+
+    @Test
+    fun testShouldPulse() {
+        ensureStateForPulse()
+
+        assertTrue(provider.makeUnloggedHeadsUpDecision(createPulseEntry()).shouldInterrupt)
+    }
+
+    @Test
+    fun testShouldFsi_awake() {
+        ensureStateForAwakeFsi()
+
+        assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+    }
+
+    @Test
+    fun testShouldFsi_dreaming() {
+        ensureStateForDreamingFsi()
+
+        assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+    }
+
+    @Test
+    fun testShouldFsi_keyguard() {
+        ensureStateForKeyguardFsi()
+
+        assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+    }
+
+    @Test
+    fun testShouldBubble() {
+        assertTrue(provider.makeAndLogBubbleDecision(createBubbleEntry()).shouldInterrupt)
+    }
+
+    private fun ensureStateForPeek() {
+        whenever(powerManager.isScreenOn).thenReturn(true)
+        statusBarStateController.dozing = false
+        statusBarStateController.dreaming = false
+    }
+
+    private fun ensureStateForPulse() {
+        ambientDisplayConfiguration.fakePulseOnNotificationEnabled = true
+        batteryController.setIsAodPowerSave(false)
+        statusBarStateController.dozing = true
+    }
+
+    private fun ensureStateForAwakeFsi() {
+        whenever(powerManager.isInteractive).thenReturn(false)
+        statusBarStateController.dreaming = false
+        statusBarStateController.state = SHADE
+    }
+
+    private fun ensureStateForDreamingFsi() {
+        whenever(powerManager.isInteractive).thenReturn(true)
+        statusBarStateController.dreaming = true
+        statusBarStateController.state = SHADE
+    }
+
+    private fun ensureStateForKeyguardFsi() {
+        whenever(powerManager.isInteractive).thenReturn(true)
+        statusBarStateController.dreaming = false
+        statusBarStateController.state = KEYGUARD
+    }
+
+    private fun createNotif(
+        hasFsi: Boolean = false,
+        bubbleMetadata: BubbleMetadata? = null
+    ): Notification {
+        return Notification.Builder(context, TEST_CHANNEL_ID)
+            .apply {
+                setContentTitle(TEST_CONTENT_TITLE)
+                setContentText(TEST_CONTENT_TEXT)
+
+                if (hasFsi) {
+                    setFullScreenIntent(mock(), /* highPriority = */ true)
+                }
+
+                if (bubbleMetadata != null) {
+                    setBubbleMetadata(bubbleMetadata)
+                }
+            }
+            .setContentTitle(TEST_CONTENT_TITLE)
+            .setContentText(TEST_CONTENT_TEXT)
+            .build()
+    }
+
+    private fun createBubbleMetadata(): BubbleMetadata {
+        val pendingIntent =
+            PendingIntent.getActivity(
+                context,
+                /* requestCode = */ 0,
+                Intent().setPackage(context.packageName),
+                FLAG_MUTABLE
+            )
+
+        val icon = Icon.createWithResource(context.resources, R.drawable.android)
+
+        return BubbleMetadata.Builder(pendingIntent, icon).build()
+    }
+
+    private fun createEntry(
+        notif: Notification,
+        importance: Int = IMPORTANCE_DEFAULT,
+        canBubble: Boolean? = null
+    ): NotificationEntry {
+        return NotificationEntryBuilder()
+            .apply {
+                setPkg(TEST_PACKAGE)
+                setOpPkg(TEST_PACKAGE)
+                setTag(TEST_TAG)
+                setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
+                setNotification(notif)
+                setImportance(importance)
+
+                if (canBubble != null) {
+                    setCanBubble(canBubble)
+                }
+            }
+            .build()
+    }
+
+    private fun createPeekEntry() = createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH)
+
+    private fun createPulseEntry() =
+        createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH).also {
+            modifyRanking(it).setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()
+        }
+
+    private fun createFsiEntry() =
+        createEntry(notif = createNotif(hasFsi = true), importance = IMPORTANCE_HIGH)
+
+    private fun createBubbleEntry() =
+        createEntry(
+            notif = createNotif(bubbleMetadata = createBubbleMetadata()),
+            importance = IMPORTANCE_HIGH,
+            canBubble = true
+        )
+}
+
+private const val TEST_CONTENT_TITLE = "Test Content Title"
+private const val TEST_CONTENT_TEXT = "Test content text"
+private const val TEST_CHANNEL_ID = "test_channel"
+private const val TEST_CHANNEL_NAME = "Test Channel"
+private const val TEST_PACKAGE = "test_package"
+private const val TEST_TAG = "test_tag"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
index ccef1d5..9b9cb82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
@@ -61,8 +61,9 @@
                 else -> null
             } as T?
         }
-        mNormalColor =
-            Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface)
+
+        mNormalColor = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 390c1dd..02a67d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -21,23 +21,25 @@
 import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
-import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
-import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Rule
@@ -55,98 +57,118 @@
 
     @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
-    // mocks
     @Mock private lateinit var keyguardTransitionController: LockscreenShadeTransitionController
     @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController
-    @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
 
-    // fakes
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository()
-    private val a11yRepo = FakeAccessibilityRepository()
-    private val powerRepository = FakePowerRepository()
-    private val powerInteractor by lazy {
-        PowerInteractorFactory.create(
-                repository = powerRepository,
-                screenOffAnimationController = screenOffAnimationController,
-                statusBarStateController = statusBarStateController,
-            )
-            .powerInteractor
-    }
-
-    // real impls
-    private val a11yInteractor = AccessibilityInteractor(a11yRepo)
-    private val activatableViewModel = ActivatableNotificationViewModel(a11yInteractor)
-    private val interactor by lazy {
-        NotificationShelfInteractor(
-            keyguardRepository,
-            deviceEntryFaceAuthRepository,
-            powerInteractor,
-            keyguardTransitionController,
-        )
-    }
-    private val underTest by lazy { NotificationShelfViewModel(interactor, activatableViewModel) }
+    private lateinit var testComponent: TestComponent
 
     @Before
     fun setUp() {
         whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true)
+        testComponent =
+            DaggerNotificationShelfViewModelTest_TestComponent.factory()
+                .create(
+                    test = this,
+                    mocks =
+                        TestMocksModule(
+                            lockscreenShadeTransitionController = keyguardTransitionController,
+                            screenOffAnimationController = screenOffAnimationController,
+                            statusBarStateController = statusBarStateController,
+                        )
+                )
     }
 
     @Test
-    fun canModifyColorOfNotifications_whenKeyguardNotShowing() = runTest {
-        val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications)
+    fun canModifyColorOfNotifications_whenKeyguardNotShowing() =
+        with(testComponent) {
+            testScope.runTest {
+                val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications)
 
-        keyguardRepository.setKeyguardShowing(false)
+                keyguardRepository.setKeyguardShowing(false)
 
-        assertThat(canModifyNotifColor).isTrue()
-    }
+                assertThat(canModifyNotifColor).isTrue()
+            }
+        }
 
     @Test
-    fun canModifyColorOfNotifications_whenKeyguardShowingAndNotBypass() = runTest {
-        val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications)
+    fun canModifyColorOfNotifications_whenKeyguardShowingAndNotBypass() =
+        with(testComponent) {
+            testScope.runTest {
+                val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications)
 
-        keyguardRepository.setKeyguardShowing(true)
-        deviceEntryFaceAuthRepository.isBypassEnabled.value = false
+                keyguardRepository.setKeyguardShowing(true)
+                deviceEntryFaceAuthRepository.isBypassEnabled.value = false
 
-        assertThat(canModifyNotifColor).isTrue()
-    }
+                assertThat(canModifyNotifColor).isTrue()
+            }
+        }
 
     @Test
-    fun cannotModifyColorOfNotifications_whenBypass() = runTest {
-        val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications)
+    fun cannotModifyColorOfNotifications_whenBypass() =
+        with(testComponent) {
+            testScope.runTest {
+                val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications)
 
-        keyguardRepository.setKeyguardShowing(true)
-        deviceEntryFaceAuthRepository.isBypassEnabled.value = true
+                keyguardRepository.setKeyguardShowing(true)
+                deviceEntryFaceAuthRepository.isBypassEnabled.value = true
 
-        assertThat(canModifyNotifColor).isFalse()
-    }
+                assertThat(canModifyNotifColor).isFalse()
+            }
+        }
 
     @Test
-    fun isClickable_whenKeyguardShowing() = runTest {
-        val isClickable by collectLastValue(underTest.isClickable)
+    fun isClickable_whenKeyguardShowing() =
+        with(testComponent) {
+            testScope.runTest {
+                val isClickable by collectLastValue(underTest.isClickable)
 
-        keyguardRepository.setKeyguardShowing(true)
+                keyguardRepository.setKeyguardShowing(true)
 
-        assertThat(isClickable).isTrue()
-    }
+                assertThat(isClickable).isTrue()
+            }
+        }
 
     @Test
-    fun isNotClickable_whenKeyguardNotShowing() = runTest {
-        val isClickable by collectLastValue(underTest.isClickable)
+    fun isNotClickable_whenKeyguardNotShowing() =
+        with(testComponent) {
+            testScope.runTest {
+                val isClickable by collectLastValue(underTest.isClickable)
 
-        keyguardRepository.setKeyguardShowing(false)
+                keyguardRepository.setKeyguardShowing(false)
 
-        assertThat(isClickable).isFalse()
-    }
+                assertThat(isClickable).isFalse()
+            }
+        }
 
     @Test
-    fun onClicked_goesToLockedShade() {
-        whenever(statusBarStateController.isDozing).thenReturn(true)
+    fun onClicked_goesToLockedShade() =
+        with(testComponent) {
+            whenever(statusBarStateController.isDozing).thenReturn(true)
 
-        underTest.onShelfClicked()
+            underTest.onShelfClicked()
 
-        assertThat(powerRepository.lastWakeReason).isNotNull()
-        assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_GESTURE)
-        verify(keyguardTransitionController).goToLockedShade(Mockito.isNull(), eq(true))
+            assertThat(powerRepository.lastWakeReason).isNotNull()
+            assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_GESTURE)
+            verify(keyguardTransitionController).goToLockedShade(Mockito.isNull(), eq(true))
+        }
+
+    @Component(modules = [SysUITestModule::class, ActivatableNotificationViewModelModule::class])
+    @SysUISingleton
+    interface TestComponent {
+
+        val underTest: NotificationShelfViewModel
+        val deviceEntryFaceAuthRepository: FakeDeviceEntryFaceAuthRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val powerRepository: FakePowerRepository
+        val testScope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 3dafb23..39e3d5da3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -84,6 +85,7 @@
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -716,8 +718,9 @@
                 mSecureSettings,
                 mock(NotificationDismissibilityProvider.class),
                 mActivityStarter,
-                new ResourcesSplitShadeStateController()
-        );
+                new ResourcesSplitShadeStateController(),
+                mock(ConfigurationState.class),
+                mock(ShelfNotificationIconViewStore.class));
     }
 
     static class LogMatcher implements ArgumentMatcher<LogMaker> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8f36d4f..033c96a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,8 +19,6 @@
 import static android.view.View.GONE;
 import static android.view.WindowInsets.Type.ime;
 
-import static com.android.systemui.Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -167,11 +165,6 @@
         mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
         mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION);
         mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
-        // Some tests in this file test the FooterView. Since we're refactoring the FooterView
-        // business logic out of the NSSL, the behavior tested in this file will eventually be
-        // tested directly in the new FooterView stack. For now, we just want to make sure that the
-        // old behavior is preserved when the flag is off.
-        setFlagDefault(mSetFlagsRule, FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR);
 
         // Inject dependencies before initializing the layout
         mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index cfd220b..164325a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -140,7 +140,7 @@
         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
-        when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
+        when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
         when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
                 .thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 41eaf85..05fd6d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -185,8 +185,6 @@
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.startingsurface.StartingSurface;
 
-import dagger.Lazy;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -200,6 +198,8 @@
 
 import javax.inject.Provider;
 
+import dagger.Lazy;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -335,7 +335,6 @@
         mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
         // Set default value to avoid IllegalStateException.
         mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
         // For the Shade to respond to Back gesture, we must enable the event routing
         mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
         // For the Shade to animate during the Back gesture, we must enable the animation flag.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 472709c..53cb8a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -43,7 +43,6 @@
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -105,7 +104,6 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
         mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle,
                 mStatusBarStateController, mDeviceProvisionedController, mFeatureFlags,
                 mHeadsUpManager, mBatteryController, mScrimController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 529e2c9..c1ef1ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -35,7 +35,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shade.ShadeHeadsUpTracker;
@@ -90,7 +89,6 @@
     @Before
     public void setUp() throws Exception {
         allowTestableLooperAsMainThread();
-        mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
         mTestHelper = new NotificationTestHelper(
                 mContext,
                 mDependency,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index cda2a74..48b95d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -34,7 +34,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import org.junit.After;
 import org.junit.Before;
@@ -56,6 +57,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import kotlinx.coroutines.flow.StateFlowKt;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -70,8 +73,9 @@
     @Mock private KeyguardBypassController mBypassController;
     @Mock private ConfigurationControllerImpl mConfigurationController;
     @Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
-    @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
     @Mock private UiEventLogger mUiEventLogger;
+    @Mock private JavaAdapter mJavaAdapter;
+    @Mock private ShadeInteractor mShadeInteractor;
 
     private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
         TestableHeadsUpManagerPhone(
@@ -85,7 +89,8 @@
                 Handler handler,
                 AccessibilityManagerWrapper accessibilityManagerWrapper,
                 UiEventLogger uiEventLogger,
-                ShadeExpansionStateManager shadeExpansionStateManager
+                JavaAdapter javaAdapter,
+                ShadeInteractor shadeInteractor
         ) {
             super(
                     context,
@@ -98,7 +103,8 @@
                     handler,
                     accessibilityManagerWrapper,
                     uiEventLogger,
-                    shadeExpansionStateManager
+                    javaAdapter,
+                    shadeInteractor
             );
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -117,7 +123,8 @@
                 mTestHandler,
                 mAccessibilityManagerWrapper,
                 mUiEventLogger,
-                mShadeExpansionStateManager
+                mJavaAdapter,
+                mShadeInteractor
         );
     }
 
@@ -129,6 +136,7 @@
     @Before
     @Override
     public void setUp() {
+        when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
         final AccessibilityManagerWrapper accessibilityMgr =
                 mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
         when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 6209f73..bd0dbee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -86,7 +86,7 @@
         featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
 
         whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true)
-        whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true)
+        whenever(keyguardStateController.isFaceEnrolled).thenReturn(true)
     }
 
     @After
@@ -158,7 +158,7 @@
         keyguardBypassController.registerOnBypassStateChangedListener(bypassListener)
         verify(keyguardStateController).addCallback(callback.capture())
 
-        callback.value.onFaceAuthEnabledChanged()
+        callback.value.onFaceEnrolledChanged()
         verify(bypassListener).onBypassStateChanged(anyBoolean())
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 6484389..361df1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -168,7 +168,6 @@
                 PowerInteractorFactory.create().getPowerInteractor(),
                 mFeatureFlags,
                 mSceneTestUtils.getSceneContainerFlags(),
-                mSceneTestUtils.getDeviceEntryRepository(),
                 new FakeKeyguardBouncerRepository(),
                 new FakeConfigurationRepository(),
                 new FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 1b8cfd4..c24d9ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.wm.shell.bubbles.Bubbles;
 
@@ -82,6 +83,7 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
         mController = new LegacyNotificationIconAreaControllerImpl(
                 mContext,
                 mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index c282c1e..62d8f7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -31,9 +31,7 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.`when` as whenever
 
-/**
- * Tests for {@link NotificationIconContainer}.
- */
+/** Tests for {@link NotificationIconContainer}. */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
@@ -43,8 +41,10 @@
 
     @Test
     fun calculateWidthFor_zeroIcons_widthIsZero() {
-        assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f),
-                /* actual= */ 0f)
+        assertEquals(
+            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f),
+            /* actual= */ 0f
+        )
     }
 
     @Test
@@ -53,8 +53,10 @@
         iconContainer.setActualPaddingEnd(10f)
         iconContainer.setIconSize(10)
 
-        assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f),
-                /* actual= */ 30f)
+        assertEquals(
+            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f),
+            /* actual= */ 30f
+        )
     }
 
     @Test
@@ -63,8 +65,10 @@
         iconContainer.setActualPaddingEnd(10f)
         iconContainer.setIconSize(10)
 
-        assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f),
-                /* actual= */ 60f)
+        assertEquals(
+            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f),
+            /* actual= */ 60f
+        )
     }
 
     @Test
@@ -72,8 +76,10 @@
         iconContainer.setActualPaddingStart(10f)
         iconContainer.setActualPaddingEnd(10f)
         iconContainer.setIconSize(10)
-        assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f),
-                /* actual= */ 60f)
+        assertEquals(
+            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f),
+            /* actual= */ 60f
+        )
     }
 
     @Test
@@ -206,112 +212,121 @@
 
     @Test
     fun shouldForceOverflow_appearingAboveSpeedBump_true() {
-        val forceOverflow = iconContainer.shouldForceOverflow(
+        val forceOverflow =
+            iconContainer.shouldForceOverflow(
                 /* i= */ 1,
                 /* speedBumpIndex= */ 0,
                 /* iconAppearAmount= */ 1f,
                 /* maxVisibleIcons= */ 5
-        )
+            )
         assertTrue(forceOverflow)
     }
 
     @Test
     fun shouldForceOverflow_moreThanMaxVisible_true() {
-        val forceOverflow = iconContainer.shouldForceOverflow(
+        val forceOverflow =
+            iconContainer.shouldForceOverflow(
                 /* i= */ 10,
                 /* speedBumpIndex= */ 11,
                 /* iconAppearAmount= */ 0f,
                 /* maxVisibleIcons= */ 5
-        )
+            )
         assertTrue(forceOverflow)
     }
 
     @Test
     fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() {
-        val forceOverflow = iconContainer.shouldForceOverflow(
+        val forceOverflow =
+            iconContainer.shouldForceOverflow(
                 /* i= */ 0,
                 /* speedBumpIndex= */ 11,
                 /* iconAppearAmount= */ 0f,
                 /* maxVisibleIcons= */ 5
-        )
+            )
         assertFalse(forceOverflow)
     }
 
     @Test
     fun isOverflowing_lastChildXLessThanLayoutEnd_false() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ true,
                 /* translationX= */ 0f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertFalse(isOverflowing)
     }
 
-
     @Test
     fun isOverflowing_lastChildXEqualToLayoutEnd_true() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ true,
                 /* translationX= */ 10f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertTrue(isOverflowing)
     }
 
     @Test
     fun isOverflowing_lastChildXGreaterThanDotX_true() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ true,
                 /* translationX= */ 9f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertTrue(isOverflowing)
     }
 
     @Test
     fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ true,
                 /* translationX= */ 20f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertTrue(isOverflowing)
     }
 
     @Test
     fun isOverflowing_notLastChildXLessThanDotX_false() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ false,
                 /* translationX= */ 0f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertFalse(isOverflowing)
     }
 
     @Test
     fun isOverflowing_notLastChildXGreaterThanDotX_true() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ false,
                 /* translationX= */ 20f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertTrue(isOverflowing)
     }
 
     @Test
     fun isOverflowing_notLastChildXEqualToDotX_true() {
-        val isOverflowing = iconContainer.isOverflowing(
+        val isOverflowing =
+            iconContainer.isOverflowing(
                 /* isLastChild= */ false,
                 /* translationX= */ 8f,
                 /* layoutEnd= */ 10f,
                 /* iconSize= */ 2f,
-        )
+            )
         assertTrue(isOverflowing)
     }
 
@@ -327,4 +342,4 @@
         whenever(iconView.notification).thenReturn(sbn)
         return iconView
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 9a77f0c..d1b9b8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -24,13 +24,8 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Fragment;
@@ -42,8 +37,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
 
@@ -67,10 +60,8 @@
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
-import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -283,15 +274,15 @@
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
     }
 
     @Test
@@ -323,7 +314,7 @@
 
         // THEN all views are hidden
         assertEquals(View.INVISIBLE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -339,7 +330,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -356,7 +347,7 @@
 
         // THEN all views are hidden
         assertEquals(View.INVISIBLE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
 
         // WHEN the shade is updated to no longer be open
@@ -367,7 +358,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -381,7 +372,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -395,7 +386,7 @@
 
         // THEN all views are hidden
         assertEquals(View.GONE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -409,7 +400,7 @@
 
         // THEN all views are hidden
         assertEquals(View.GONE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
 
         // WHEN the transition has finished
@@ -418,7 +409,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -451,7 +442,7 @@
 
         assertEquals(View.VISIBLE,
                 mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
-        verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
     }
 
     @Test
@@ -507,6 +498,20 @@
     }
 
     @Test
+    public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // Ongoing call started
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+        // Notification area is hidden without delay
+        assertEquals(0f, mNotificationAreaInner.getAlpha(), 0.01);
+        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+    }
+
+    @Test
     public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -713,8 +718,6 @@
                 mock(NotificationIconContainerStatusBarViewModel.class),
                 mock(ConfigurationState.class),
                 mock(ConfigurationController.class),
-                mock(DozeParameters.class),
-                mock(ScreenOffAnimationController.class),
                 mock(StatusBarNotificationIconViewStore.class),
                 mock(DemoModeController.class));
     }
@@ -729,18 +732,7 @@
     private void setUpNotificationIconAreaController() {
         mMockNotificationAreaController = mock(NotificationIconAreaController.class);
 
-        mNotificationAreaInner = mock(View.class);
-
-        when(mNotificationAreaInner.getLayoutParams()).thenReturn(
-                new FrameLayout.LayoutParams(100, 100));
-        // We should probably start using a real view so that we don't need to mock these methods.
-        ViewPropertyAnimator viewPropertyAnimator = mock(ViewPropertyAnimator.class);
-        when(mNotificationAreaInner.animate()).thenReturn(viewPropertyAnimator);
-        when(viewPropertyAnimator.alpha(anyFloat())).thenReturn(viewPropertyAnimator);
-        when(viewPropertyAnimator.setDuration(anyLong())).thenReturn(viewPropertyAnimator);
-        when(viewPropertyAnimator.setInterpolator(any())).thenReturn(viewPropertyAnimator);
-        when(viewPropertyAnimator.setStartDelay(anyLong())).thenReturn(viewPropertyAnimator);
-        when(viewPropertyAnimator.withEndAction(any())).thenReturn(viewPropertyAnimator);
+        mNotificationAreaInner = new View(mContext);
 
         when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
                 mNotificationAreaInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index e6b09e3..5c960b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -97,20 +97,20 @@
     }
 
     @Test
-    public void testFaceAuthEnabledChanged_calledWhenFaceEnrollmentStateChanges() {
+    public void testFaceAuthEnrolleddChanged_calledWhenFaceEnrollmentStateChanges() {
         KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class);
 
-        when(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(false);
         verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture());
         mKeyguardStateController.addCallback(callback);
-        assertThat(mKeyguardStateController.isFaceAuthEnabled()).isFalse();
+        assertThat(mKeyguardStateController.isFaceEnrolled()).isFalse();
 
-        when(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(true);
         mUpdateCallbackCaptor.getValue().onBiometricEnrollmentStateChanged(
                 BiometricSourceType.FACE);
 
-        assertThat(mKeyguardStateController.isFaceAuthEnabled()).isTrue();
-        verify(callback).onFaceAuthEnabledChanged();
+        assertThat(mKeyguardStateController.isFaceEnrolled()).isTrue();
+        verify(callback).onFaceEnrolledChanged();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
index b78e839..63de068 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -23,26 +23,27 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 import java.util.Date
 
 @RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class VariableDateViewControllerTest : SysuiTestCase() {
 
@@ -57,12 +58,15 @@
     private lateinit var broadcastDispatcher: BroadcastDispatcher
     @Mock
     private lateinit var view: VariableDateView
+    @Mock
+    private lateinit var shadeInteractor: ShadeInteractor
     @Captor
     private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener>
 
+    private val qsExpansion = MutableStateFlow(0F)
+
     private var lastText: String? = null
 
-    private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
     private lateinit var systemClock: FakeSystemClock
     private lateinit var testableLooper: TestableLooper
     private lateinit var testableHandler: Handler
@@ -80,7 +84,7 @@
         systemClock = FakeSystemClock()
         systemClock.setCurrentTimeMillis(TIME_STAMP)
 
-        shadeExpansionStateManager = ShadeExpansionStateManager()
+        `when`(shadeInteractor.qsExpansion).thenReturn(qsExpansion)
 
         `when`(view.longerPattern).thenReturn(LONG_PATTERN)
         `when`(view.shorterPattern).thenReturn(SHORT_PATTERN)
@@ -91,6 +95,7 @@
             Unit
         }
         `when`(view.isAttachedToWindow).thenReturn(true)
+        `when`(view.viewTreeObserver).thenReturn(mock())
 
         val date = Date(TIME_STAMP)
         longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN))
@@ -103,12 +108,12 @@
         }
 
         controller = VariableDateViewController(
-                systemClock,
-                broadcastDispatcher,
-                shadeExpansionStateManager,
-                mock(),
-                testableHandler,
-                view
+            systemClock,
+            broadcastDispatcher,
+            shadeInteractor,
+            mock(),
+            testableHandler,
+            view
         )
 
         controller.init()
@@ -180,7 +185,7 @@
 
     @Test
     fun testQsExpansionTrue_ignoreAtMostMeasureRequests() {
-        shadeExpansionStateManager.onQsExpansionFractionChanged(0f)
+        qsExpansion.value = 0f
 
         onMeasureListenerCaptor.value.onMeasureAction(
                 getTextLength(shortText).toInt(),
@@ -195,7 +200,7 @@
 
     @Test
     fun testQsExpansionFalse_acceptAtMostMeasureRequests() {
-        shadeExpansionStateManager.onQsExpansionFractionChanged(1f)
+        qsExpansion.value = 1f
 
         onMeasureListenerCaptor.value.onMeasureAction(
                 getTextLength(shortText).toInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 1bc346d..96db09e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -57,7 +57,6 @@
             PowerInteractorFactory.create().powerInteractor,
             FakeFeatureFlagsClassic(),
             sceneTestUtils.sceneContainerFlags,
-            sceneTestUtils.deviceEntryRepository,
             FakeKeyguardBouncerRepository(),
             FakeConfigurationRepository(),
             FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/LayoutInflaterUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/LayoutInflaterUtilTest.kt
deleted file mode 100644
index 1c8465a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/LayoutInflaterUtilTest.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2023 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.systemui.util.kotlin
-
-import android.content.Context
-import android.testing.AndroidTestingRunner
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.view.reinflateAndBindLatest
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancelAndJoin
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.asSharedFlow
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class LayoutInflaterUtilTest : SysuiTestCase() {
-    @JvmField @Rule val mockito = MockitoJUnit.rule()
-
-    private var inflationCount = 0
-    private var callbackCount = 0
-    @Mock private lateinit var disposableHandle: DisposableHandle
-
-    inner class TestLayoutInflater : LayoutInflater(context) {
-        override fun inflate(resource: Int, root: ViewGroup?, attachToRoot: Boolean): View {
-            inflationCount++
-            return View(context)
-        }
-
-        override fun cloneInContext(p0: Context?): LayoutInflater {
-            // not needed for this test
-            return this
-        }
-    }
-
-    val underTest = TestLayoutInflater()
-
-    @After
-    fun cleanUp() {
-        inflationCount = 0
-        callbackCount = 0
-    }
-
-    @Test
-    fun testReinflateAndBindLatest_inflatesWithoutEmission() = runTest {
-        backgroundScope.launch {
-            underTest.reinflateAndBindLatest(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-                emptyFlow<Unit>()
-            ) {
-                callbackCount++
-                null
-            }
-        }
-
-        // Inflates without an emission
-        runCurrent()
-        assertThat(inflationCount).isEqualTo(1)
-        assertThat(callbackCount).isEqualTo(1)
-    }
-
-    @Test
-    fun testReinflateAndBindLatest_reinflatesOnEmission() = runTest {
-        val observable = MutableSharedFlow<Unit>()
-        val flow = observable.asSharedFlow()
-        backgroundScope.launch {
-            underTest.reinflateAndBindLatest(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-                flow
-            ) {
-                callbackCount++
-                null
-            }
-        }
-
-        listOf(1, 2, 3).forEach { count ->
-            runCurrent()
-            assertThat(inflationCount).isEqualTo(count)
-            assertThat(callbackCount).isEqualTo(count)
-            observable.emit(Unit)
-        }
-    }
-
-    @Test
-    fun testReinflateAndBindLatest_disposesOnCancel() = runTest {
-        val job = launch {
-            underTest.reinflateAndBindLatest(
-                resource = 0,
-                root = null,
-                attachToRoot = false,
-                emptyFlow()
-            ) {
-                callbackCount++
-                disposableHandle
-            }
-        }
-
-        runCurrent()
-        job.cancelAndJoin()
-        verify(disposableHandle).dispose()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c4c7472..7456e00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -26,6 +26,8 @@
 import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertTrue;
 
@@ -40,6 +42,9 @@
 
 import android.app.KeyguardManager;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.os.SystemClock;
 import android.provider.Settings;
@@ -52,6 +57,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageButton;
 
 import androidx.test.core.view.MotionEventBuilder;
 import androidx.test.filters.SmallTest;
@@ -90,6 +96,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
 import java.util.function.Predicate;
 
 @SmallTest
@@ -757,6 +764,86 @@
                 foundCaptionLog);
     }
 
+    @Test
+    public void turnOnDnD_volumeSliderIconChangesToDnd() {
+        State state = createShellState();
+        state.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+        mDialog.onStateChangedH(state);
+        mTestableLooper.processAllMessages();
+
+        boolean foundDnDIcon = findDndIconAmongVolumeRows();
+        assertTrue(foundDnDIcon);
+    }
+
+    @Test
+    public void turnOffDnD_volumeSliderIconIsNotDnd() {
+        State state = createShellState();
+        state.zenMode = Settings.Global.ZEN_MODE_OFF;
+
+        mDialog.onStateChangedH(state);
+        mTestableLooper.processAllMessages();
+
+        boolean foundDnDIcon = findDndIconAmongVolumeRows();
+        assertFalse(foundDnDIcon);
+    }
+
+    /**
+     * @return true if at least one volume row has the DND icon
+     */
+    private boolean findDndIconAmongVolumeRows() {
+        ViewGroup volumeDialogRows = mDialog.getDialogView().findViewById(R.id.volume_dialog_rows);
+        assumeNotNull(volumeDialogRows);
+        Drawable expected =  getContext().getDrawable(com.android.internal.R.drawable.ic_qs_dnd);
+        boolean foundDnDIcon = false;
+        final int rowCount = volumeDialogRows.getChildCount();
+        // we don't make assumptions about the position of the dnd row
+        for (int i = 0; i < rowCount && !foundDnDIcon; i++) {
+            View volumeRow = volumeDialogRows.getChildAt(i);
+            ImageButton rowIcon = volumeRow.findViewById(R.id.volume_row_icon);
+            assertNotNull(rowIcon);
+
+            // VolumeDialogImpl changes tint and alpha in a private method, so we clear those here.
+            rowIcon.setImageTintList(null);
+            rowIcon.setAlpha(0xFF);
+
+            Drawable actual = rowIcon.getDrawable();
+            foundDnDIcon |= areDrawablesEqual(expected, actual);
+        }
+        return foundDnDIcon;
+    }
+
+    private boolean areDrawablesEqual(Drawable drawable1, Drawable drawable2) {
+        int size = 100;
+        Bitmap bm1 = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        Bitmap bm2 = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+
+        Canvas canvas1 = new Canvas(bm1);
+        Canvas canvas2 = new Canvas(bm2);
+
+        drawable1.setBounds(0, 0, size, size);
+        drawable2.setBounds(0, 0, size, size);
+
+        drawable1.draw(canvas1);
+        drawable2.draw(canvas2);
+
+        boolean areBitmapsEqual = areBitmapsEqual(bm1, bm2);
+        bm1.recycle();
+        bm2.recycle();
+        return areBitmapsEqual;
+    }
+
+    private boolean areBitmapsEqual(Bitmap a, Bitmap b) {
+        if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false;
+        int w = a.getWidth();
+        int h = a.getHeight();
+        int[] aPix = new int[w * h];
+        int[] bPix = new int[w * h];
+        a.getPixels(aPix, 0, w, 0, 0, w, h);
+        b.getPixels(bPix, 0, w, 0, 0, w, h);
+        return Arrays.equals(aPix, bPix);
+    }
+
     @After
     public void teardown() {
         // Detailed logs to track down timeout issues in b/299491332
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index a42fa41..ec808c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -94,7 +94,6 @@
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -403,7 +402,6 @@
                 powerInteractor,
                 featureFlags,
                 sceneContainerFlags,
-                new FakeDeviceEntryRepository(),
                 new FakeKeyguardBouncerRepository(),
                 configurationRepository,
                 shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/display/FakeAmbientDisplayConfiguration.kt b/packages/SystemUI/tests/utils/src/android/hardware/display/FakeAmbientDisplayConfiguration.kt
new file mode 100644
index 0000000..cdd0ff7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/hardware/display/FakeAmbientDisplayConfiguration.kt
@@ -0,0 +1,68 @@
+package android.hardware.display
+
+import android.content.Context
+
+class FakeAmbientDisplayConfiguration(context: Context) : AmbientDisplayConfiguration(context) {
+    var fakePulseOnNotificationEnabled = true
+
+    override fun pulseOnNotificationEnabled(user: Int) = fakePulseOnNotificationEnabled
+
+    override fun pulseOnNotificationAvailable() = TODO("Not yet implemented")
+
+    override fun pickupGestureEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun dozePickupSensorAvailable() = TODO("Not yet implemented")
+
+    override fun tapGestureEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun tapSensorAvailable() = TODO("Not yet implemented")
+
+    override fun doubleTapGestureEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun doubleTapSensorAvailable() = TODO("Not yet implemented")
+
+    override fun quickPickupSensorEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun screenOffUdfpsEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun wakeScreenGestureAvailable() = TODO("Not yet implemented")
+
+    override fun wakeLockScreenGestureEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun wakeDisplayGestureEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun getWakeLockScreenDebounce() = TODO("Not yet implemented")
+
+    override fun doubleTapSensorType() = TODO("Not yet implemented")
+
+    override fun tapSensorTypeMapping() = TODO("Not yet implemented")
+
+    override fun longPressSensorType() = TODO("Not yet implemented")
+
+    override fun udfpsLongPressSensorType() = TODO("Not yet implemented")
+
+    override fun quickPickupSensorType() = TODO("Not yet implemented")
+
+    override fun pulseOnLongPressEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun alwaysOnEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun alwaysOnAvailable() = TODO("Not yet implemented")
+
+    override fun alwaysOnAvailableForUser(user: Int) = TODO("Not yet implemented")
+
+    override fun ambientDisplayComponent() = TODO("Not yet implemented")
+
+    override fun accessibilityInversionEnabled(user: Int) = TODO("Not yet implemented")
+
+    override fun ambientDisplayAvailable() = TODO("Not yet implemented")
+
+    override fun dozeSuppressed(user: Int) = TODO("Not yet implemented")
+
+    override fun disableDozeSettings(userId: Int) = TODO("Not yet implemented")
+
+    override fun disableDozeSettings(shouldDisableNonUserConfigurable: Boolean, userId: Int) =
+        TODO("Not yet implemented")
+
+    override fun restoreDozeSettings(userId: Int) = TODO("Not yet implemented")
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 6ef812b..29e737e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,9 +15,8 @@
  */
 package com.android.systemui;
 
-import static com.android.systemui.Flags.FLAG_EXAMPLE_FLAG;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -38,11 +37,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.broadcast.FakeBroadcastDispatcher;
-import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.settings.UserTracker;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -53,7 +48,6 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
 
 /**
@@ -71,7 +65,7 @@
             new AndroidXAnimatorIsolationRule();
 
     @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
     @Rule
     public SysuiTestableContext mContext = new SysuiTestableContext(
@@ -86,7 +80,7 @@
 
     public TestableDependency mDependency;
     private Instrumentation mRealInstrumentation;
-    private FakeBroadcastDispatcher mFakeBroadcastDispatcher;
+    private SysuiTestDependency mSysuiDependency;
 
     @Before
     public void SysuiSetup() throws Exception {
@@ -94,21 +88,8 @@
         if (isRobolectricTest()) {
             mContext = mContext.createDefaultDisplayContext();
         }
-        // Set the value of a single gantry flag inside the com.android.systemui package to
-        // ensure all flags in that package are faked (and thus require a value to be set).
-        mSetFlagsRule.disableFlags(FLAG_EXAMPLE_FLAG);
-
-        mDependency = SysuiTestDependencyKt.installSysuiTestDependency(mContext);
-        mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(
-                mContext,
-                mContext.getMainExecutor(),
-                mock(Looper.class),
-                mock(Executor.class),
-                mock(DumpManager.class),
-                mock(BroadcastDispatcherLogger.class),
-                mock(UserTracker.class),
-                shouldFailOnLeakedReceiver());
-
+        mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
+        mDependency = mSysuiDependency.install();
         mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
         Instrumentation inst = spy(mRealInstrumentation);
         when(inst.getContext()).thenAnswer(invocation -> {
@@ -120,11 +101,6 @@
                     "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
         });
         InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
-        // Many tests end up creating a BroadcastDispatcher. Instead, give them a fake that will
-        // record receivers registered. They are not actually leaked as they are kept just as a weak
-        // reference and are never sent to the Context. This will also prevent a real
-        // BroadcastDispatcher from actually registering receivers.
-        mDependency.injectTestDependency(BroadcastDispatcher.class, mFakeBroadcastDispatcher);
     }
 
     protected boolean shouldFailOnLeakedReceiver() {
@@ -144,8 +120,9 @@
         }
         disallowTestableLooperAsMainThread();
         mContext.cleanUpReceivers(this.getClass().getSimpleName());
-        if (mFakeBroadcastDispatcher != null) {
-            mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName());
+        FakeBroadcastDispatcher dispatcher = getFakeBroadcastDispatcher();
+        if (dispatcher != null) {
+            dispatcher.cleanUpReceivers(this.getClass().getSimpleName());
         }
     }
 
@@ -172,7 +149,7 @@
     }
 
     public FakeBroadcastDispatcher getFakeBroadcastDispatcher() {
-        return mFakeBroadcastDispatcher;
+        return mSysuiDependency.getFakeBroadcastDispatcher();
     }
 
     public SysuiTestableContext getContext() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
index c791f4f..d89d7b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
@@ -1,26 +1,60 @@
 package com.android.systemui
 
 import android.annotation.SuppressLint
-import android.content.Context
+import android.os.Looper
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.fakeDialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import java.util.concurrent.Executor
+import org.mockito.Mockito.mock
 
-@SuppressLint("VisibleForTests")
-fun installSysuiTestDependency(context: Context): TestableDependency {
-    val initializer: SystemUIInitializer = SystemUIInitializerImpl(context)
-    initializer.init(true)
+class SysuiTestDependency(
+    val context: SysuiTestableContext,
+    private val shouldFailOnLeakedReceiver: Boolean
+) {
+    var fakeBroadcastDispatcher: FakeBroadcastDispatcher? = null
 
-    val dependency = TestableDependency(initializer.sysUIComponent.createDependency())
-    Dependency.setInstance(dependency)
+    @SuppressLint("VisibleForTests")
+    fun install(): TestableDependency {
+        val initializer: SystemUIInitializer = SystemUIInitializerImpl(context)
+        initializer.init(true)
 
-    dependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+        val dependency = TestableDependency(initializer.sysUIComponent.createDependency())
+        Dependency.setInstance(dependency)
 
-    // Make sure that all tests on any SystemUIDialog does not crash because this dependency
-    // is missing (constructing the actual one would throw).
-    // TODO(b/219008720): Remove this.
-    dependency.injectMockDependency(SystemUIDialogManager::class.java)
-    dependency.injectTestDependency(DialogLaunchAnimator::class.java, fakeDialogLaunchAnimator())
-    return dependency
+        dependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+
+        // Make sure that all tests on any SystemUIDialog does not crash because this dependency
+        // is missing (constructing the actual one would throw).
+        // TODO(b/219008720): Remove this.
+        dependency.injectMockDependency(SystemUIDialogManager::class.java)
+        dependency.injectTestDependency(
+            DialogLaunchAnimator::class.java,
+            fakeDialogLaunchAnimator()
+        )
+
+        // Many tests end up creating a BroadcastDispatcher. Instead, give them a fake that will
+        // record receivers registered. They are not actually leaked as they are kept just as a weak
+        // reference and are never sent to the Context. This will also prevent a real
+        // BroadcastDispatcher from actually registering receivers.
+        fakeBroadcastDispatcher =
+            FakeBroadcastDispatcher(
+                context,
+                context.mainExecutor,
+                mock(Looper::class.java),
+                mock(Executor::class.java),
+                mock(DumpManager::class.java),
+                mock(BroadcastDispatcherLogger::class.java),
+                mock(UserTracker::class.java),
+                shouldFailOnLeakedReceiver
+            )
+        dependency.injectTestDependency(BroadcastDispatcher::class.java, fakeBroadcastDispatcher)
+        return dependency
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/FakeAccessibilityDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/FakeAccessibilityDataLayerModule.kt
new file mode 100644
index 0000000..baf1006
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/FakeAccessibilityDataLayerModule.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 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.systemui.accessibility.data
+
+import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeAccessibilityRepositoryModule::class])
+object FakeAccessibilityDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
index 8444c7b..4085b1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
@@ -16,8 +16,20 @@
 
 package com.android.systemui.accessibility.data.repository
 
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 
+@SysUISingleton
 class FakeAccessibilityRepository(
-    override val isTouchExplorationEnabled: MutableStateFlow<Boolean> = MutableStateFlow(false)
-) : AccessibilityRepository
+    override val isTouchExplorationEnabled: MutableStateFlow<Boolean>,
+) : AccessibilityRepository {
+    @Inject constructor() : this(MutableStateFlow(false))
+}
+
+@Module
+interface FakeAccessibilityRepositoryModule {
+    @Binds fun bindFake(fake: FakeAccessibilityRepository): AccessibilityRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
index cffbf02..36f0882 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.data
 
+import com.android.systemui.accessibility.data.FakeAccessibilityDataLayerModule
 import com.android.systemui.authentication.data.FakeAuthenticationDataLayerModule
 import com.android.systemui.bouncer.data.repository.FakeBouncerDataLayerModule
 import com.android.systemui.common.ui.data.FakeCommonDataLayerModule
@@ -30,6 +31,7 @@
 @Module(
     includes =
         [
+            FakeAccessibilityDataLayerModule::class,
             FakeAuthenticationDataLayerModule::class,
             FakeBouncerDataLayerModule::class,
             FakeCommonDataLayerModule::class,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt
index abf72af..6710072 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.keyguard.data
 
 import com.android.systemui.keyguard.data.repository.FakeCommandQueueModule
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepositoryModule
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepositoryModule
 import dagger.Module
@@ -24,6 +25,7 @@
     includes =
         [
             FakeCommandQueueModule::class,
+            FakeDeviceEntryFaceAuthRepositoryModule::class,
             FakeKeyguardRepositoryModule::class,
             FakeKeyguardTransitionRepositoryModule::class,
         ]
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 322fb28..e289083 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -17,15 +17,20 @@
 package com.android.systemui.keyguard.data.repository
 
 import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.filterNotNull
 
-class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
+@SysUISingleton
+class FakeDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceAuthRepository {
 
     override val isAuthenticated = MutableStateFlow(false)
     override val canRunFaceAuth = MutableStateFlow(false)
@@ -66,3 +71,8 @@
         _runningAuthRequest.value = null
     }
 }
+
+@Module
+interface FakeDeviceEntryFaceAuthRepositoryModule {
+    @Binds fun bindFake(fake: FakeDeviceEntryFaceAuthRepository): DeviceEntryFaceAuthRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index fae49b1..88a88c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -58,6 +58,9 @@
     private val _isKeyguardShowing = MutableStateFlow(false)
     override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
 
+    private val _isKeyguardUnlocked = MutableStateFlow(false)
+    override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow()
+
     private val _isKeyguardOccluded = MutableStateFlow(false)
     override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 82ce802..d2ff9bc5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -19,7 +19,6 @@
 
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -45,7 +44,6 @@
         sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(),
         repository: FakeKeyguardRepository = FakeKeyguardRepository(),
         commandQueue: FakeCommandQueue = FakeCommandQueue(),
-        deviceEntryRepository: FakeDeviceEntryRepository = FakeDeviceEntryRepository(),
         bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
         configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
         shadeRepository: FakeShadeRepository = FakeShadeRepository(),
@@ -57,7 +55,6 @@
             commandQueue = commandQueue,
             featureFlags = featureFlags,
             sceneContainerFlags = sceneContainerFlags,
-            deviceEntryRepository = deviceEntryRepository,
             bouncerRepository = bouncerRepository,
             configurationRepository = configurationRepository,
             shadeRepository = shadeRepository,
@@ -67,7 +64,6 @@
                 commandQueue = commandQueue,
                 featureFlags = featureFlags,
                 sceneContainerFlags = sceneContainerFlags,
-                deviceEntryRepository = deviceEntryRepository,
                 bouncerRepository = bouncerRepository,
                 configurationRepository = configurationRepository,
                 shadeRepository = shadeRepository,
@@ -87,7 +83,6 @@
         val commandQueue: FakeCommandQueue,
         val featureFlags: FakeFeatureFlags,
         val sceneContainerFlags: SceneContainerFlags,
-        val deviceEntryRepository: FakeDeviceEntryRepository,
         val bouncerRepository: FakeKeyguardBouncerRepository,
         val configurationRepository: FakeConfigurationRepository,
         val shadeRepository: FakeShadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
index f62bf60..1efa74b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles.base.interactor
 
+import android.os.UserHandle
+
 class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
 
     var handleResult: Boolean = false
@@ -23,7 +25,7 @@
         DisabledByPolicyInteractor.PolicyResult.TileEnabled
 
     override suspend fun isDisabled(
-        userId: Int,
+        user: UserHandle,
         userRestriction: String?
     ): DisabledByPolicyInteractor.PolicyResult = policyResult
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
index 5593596..2b3330f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.base.interactor
 
+import android.os.UserHandle
 import javax.annotation.CheckReturnValue
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -38,16 +39,16 @@
     fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
     suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
 
-    override fun tileData(userId: Int, triggers: Flow<DataUpdateTrigger>): Flow<T> {
-        mutableDataRequests.add(DataRequest(userId))
+    override fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<T> {
+        mutableDataRequests.add(DataRequest(user))
         return triggers.flatMapLatest { dataFlow }
     }
 
-    override fun availability(userId: Int): Flow<Boolean> {
-        mutableAvailabilityRequests.add(AvailabilityRequest(userId))
+    override fun availability(user: UserHandle): Flow<Boolean> {
+        mutableAvailabilityRequests.add(AvailabilityRequest(user))
         return availabilityFlow
     }
 
-    data class DataRequest(val userId: Int)
-    data class AvailabilityRequest(val userId: Int)
+    data class DataRequest(val user: UserHandle)
+    data class AvailabilityRequest(val user: UserHandle)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
new file mode 100644
index 0000000..de72a7d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.viewmodel
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+class FakeQSTileConfigProvider : QSTileConfigProvider {
+
+    private val configs: MutableMap<String, QSTileConfig> = mutableMapOf()
+
+    override fun getConfig(tileSpec: String): QSTileConfig = configs.getValue(tileSpec)
+
+    fun putConfig(tileSpec: TileSpec, config: QSTileConfig) {
+        configs[tileSpec.spec] = config
+    }
+
+    fun removeConfig(tileSpec: TileSpec): QSTileConfig? = configs.remove(tileSpec.spec)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
index 201926d..2a0ee88 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
@@ -16,10 +16,7 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
-import androidx.annotation.StringRes
 import com.android.internal.logging.InstanceId
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
 object QSTileConfigTestBuilder {
@@ -29,8 +26,7 @@
 
     class BuildingScope {
         var tileSpec: TileSpec = TileSpec.create("test_spec")
-        var tileIcon: Icon = Icon.Resource(0, ContentDescription.Resource(0))
-        @StringRes var tileLabel: Int = 0
+        var uiConfig: QSTileUIConfig = QSTileUIConfig.Empty
         var instanceId: InstanceId = InstanceId.fakeInstanceId(0)
         var metricsSpec: String = tileSpec.spec
         var policy: QSTilePolicy = QSTilePolicy.NoRestrictions
@@ -38,8 +34,7 @@
         fun build() =
             QSTileConfig(
                 tileSpec,
-                tileIcon,
-                tileLabel,
+                uiConfig,
                 instanceId,
                 metricsSpec,
                 policy,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index a4881bc..17384351 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -185,7 +185,6 @@
             commandQueue = FakeCommandQueue(),
             featureFlags = featureFlags,
             sceneContainerFlags = sceneContainerFlags,
-            deviceEntryRepository = FakeDeviceEntryRepository(),
             bouncerRepository = FakeKeyguardBouncerRepository(),
             configurationRepository = FakeConfigurationRepository(),
             shadeRepository = FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
new file mode 100644
index 0000000..19fdb6d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
@@ -0,0 +1,159 @@
+package com.android.systemui.statusbar
+
+import android.view.View
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+
+class FakeStatusBarStateController : SysuiStatusBarStateController {
+    @JvmField var state = StatusBarState.SHADE
+
+    @JvmField var upcomingState = StatusBarState.SHADE
+
+    @JvmField var lastState = StatusBarState.SHADE
+
+    @JvmField var dozing = false
+
+    @JvmField var expanded = false
+
+    @JvmField var pulsing = false
+
+    @JvmField var dreaming = false
+
+    @JvmField var dozeAmount = 0.0f
+
+    @JvmField var interpolatedDozeAmount = 0.0f
+
+    @JvmField var dozeAmountTarget = 0.0f
+
+    @JvmField var leaveOpen = false
+
+    @JvmField var keyguardRequested = false
+
+    var lastSetDozeAmountView: View? = null
+        private set
+
+    var lastSetDozeAmountAnimated = false
+        private set
+
+    var lastSystemBarAppearance = 0
+        private set
+
+    var lastSystemBarBehavior = 0
+        private set
+
+    var lastSystemBarRequestedVisibleTypes = 0
+        private set
+
+    var lastSystemBarPackageName: String? = null
+        private set
+
+    private val _callbacks = mutableSetOf<StatusBarStateController.StateListener>()
+
+    @JvmField val callbacks: Set<StatusBarStateController.StateListener> = _callbacks
+
+    private var fullscreen = false
+
+    override fun start() {}
+
+    override fun getState() = state
+
+    override fun setState(newState: Int, force: Boolean): Boolean {
+        val oldState = this.state
+        newState != oldState || force || return false
+
+        callbacks.forEach { it.onStatePreChange(oldState, newState) }
+        this.lastState = oldState
+        this.state = newState
+        setUpcomingState(newState)
+        callbacks.forEach { it.onStateChanged(newState) }
+        callbacks.forEach { it.onStatePostChange() }
+        return true
+    }
+
+    override fun getCurrentOrUpcomingState() = upcomingState
+
+    override fun setUpcomingState(upcomingState: Int) {
+        upcomingState != this.upcomingState || return
+        this.upcomingState = upcomingState
+        callbacks.forEach { it.onUpcomingStateChanged(upcomingState) }
+    }
+
+    override fun isDozing() = dozing
+
+    override fun setIsDozing(dozing: Boolean): Boolean {
+        dozing != this.dozing || return false
+        this.dozing = dozing
+        callbacks.forEach { it.onDozingChanged(dozing) }
+        return true
+    }
+
+    override fun isExpanded() = expanded
+
+    fun fakeShadeExpansionFullyChanged(expanded: Boolean) {
+        expanded != this.expanded || return
+        this.expanded = expanded
+        callbacks.forEach { it.onExpandedChanged(expanded) }
+    }
+
+    override fun isPulsing() = pulsing
+
+    override fun setPulsing(pulsing: Boolean) {
+        pulsing != this.pulsing || return
+        this.pulsing = pulsing
+        callbacks.forEach { it.onPulsingChanged(pulsing) }
+    }
+
+    override fun isDreaming() = dreaming
+
+    override fun setIsDreaming(drreaming: Boolean): Boolean {
+        dreaming != this.dreaming || return false
+        this.dreaming = dreaming
+        callbacks.forEach { it.onDreamingChanged(dreaming) }
+        return true
+    }
+
+    override fun getDozeAmount() = dozeAmount
+
+    override fun setAndInstrumentDozeAmount(view: View?, dozeAmount: Float, animated: Boolean) {
+        dozeAmountTarget = dozeAmount
+        lastSetDozeAmountView = view
+        lastSetDozeAmountAnimated = animated
+        if (!animated) {
+            this.dozeAmount = dozeAmount
+        }
+    }
+
+    override fun leaveOpenOnKeyguardHide() = leaveOpen
+
+    override fun setLeaveOpenOnKeyguardHide(leaveOpen: Boolean) {
+        this.leaveOpen = leaveOpen
+    }
+
+    override fun getInterpolatedDozeAmount() = interpolatedDozeAmount
+
+    fun fakeInterpolatedDozeAmountChanged(interpolatedDozeAmount: Float) {
+        this.interpolatedDozeAmount = interpolatedDozeAmount
+        callbacks.forEach { it.onDozeAmountChanged(dozeAmount, interpolatedDozeAmount) }
+    }
+
+    override fun goingToFullShade() = state == StatusBarState.SHADE && leaveOpen
+
+    override fun fromShadeLocked() = lastState == StatusBarState.SHADE_LOCKED
+
+    override fun isKeyguardRequested(): Boolean = keyguardRequested
+
+    override fun setKeyguardRequested(keyguardRequested: Boolean) {
+        this.keyguardRequested = keyguardRequested
+    }
+
+    override fun addCallback(listener: StatusBarStateController.StateListener?) {
+        _callbacks.add(listener!!)
+    }
+
+    override fun addCallback(listener: StatusBarStateController.StateListener?, rank: Int) {
+        throw RuntimeException("addCallback with rank unsupported")
+    }
+
+    override fun removeCallback(listener: StatusBarStateController.StateListener?) {
+        _callbacks.remove(listener!!)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index eaa109d..209cac6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -25,6 +25,7 @@
 
 public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
         implements BatteryController {
+    private boolean mIsAodPowerSave = false;
     private boolean mWirelessCharging;
 
     public FakeBatteryController(LeakCheck test) {
@@ -63,7 +64,7 @@
 
     @Override
     public boolean isAodPowerSave() {
-        return false;
+        return mIsAodPowerSave;
     }
 
     @Override
@@ -71,6 +72,10 @@
         return mWirelessCharging;
     }
 
+    public void setIsAodPowerSave(boolean isAodPowerSave) {
+        mIsAodPowerSave = isAodPowerSave;
+    }
+
     public void setWirelessCharging(boolean wirelessCharging) {
         mWirelessCharging = wirelessCharging;
     }
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 1866a99..67c2caa 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -16,9 +16,12 @@
 
 package android.renderscript;
 
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.res.Resources;
+import android.util.Slog;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -35,6 +38,15 @@
     private static final String TAG = "ScriptC";
 
     /**
+     * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported
+     * and an exception is thrown when the class is instantiated.
+     * In targetSdkVersion 34 and below, Renderscript's ScriptC still works.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = 35)
+    private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L;
+
+    /**
      * Only intended for use by the generated derived classes.
      *
      * @param id
@@ -89,7 +101,19 @@
         setID(id);
     }
 
+    private static void throwExceptionIfSDKTooHigh() {
+        String message =
+                "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer "
+                    + "to https://developer.android.com/guide/topics/renderscript/migration-guide "
+                    + "for proposed alternatives.";
+        Slog.w(TAG, message);
+        if (CompatChanges.isChangeEnabled(RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID)) {
+            throw new UnsupportedOperationException(message);
+        }
+    }
+
     private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
+        throwExceptionIfSDKTooHigh();
         byte[] pgm;
         int pgmLength;
         InputStream is = resources.openRawResource(resourceID);
@@ -126,6 +150,7 @@
 
     private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
         //        Log.v(TAG, "Create script for resource = " + resName);
+        throwExceptionIfSDKTooHigh();
         return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length);
     }
 }
diff --git a/services/Android.bp b/services/Android.bp
index 3ae9360..aca8409 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -238,12 +238,14 @@
 stubs_defaults {
     name: "services-stubs-default",
     installable: false,
-    args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
-        " --hide-annotation android.annotation.Hide" +
-        " --hide InternalClasses" + // com.android.* classes are okay in this interface
+    flags: [
+        "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)",
+        "--hide-annotation android.annotation.Hide",
+        "--hide InternalClasses", // com.android.* classes are okay in this interface
         // TODO: remove the --hide options below
-        " --hide DeprecationMismatch" +
-        " --hide HiddenTypedefConstant",
+        "--hide DeprecationMismatch",
+        "--hide HiddenTypedefConstant",
+    ],
     visibility: ["//frameworks/base:__subpackages__"],
     filter_packages: ["com.android."],
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 5af80da..2913716 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -145,6 +145,13 @@
     /** Flag for intercepting generic motion events. */
     static final int FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS = 0x00000800;
 
+    /**
+     * Flag for enabling the two-finger triple-tap magnification feature.
+     *
+     * @see #setUserAndEnabledFeatures(int, int)
+     */
+    static final int FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP = 0x00001000;
+
     static final int FEATURES_AFFECTING_MOTION_EVENTS =
             FLAG_FEATURE_INJECT_MOTION_EVENTS
                     | FLAG_FEATURE_AUTOCLICK
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 87f9cf1..a7b5325 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -64,7 +64,6 @@
 import android.app.RemoteAction;
 import android.app.admin.DevicePolicyManager;
 import android.appwidget.AppWidgetManagerInternal;
-import android.companion.virtual.VirtualDeviceManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -1071,18 +1070,7 @@
         mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler,
                 Context.RECEIVER_EXPORTED);
 
-        if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
-            VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
-            if (vdm != null) {
-                vdm.registerVirtualDeviceListener(mContext.getMainExecutor(),
-                        new VirtualDeviceManager.VirtualDeviceListener() {
-                            @Override
-                            public void onVirtualDeviceClosed(int deviceId) {
-                                mProxyManager.clearConnections(deviceId);
-                            }
-                        });
-            }
-        } else {
+        if (!android.companion.virtual.flags.Flags.vdmPublicApis()) {
             final BroadcastReceiver virtualDeviceReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
@@ -2798,6 +2786,12 @@
                 flags |= AccessibilityInputFilter
                         .FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP;
             }
+            if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+                if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()) {
+                    flags |= AccessibilityInputFilter
+                            .FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP;
+                }
+            }
             if (userState.isShortcutMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
             }
@@ -3162,6 +3156,21 @@
         return false;
     }
 
+    private boolean readMagnificationTwoFingerTripleTapSettingsLocked(
+            AccessibilityUserState userState) {
+        final boolean magnificationTwoFingerTripleTapEnabled = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+                0, userState.mUserId) == 1;
+        if ((magnificationTwoFingerTripleTapEnabled
+                != userState.isMagnificationTwoFingerTripleTapEnabledLocked())) {
+            userState.setMagnificationTwoFingerTripleTapEnabledLocked(
+                    magnificationTwoFingerTripleTapEnabled);
+            return true;
+        }
+        return false;
+    }
+
     private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean autoclickEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
@@ -3397,6 +3406,7 @@
         // displays in one display. It's not a real display and there's no input events for it.
         final ArrayList<Display> displays = getValidDisplayList();
         if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()
+                || userState.isMagnificationTwoFingerTripleTapEnabledLocked()
                 || userState.isShortcutMagnificationEnabledLocked()) {
             for (int i = 0; i < displays.size(); i++) {
                 final Display display = displays.get(i);
@@ -4932,6 +4942,9 @@
         private final Uri mMagnificationmSingleFingerTripleTapEnabledUri = Settings.Secure
                 .getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
 
+        private final Uri mMagnificationTwoFingerTripleTapEnabledUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
+
         private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
 
@@ -4989,6 +5002,10 @@
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mMagnificationmSingleFingerTripleTapEnabledUri,
                     false, this, UserHandle.USER_ALL);
+            if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+                contentResolver.registerContentObserver(mMagnificationTwoFingerTripleTapEnabledUri,
+                        false, this, UserHandle.USER_ALL);
+            }
             contentResolver.registerContentObserver(mAutoclickEnabledUri,
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
@@ -5039,6 +5056,11 @@
                     if (readMagnificationEnabledSettingsLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
+                } else if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+                        && mMagnificationTwoFingerTripleTapEnabledUri.equals(uri)) {
+                    if (readMagnificationTwoFingerTripleTapSettingsLocked(userState)) {
+                        onUserStateChangedLocked(userState);
+                    }
                 } else if (mAutoclickEnabledUri.equals(uri)) {
                     if (readAutoclickEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index b4efec1..68ee780 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -110,6 +110,7 @@
     private boolean mIsAudioDescriptionByDefaultRequested;
     private boolean mIsAutoclickEnabled;
     private boolean mIsMagnificationSingleFingerTripleTapEnabled;
+    private boolean mMagnificationTwoFingerTripleTapEnabled;
     private boolean mIsFilterKeyEventsEnabled;
     private boolean mIsPerformGesturesEnabled;
     private boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -212,6 +213,7 @@
         mRequestTwoFingerPassthrough = false;
         mSendMotionEventsEnabled = false;
         mIsMagnificationSingleFingerTripleTapEnabled = false;
+        mMagnificationTwoFingerTripleTapEnabled = false;
         mIsAutoclickEnabled = false;
         mUserNonInteractiveUiTimeout = 0;
         mUserInteractiveUiTimeout = 0;
@@ -633,6 +635,14 @@
         mIsMagnificationSingleFingerTripleTapEnabled = enabled;
     }
 
+    public boolean isMagnificationTwoFingerTripleTapEnabledLocked() {
+        return mMagnificationTwoFingerTripleTapEnabled;
+    }
+
+    public void setMagnificationTwoFingerTripleTapEnabledLocked(boolean enabled) {
+        mMagnificationTwoFingerTripleTapEnabled = enabled;
+    }
+
     public boolean isFilterKeyEventsEnabledLocked() {
         return mIsFilterKeyEventsEnabled;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index ed77476..2032a50 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -101,6 +101,8 @@
     private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
             mAppsOnVirtualDeviceListener;
 
+    private VirtualDeviceManager.VirtualDeviceListener mVirtualDeviceListener;
+
     /**
      * Callbacks into AccessibilityManagerService.
      */
@@ -189,6 +191,9 @@
                     }
                 }
             }
+            if (mProxyA11yServiceConnections.size() == 1) {
+                registerVirtualDeviceListener();
+            }
         }
 
         // If the client dies, make sure to remove the connection.
@@ -210,6 +215,31 @@
         connection.initializeServiceInterface(client);
     }
 
+    private void registerVirtualDeviceListener() {
+        VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
+        if (vdm == null || !android.companion.virtual.flags.Flags.vdmPublicApis()) {
+            return;
+        }
+        if (mVirtualDeviceListener == null) {
+            mVirtualDeviceListener = new VirtualDeviceManager.VirtualDeviceListener() {
+                @Override
+                public void onVirtualDeviceClosed(int deviceId) {
+                    clearConnections(deviceId);
+                }
+            };
+        }
+
+        vdm.registerVirtualDeviceListener(mContext.getMainExecutor(), mVirtualDeviceListener);
+    }
+
+    private void unregisterVirtualDeviceListener() {
+        VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
+        if (vdm == null || !android.companion.virtual.flags.Flags.vdmPublicApis()) {
+            return;
+        }
+        vdm.unregisterVirtualDeviceListener(mVirtualDeviceListener);
+    }
+
     /**
      * Unregister the proxy based on display id.
      */
@@ -258,6 +288,9 @@
                 deviceId = mProxyA11yServiceConnections.get(displayId).getDeviceId();
                 mProxyA11yServiceConnections.remove(displayId);
                 removedFromConnections = true;
+                if (mProxyA11yServiceConnections.size() == 0) {
+                    unregisterVirtualDeviceListener();
+                }
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 423b85f..4688658 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -56,7 +56,7 @@
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
-    private FillServiceCallbacks mCallbacks;
+    private final FillServiceCallbacks mCallbacks;
     private final Object mLock = new Object();
     private CompletableFuture<FillResponse> mPendingFillRequest;
     private int mPendingFillRequestId = INVALID_REQUEST_ID;
@@ -128,12 +128,9 @@
      */
     public int cancelCurrentRequest() {
         synchronized (mLock) {
-            int canceledRequestId = mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+            return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
                     ? mPendingFillRequestId
                     : INVALID_REQUEST_ID;
-            mPendingFillRequest = null;
-            mPendingFillRequestId = INVALID_REQUEST_ID;
-            return canceledRequestId;
         }
     }
 
@@ -187,10 +184,6 @@
                 mPendingFillRequest = null;
                 mPendingFillRequestId = INVALID_REQUEST_ID;
             }
-            if (mCallbacks == null) {
-                Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
-                return;
-            }
             if (err == null) {
                 mCallbacks.onFillRequestSuccess(request.getId(), res,
                         mComponentName.getPackageName(), request.getFlags());
@@ -227,10 +220,6 @@
             return save;
         }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS)
                 .whenComplete((res, err) -> Handler.getMain().post(() -> {
-                    if (mCallbacks == null) {
-                        Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
-                        return;
-                    }
                     if (err == null) {
                         mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), res);
                     } else {
@@ -245,8 +234,6 @@
     }
 
     public void destroy() {
-        cancelCurrentRequest();
         unbind();
-        mCallbacks = null;
     }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4dca5a5..3985a0e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -155,6 +155,7 @@
         "service-permission.stubs.system_server",
         "service-rkp.stubs.system_server",
         "service-sdksandbox.stubs.system_server",
+        "device_policy_aconfig_flags_lib",
     ],
     plugins: ["ImmutabilityAnnotationProcessor"],
 
@@ -197,6 +198,7 @@
         "android.hardware.power.stats-V2-java",
         "android.hidl.manager-V1.2-java",
         "cbor-java",
+        "dropbox_flags_lib",
         "icu4j_calendar_astronomer",
         "android.security.aaid_aidl-java",
         "netd-client",
@@ -205,6 +207,7 @@
         "com.android.sysprop.watchdog",
         "ImmutabilityAnnotation",
         "securebox",
+        "android.content.pm.flags-aconfig-java",
         "apache-commons-math",
         "backstage_power_flags_lib",
         "notification_flags_lib",
diff --git a/services/core/java/com/android/server/BrickReceiver.java b/services/core/java/com/android/server/BrickReceiver.java
deleted file mode 100644
index cff3805..0000000
--- a/services/core/java/com/android/server/BrickReceiver.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
-import android.os.SystemService;
-import android.util.Slog;
-
-public class BrickReceiver extends BroadcastReceiver {
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Slog.w("BrickReceiver", "!!! BRICKING DEVICE !!!");
-        SystemService.start("brick");
-    }
-}
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index 55069b7..f82a6aa 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -16,10 +16,14 @@
 
 package com.android.server;
 
+import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -30,6 +34,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.BundleMerger;
 import android.os.Debug;
@@ -66,6 +71,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ObjectUtils;
 import com.android.server.DropBoxManagerInternal.EntrySource;
+import com.android.server.feature.flags.Flags;
 
 import libcore.io.IoUtils;
 
@@ -89,6 +95,13 @@
  * Clients use {@link DropBoxManager} to access this service.
  */
 public final class DropBoxManagerService extends SystemService {
+    /**
+     * For Android U and earlier versions, apps can continue to use the READ_LOGS permission,
+     * but for all subsequent versions, the READ_DROPBOX_DATA permission must be used.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    private static final long ENFORCE_READ_DROPBOX_DATA = 296060945L;
     private static final String TAG = "DropBoxManagerService";
     private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
     private static final int DEFAULT_MAX_FILES = 1000;
@@ -109,7 +122,6 @@
     // Tags that we should drop by default.
     private static final List<String> DISABLED_BY_DEFAULT_TAGS =
             List.of("data_app_wtf", "system_app_wtf", "system_server_wtf");
-
     // TODO: This implementation currently uses one file per entry, which is
     // inefficient for smallish entries -- consider using a single queue file
     // per tag (or even globally) instead.
@@ -291,8 +303,21 @@
             if (!DropBoxManagerService.this.mBooted) {
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             }
-            getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
-                    android.Manifest.permission.READ_LOGS, options);
+            if (Flags.enableReadDropboxPermission()) {
+                BroadcastOptions unbundledOptions = (options == null)
+                        ? BroadcastOptions.makeBasic() : BroadcastOptions.fromBundle(options);
+
+                unbundledOptions.setRequireCompatChange(ENFORCE_READ_DROPBOX_DATA, true);
+                getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                        Manifest.permission.READ_DROPBOX_DATA, unbundledOptions.toBundle());
+
+                unbundledOptions.setRequireCompatChange(ENFORCE_READ_DROPBOX_DATA, false);
+                getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                        Manifest.permission.READ_LOGS, unbundledOptions.toBundle());
+            } else {
+                getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                        android.Manifest.permission.READ_LOGS, options);
+            }
         }
 
         private Intent createIntent(String tag, long time) {
@@ -572,9 +597,16 @@
             return true;
         }
 
+
+        String permission = Manifest.permission.READ_LOGS;
+        if (Flags.enableReadDropboxPermission()
+                && CompatChanges.isChangeEnabled(ENFORCE_READ_DROPBOX_DATA, callingUid)) {
+            permission = Manifest.permission.READ_DROPBOX_DATA;
+        }
+
         // Callers always need this permission
-        getContext().enforceCallingOrSelfPermission(
-                android.Manifest.permission.READ_LOGS, TAG);
+        getContext().enforceCallingOrSelfPermission(permission, TAG);
+
 
         // Callers also need the ability to read usage statistics
         switch (getContext().getSystemService(AppOpsManager.class).noteOp(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3af0e8c..15fc2dc 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3007,7 +3007,7 @@
             // We need all the users unlocked to move their primary storage
             users = mContext.getSystemService(UserManager.class).getUsers();
             for (UserInfo user : users) {
-                if (StorageManager.isFileEncrypted() && !isUserKeyUnlocked(user.id)) {
+                if (StorageManager.isFileEncrypted() && !isCeStorageUnlocked(user.id)) {
                     Slog.w(TAG, "Failing move due to locked user " + user.id);
                     onMoveStatusLocked(PackageManager.MOVE_FAILED_LOCKED_USER);
                     return;
@@ -3231,12 +3231,12 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
+    public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) {
 
-        super.createUserKey_enforcePermission();
+        super.createUserStorageKeys_enforcePermission();
 
         try {
-            mVold.createUserKey(userId, serialNumber, ephemeral);
+            mVold.createUserStorageKeys(userId, serialNumber, ephemeral);
             // Since the user's CE key was just created, the user's CE storage is now unlocked.
             synchronized (mLock) {
                 mCeUnlockedUsers.append(userId);
@@ -3248,12 +3248,12 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void destroyUserKey(int userId) {
+    public void destroyUserStorageKeys(int userId) {
 
-        super.destroyUserKey_enforcePermission();
+        super.destroyUserStorageKeys_enforcePermission();
 
         try {
-            mVold.destroyUserKey(userId);
+            mVold.destroyUserStorageKeys(userId);
             // Since the user's CE key was just destroyed, the user's CE storage is now locked.
             synchronized (mLock) {
                 mCeUnlockedUsers.remove(userId);
@@ -3266,21 +3266,22 @@
     /* Only for use by LockSettingsService */
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void setUserKeyProtection(@UserIdInt int userId, byte[] secret) throws RemoteException {
-        super.setUserKeyProtection_enforcePermission();
+    public void setCeStorageProtection(@UserIdInt int userId, byte[] secret)
+            throws RemoteException {
+        super.setCeStorageProtection_enforcePermission();
 
-        mVold.setUserKeyProtection(userId, HexDump.toHexString(secret));
+        mVold.setCeStorageProtection(userId, HexDump.toHexString(secret));
     }
 
     /* Only for use by LockSettingsService */
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void unlockUserKey(@UserIdInt int userId, int serialNumber, byte[] secret)
-        throws RemoteException {
-        super.unlockUserKey_enforcePermission();
+    public void unlockCeStorage(@UserIdInt int userId, int serialNumber, byte[] secret)
+            throws RemoteException {
+        super.unlockCeStorage_enforcePermission();
 
         if (StorageManager.isFileEncrypted()) {
-            mVold.unlockUserKey(userId, serialNumber, HexDump.toHexString(secret));
+            mVold.unlockCeStorage(userId, serialNumber, HexDump.toHexString(secret));
         }
         synchronized (mLock) {
             mCeUnlockedUsers.append(userId);
@@ -3289,23 +3290,22 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void lockUserKey(int userId) {
-        //  Do not lock user 0 data for headless system user
-        super.lockUserKey_enforcePermission();
+    public void lockCeStorage(int userId) {
+        super.lockCeStorage_enforcePermission();
 
+        // Never lock the CE storage of a headless system user.
         if (userId == UserHandle.USER_SYSTEM
                 && UserManager.isHeadlessSystemUserMode()) {
             throw new IllegalArgumentException("Headless system user data cannot be locked..");
         }
 
-
-        if (!isUserKeyUnlocked(userId)) {
+        if (!isCeStorageUnlocked(userId)) {
             Slog.d(TAG, "User " + userId + "'s CE storage is already locked");
             return;
         }
 
         try {
-            mVold.lockUserKey(userId);
+            mVold.lockCeStorage(userId);
         } catch (Exception e) {
             Slog.wtf(TAG, e);
             return;
@@ -3317,7 +3317,7 @@
     }
 
     @Override
-    public boolean isUserKeyUnlocked(int userId) {
+    public boolean isCeStorageUnlocked(int userId) {
         synchronized (mLock) {
             return mCeUnlockedUsers.contains(userId);
         }
@@ -3719,8 +3719,8 @@
         final int userId = UserHandle.getUserId(callingUid);
         final String propertyName = "sys.user." + userId + ".ce_available";
 
-        // Ignore requests to create directories while storage is locked
-        if (!isUserKeyUnlocked(userId)) {
+        // Ignore requests to create directories while CE storage is locked
+        if (!isCeStorageUnlocked(userId)) {
             throw new IllegalStateException("Failed to prepare " + appPath);
         }
 
@@ -3846,15 +3846,15 @@
         final boolean systemUserUnlocked = isSystemUnlocked(UserHandle.USER_SYSTEM);
 
         final boolean userIsDemo;
-        final boolean userKeyUnlocked;
         final boolean storagePermission;
+        final boolean ceStorageUnlocked;
         final long token = Binder.clearCallingIdentity();
         try {
             userIsDemo = LocalServices.getService(UserManagerInternal.class)
                     .getUserInfo(userId).isDemo();
             storagePermission = mStorageManagerInternal.hasExternalStorage(callingUid,
                     callingPackage);
-            userKeyUnlocked = isUserKeyUnlocked(userId);
+            ceStorageUnlocked = isCeStorageUnlocked(userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -3914,7 +3914,7 @@
                 } else if (!systemUserUnlocked) {
                     reportUnmounted = true;
                     Slog.w(TAG, "Reporting " + volId + " unmounted due to system locked");
-                } else if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
+                } else if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !ceStorageUnlocked) {
                     reportUnmounted = true;
                     Slog.w(TAG, "Reporting " + volId + "unmounted due to " + userId + " locked");
                 } else if (!storagePermission && !realState) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ef67cbe..e88d0c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2299,7 +2299,7 @@
             return;
         }
         // TODO(b/148767783): should we check all profiles under user0?
-        UserspaceRebootLogger.logEventAsync(StorageManager.isUserKeyUnlocked(userId),
+        UserspaceRebootLogger.logEventAsync(StorageManager.isCeStorageUnlocked(userId),
                 BackgroundThread.getExecutor());
     }
 
@@ -4648,7 +4648,7 @@
             // We carefully use the same state that PackageManager uses for
             // filtering, since we use this flag to decide if we need to install
             // providers when user is unlocked later
-            app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId));
+            app.setUnlocked(StorageManager.isCeStorageUnlocked(app.userId));
         }
 
         boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
@@ -9718,9 +9718,29 @@
     public ParceledListSlice<ApplicationStartInfo> getHistoricalProcessStartReasons(
             String packageName, int maxNum, int userId) {
         enforceNotIsolatedCaller("getHistoricalProcessStartReasons");
+        // For the simplification, we don't support USER_ALL nor USER_CURRENT here.
+        if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_CURRENT) {
+            throw new IllegalArgumentException("Unsupported userId");
+        }
+
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL,
+                "getHistoricalProcessStartReasons", null);
 
         final ArrayList<ApplicationStartInfo> results = new ArrayList<ApplicationStartInfo>();
-
+        if (!TextUtils.isEmpty(packageName)) {
+            final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid,
+                        "getHistoricalProcessStartReasons");
+            if (uid != INVALID_UID) {
+                mProcessList.mAppStartInfoTracker.getStartInfo(
+                        packageName, userId, callingPid, maxNum, results);
+            }
+        } else {
+            // If no package name is given, use the caller's uid as the filter uid.
+            mProcessList.mAppStartInfoTracker.getStartInfo(
+                    packageName, callingUid, callingPid, maxNum, results);
+        }
         return new ParceledListSlice<ApplicationStartInfo>(results);
     }
 
@@ -9729,6 +9749,14 @@
     public void setApplicationStartInfoCompleteListener(
             IApplicationStartInfoCompleteListener listener, int userId) {
         enforceNotIsolatedCaller("setApplicationStartInfoCompleteListener");
+
+        // For the simplification, we don't support USER_ALL nor USER_CURRENT here.
+        if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_CURRENT) {
+            throw new IllegalArgumentException("Unsupported userId");
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        mProcessList.mAppStartInfoTracker.addStartInfoCompleteListener(listener, callingUid);
     }
 
 
@@ -9742,6 +9770,7 @@
         }
 
         final int callingUid = Binder.getCallingUid();
+        mProcessList.mAppStartInfoTracker.clearStartInfoCompleteListener(callingUid, true);
     }
 
     @Override
@@ -10043,6 +10072,8 @@
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
+                mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage);
+                pw.println("-------------------------------------------------------------------------------");
                 mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage);
             }
             if (dumpPackage == null) {
@@ -10439,6 +10470,12 @@
                 LockGuard.dump(fd, pw, args);
             } else if ("users".equals(cmd)) {
                 dumpUsers(pw);
+            } else if ("start-info".equals(cmd)) {
+                if (opti < args.length) {
+                    dumpPackage = args[opti];
+                    opti++;
+                }
+                mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage);
             } else if ("exit-info".equals(cmd)) {
                 if (opti < args.length) {
                     dumpPackage = args[opti];
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a057f32..69bf612 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -272,6 +272,8 @@
                     return runSetWatchHeap(pw);
                 case "clear-watch-heap":
                     return runClearWatchHeap(pw);
+                case "clear-start-info":
+                    return runClearStartInfo(pw);
                 case "clear-exit-info":
                     return runClearExitInfo(pw);
                 case "bug-report":
@@ -1339,6 +1341,31 @@
         return 0;
     }
 
+    int runClearStartInfo(PrintWriter pw) throws RemoteException {
+        mInternal.enforceCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
+                "runClearStartInfo()");
+        String opt;
+        int userId = UserHandle.USER_CURRENT;
+        String packageName = null;
+        while ((opt = getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                packageName = opt;
+            }
+        }
+        if (userId == UserHandle.USER_CURRENT) {
+            UserInfo user = mInterface.getCurrentUser();
+            if (user == null) {
+                return -1;
+            }
+            userId = user.id;
+        }
+        mInternal.mProcessList.mAppStartInfoTracker
+                .clearHistoryProcessStartInfo(packageName, userId);
+        return 0;
+    }
+
     int runClearExitInfo(PrintWriter pw) throws RemoteException {
         mInternal.enforceCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
                 "runClearExitInfo()");
@@ -4090,6 +4117,7 @@
             pw.println("    s[ervices] [COMP_SPEC ...]: service state");
             pw.println("    allowed-associations: current package association restrictions");
             pw.println("    as[sociations]: tracked app associations");
+            pw.println("    start-info [PACKAGE_NAME]: historical process start information");
             pw.println("    exit-info [PACKAGE_NAME]: historical process exit information");
             pw.println("    lmk: stats on low memory killer");
             pw.println("    lru: raw LRU process list");
@@ -4265,6 +4293,8 @@
             pw.println("      above <HEAP-LIMIT> then a heap dump is collected for the user to report.");
             pw.println("  clear-watch-heap");
             pw.println("      Clear the previously set-watch-heap.");
+            pw.println("  clear-start-info [--user <USER_ID> | all | current] [package]");
+            pw.println("      Clear the process start-info for given package");
             pw.println("  clear-exit-info [--user <USER_ID> | all | current] [package]");
             pw.println("      Clear the process exit-info for given package");
             pw.println("  bug-report [--progress | --telephony]");
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
new file mode 100644
index 0000000..edca74f
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ApplicationStartInfo.START_TIMESTAMP_LAUNCH;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.ActivityOptions;
+import android.app.ApplicationStartInfo;
+import android.app.Flags;
+import android.app.IApplicationStartInfoCompleteListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.icu.text.SimpleDateFormat;
+import android.os.Binder;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ProcessMap;
+import com.android.server.IoThread;
+import com.android.server.ServiceThread;
+import com.android.server.SystemServiceManager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiFunction;
+
+/** A class to manage all the {@link android.app.ApplicationStartInfo} records. */
+public final class AppStartInfoTracker {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppStartInfoTracker" : TAG_AM;
+
+    /** Interval of persisting the app start info to persistent storage. */
+    private static final long APP_START_INFO_PERSIST_INTERVAL = TimeUnit.MINUTES.toMillis(30);
+
+    /** These are actions that the forEach* should take after each iteration */
+    private static final int FOREACH_ACTION_NONE = 0;
+    private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
+    private static final int FOREACH_ACTION_STOP_ITERATION = 2;
+
+    private static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
+
+    @VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore";
+
+    @VisibleForTesting static final String APP_START_INFO_FILE = "procstartinfo";
+
+    private final Object mLock = new Object();
+
+    private boolean mEnabled = false;
+
+    /** Initialized in {@link #init} and read-only after that. */
+    private ActivityManagerService mService;
+
+    /** Initialized in {@link #init} and read-only after that. */
+    private Handler mHandler;
+
+    /** The task to persist app process start info */
+    @GuardedBy("mLock")
+    private Runnable mAppStartInfoPersistTask = null;
+
+    /**
+     * Last time(in ms) since epoch that the app start info was persisted into persistent storage.
+     */
+    @GuardedBy("mLock")
+    private long mLastAppStartInfoPersistTimestamp = 0L;
+
+    /**
+     * Retention policy: keep up to X historical start info per package.
+     *
+     * <p>Initialized in {@link #init} and read-only after that. No lock is needed.
+     */
+    private int mAppStartInfoHistoryListSize;
+
+    @GuardedBy("mLock")
+    private final ProcessMap<AppStartInfoContainer> mData;
+
+    /** UID as key. */
+    @GuardedBy("mLock")
+    private final SparseArray<ApplicationStartInfoCompleteCallback> mCallbacks;
+
+    /**
+     * Whether or not we've loaded the historical app process start info from persistent storage.
+     */
+    @VisibleForTesting AtomicBoolean mAppStartInfoLoaded = new AtomicBoolean();
+
+    /** Temporary list being used to filter/sort intermediate results in {@link #getStartInfo}. */
+    @GuardedBy("mLock")
+    final ArrayList<ApplicationStartInfo> mTmpStartInfoList = new ArrayList<>();
+
+    /**
+     * The path to the directory which includes the historical proc start info file as specified in
+     * {@link #mProcStartInfoFile}.
+     */
+    @VisibleForTesting File mProcStartStoreDir;
+
+    /** The path to the historical proc start info file, persisted in the storage. */
+    @VisibleForTesting File mProcStartInfoFile;
+
+    AppStartInfoTracker() {
+        mCallbacks = new SparseArray<>();
+        mData = new ProcessMap<AppStartInfoContainer>();
+    }
+
+    void init(ActivityManagerService service) {
+        mService = service;
+
+        ServiceThread thread =
+                new ServiceThread(TAG + ":handler", THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+        thread.start();
+        mHandler = new Handler(thread.getLooper());
+
+        mProcStartStoreDir = new File(SystemServiceManager.ensureSystemDir(), APP_START_STORE_DIR);
+        if (!FileUtils.createDir(mProcStartStoreDir)) {
+            Slog.e(TAG, "Unable to create " + mProcStartStoreDir);
+            return;
+        }
+        mProcStartInfoFile = new File(mProcStartStoreDir, APP_START_INFO_FILE);
+
+        mAppStartInfoHistoryListSize = APP_START_INFO_HISTORY_LIST_SIZE;
+    }
+
+    void onSystemReady() {
+        mEnabled = Flags.appStartInfo();
+        if (!mEnabled) {
+            return;
+        }
+
+        registerForUserRemoval();
+        registerForPackageRemoval();
+        IoThread.getHandler().post(() -> {
+            loadExistingProcessStartInfo();
+        });
+    }
+
+    void handleProcessColdStarted(long startTimeNs, HostingRecord hostingRecord,
+            ProcessRecord app) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            addBaseFieldsFromProcessRecord(start, app);
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_FORK, app.getStartElapsedTime());
+            start.setStartType(ApplicationStartInfo.START_TYPE_COLD);
+            start.setReason(ApplicationStartInfo.START_REASON_OTHER);
+            addStartInfoLocked(start);
+        }
+    }
+
+    public void handleProcessActivityWarmOrHotStarted(long startTimeNs,
+            ActivityOptions activityOptions, Intent intent) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            start.setIntent(intent);
+            start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER);
+            if (activityOptions != null) {
+                start.setProcessName(activityOptions.getPackageName());
+            }
+            start.setStartType(ApplicationStartInfo.START_TYPE_WARM);
+            if (intent != null && intent.getCategories() != null
+                    && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
+                start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER);
+            } else {
+                start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY);
+            }
+            addStartInfoLocked(start);
+        }
+    }
+
+    public void handleProcessActivityStartedFromRecents(long startTimeNs,
+            ActivityOptions activityOptions) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            if (activityOptions != null) {
+                start.setIntent(activityOptions.getResultData());
+                start.setProcessName(activityOptions.getPackageName());
+            }
+            start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER_RECENTS);
+            start.setStartType(ApplicationStartInfo.START_TYPE_WARM);
+            addStartInfoLocked(start);
+        }
+    }
+
+    public void handleProcessServiceStart(long startTimeNs, ProcessRecord app,
+                ServiceRecord serviceRecord, HostingRecord hostingRecord, boolean cold) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            addBaseFieldsFromProcessRecord(start, app);
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            start.setStartType(cold ? ApplicationStartInfo.START_TYPE_COLD
+                    : ApplicationStartInfo.START_TYPE_WARM);
+            start.setReason(serviceRecord.permission != null
+                    && serviceRecord.permission.contains("android.permission.BIND_JOB_SERVICE")
+                    ? ApplicationStartInfo.START_REASON_JOB
+                    : ApplicationStartInfo.START_REASON_SERVICE);
+            start.setIntent(serviceRecord.intent.getIntent());
+            addStartInfoLocked(start);
+        }
+    }
+
+    public void handleProcessBroadcastStart(long startTimeNs, ProcessRecord app,
+                BroadcastRecord broadcast, boolean cold) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            addBaseFieldsFromProcessRecord(start, app);
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            start.setStartType(cold ? ApplicationStartInfo.START_TYPE_COLD
+                    : ApplicationStartInfo.START_TYPE_WARM);
+            if (broadcast == null) {
+                start.setReason(ApplicationStartInfo.START_REASON_BROADCAST);
+            } else if (broadcast.alarm) {
+                start.setReason(ApplicationStartInfo.START_REASON_ALARM);
+            } else if (broadcast.pushMessage || broadcast.pushMessageOverQuota) {
+                start.setReason(ApplicationStartInfo.START_REASON_PUSH);
+            } else if (Intent.ACTION_BOOT_COMPLETED.equals(broadcast.intent.getAction())) {
+                start.setReason(ApplicationStartInfo.START_REASON_BOOT_COMPLETE);
+            } else {
+                start.setReason(ApplicationStartInfo.START_REASON_BROADCAST);
+            }
+            start.setIntent(broadcast != null ? broadcast.intent : null);
+            addStartInfoLocked(start);
+        }
+    }
+
+    public void handleProcessContentProviderStart(long startTimeNs, ProcessRecord app,
+                boolean cold) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            addBaseFieldsFromProcessRecord(start, app);
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            start.setStartType(cold ? ApplicationStartInfo.START_TYPE_COLD
+                    : ApplicationStartInfo.START_TYPE_WARM);
+            start.setReason(ApplicationStartInfo.START_REASON_CONTENT_PROVIDER);
+            addStartInfoLocked(start);
+        }
+    }
+
+    public void handleProcessBackupStart(long startTimeNs, ProcessRecord app,
+                BackupRecord backupRecord, boolean cold) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            ApplicationStartInfo start = new ApplicationStartInfo();
+            addBaseFieldsFromProcessRecord(start, app);
+            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
+            start.addStartupTimestamp(
+                ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
+            start.setStartType(cold ? ApplicationStartInfo.START_TYPE_COLD
+                    : ApplicationStartInfo.START_TYPE_WARM);
+            start.setReason(ApplicationStartInfo.START_REASON_BACKUP);
+            addStartInfoLocked(start);
+        }
+    }
+
+    private void addBaseFieldsFromProcessRecord(ApplicationStartInfo start, ProcessRecord app) {
+        if (app == null) {
+            return;
+        }
+        final int definingUid = app.getHostingRecord() != null
+                ? app.getHostingRecord().getDefiningUid() : 0;
+        start.setPid(app.getPid());
+        start.setRealUid(app.uid);
+        start.setPackageUid(app.info.uid);
+        start.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
+        start.setProcessName(app.processName);
+    }
+
+    void reportApplicationOnCreateTimeNanos(ProcessRecord app, long timeNs) {
+        if (!mEnabled) {
+            return;
+        }
+        addTimestampToStart(app, timeNs,
+                ApplicationStartInfo.START_TIMESTAMP_APPLICATION_ONCREATE);
+    }
+
+    void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) {
+        addTimestampToStart(app, timeNs,
+                ApplicationStartInfo.START_TIMESTAMP_BIND_APPLICATION);
+    }
+
+    void reportFirstFrameTimeNanos(ProcessRecord app, long timeNs) {
+        if (!mEnabled) {
+            return;
+        }
+        addTimestampToStart(app, timeNs,
+                ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
+    }
+
+    void reportFullyDrawnTimeNanos(ProcessRecord app, long timeNs) {
+        if (!mEnabled) {
+            return;
+        }
+        addTimestampToStart(app, timeNs,
+                ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN);
+    }
+
+    void reportFullyDrawnTimeNanos(String processName, int uid, long timeNs) {
+        if (!mEnabled) {
+            return;
+        }
+        addTimestampToStart(processName, uid, timeNs,
+                ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN);
+    }
+
+    private void addTimestampToStart(ProcessRecord app, long timeNs, int key) {
+        addTimestampToStart(app.processName, app.uid, timeNs, key);
+    }
+
+    private void addTimestampToStart(String processName, int uid, long timeNs, int key) {
+        synchronized (mLock) {
+            AppStartInfoContainer container = mData.get(processName, uid);
+            if (container == null) {
+                // Record was not created, discard new data.
+                return;
+            }
+            container.addTimestampToStartLocked(key, timeNs);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private ApplicationStartInfo addStartInfoLocked(ApplicationStartInfo raw) {
+        if (!mAppStartInfoLoaded.get()) {
+            //records added before initial load from storage will be lost.
+            Slog.w(TAG, "Skipping saving the start info due to ongoing loading from storage");
+            return null;
+        }
+
+        final ApplicationStartInfo info = new ApplicationStartInfo(raw);
+
+        AppStartInfoContainer container = mData.get(raw.getProcessName(), raw.getRealUid());
+        if (container == null) {
+            container = new AppStartInfoContainer(mAppStartInfoHistoryListSize);
+            container.mUid = info.getRealUid();
+            mData.put(raw.getProcessName(), raw.getRealUid(), container);
+        }
+        container.addStartInfoLocked(info);
+
+        schedulePersistProcessStartInfo(false);
+
+        return info;
+    }
+
+    /**
+     * Called whenever data is added to a {@link ApplicationStartInfo} object. Checks for
+     * completeness and triggers callback if a callback has been registered and the object
+     * is complete.
+     */
+    private void checkCompletenessAndCallback(ApplicationStartInfo startInfo) {
+        synchronized (mLock) {
+            if (startInfo.getStartupState()
+                    == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) {
+                ApplicationStartInfoCompleteCallback callback =
+                        mCallbacks.get(startInfo.getRealUid());
+                if (callback != null) {
+                    callback.onApplicationStartInfoComplete(startInfo);
+                }
+            }
+        }
+    }
+
+    void getStartInfo(String packageName, int filterUid, int filterPid,
+            int maxNum, ArrayList<ApplicationStartInfo> results) {
+        if (!mEnabled) {
+            return;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                boolean emptyPackageName = TextUtils.isEmpty(packageName);
+                if (!emptyPackageName) {
+                    // fast path
+                    AppStartInfoContainer container = mData.get(packageName, filterUid);
+                    if (container != null) {
+                        container.getStartInfoLocked(filterPid, maxNum, results);
+                    }
+                } else {
+                    // slow path
+                    final ArrayList<ApplicationStartInfo> list = mTmpStartInfoList;
+                    list.clear();
+                    // get all packages
+                    forEachPackageLocked(
+                            (name, records) -> {
+                                AppStartInfoContainer container = records.get(filterUid);
+                                if (container != null) {
+                                    list.addAll(container.mInfos);
+                                }
+                                return AppStartInfoTracker.FOREACH_ACTION_NONE;
+                            });
+
+                    Collections.sort(
+                            list, (a, b) ->
+                            Long.compare(getStartTimestamp(b), getStartTimestamp(a)));
+                    int size = list.size();
+                    if (maxNum > 0) {
+                        size = Math.min(size, maxNum);
+                    }
+                    for (int i = 0; i < size; i++) {
+                        results.add(list.get(i));
+                    }
+                    list.clear();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    final class ApplicationStartInfoCompleteCallback implements DeathRecipient {
+        private final int mUid;
+        private final IApplicationStartInfoCompleteListener mCallback;
+
+        ApplicationStartInfoCompleteCallback(IApplicationStartInfoCompleteListener callback,
+                int uid) {
+            mCallback = callback;
+            mUid = uid;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                /*ignored*/
+            }
+        }
+
+        void onApplicationStartInfoComplete(ApplicationStartInfo startInfo) {
+            try {
+                mCallback.onApplicationStartInfoComplete(startInfo);
+            } catch (RemoteException e) {
+                /*ignored*/
+            }
+            clearStartInfoCompleteListener(mUid, true);
+        }
+
+        void unlinkToDeath() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            clearStartInfoCompleteListener(mUid, false);
+        }
+    }
+
+    void addStartInfoCompleteListener(
+            final IApplicationStartInfoCompleteListener listener, final int uid) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            mCallbacks.put(uid, new ApplicationStartInfoCompleteCallback(listener, uid));
+        }
+    }
+
+    void clearStartInfoCompleteListener(final int uid, boolean unlinkDeathRecipient) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (unlinkDeathRecipient) {
+                ApplicationStartInfoCompleteCallback callback = mCallbacks.get(uid);
+                if (callback != null) {
+                    callback.unlinkToDeath();
+                }
+            }
+            mCallbacks.remove(uid);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void forEachPackageLocked(
+            BiFunction<String, SparseArray<AppStartInfoContainer>, Integer> callback) {
+        if (callback != null) {
+            ArrayMap<String, SparseArray<AppStartInfoContainer>> map = mData.getMap();
+            for (int i = map.size() - 1; i >= 0; i--) {
+                switch (callback.apply(map.keyAt(i), map.valueAt(i))) {
+                    case FOREACH_ACTION_REMOVE_ITEM:
+                        map.removeAt(i);
+                        break;
+                    case FOREACH_ACTION_STOP_ITERATION:
+                        i = 0;
+                        break;
+                    case FOREACH_ACTION_NONE:
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void removePackageLocked(String packageName, int uid, boolean removeUid, int userId) {
+        ArrayMap<String, SparseArray<AppStartInfoContainer>> map = mData.getMap();
+        SparseArray<AppStartInfoContainer> array = map.get(packageName);
+        if (array == null) {
+            return;
+        }
+        if (userId == UserHandle.USER_ALL) {
+            mData.getMap().remove(packageName);
+        } else {
+            for (int i = array.size() - 1; i >= 0; i--) {
+                if (UserHandle.getUserId(array.keyAt(i)) == userId) {
+                    array.removeAt(i);
+                    break;
+                }
+            }
+            if (array.size() == 0) {
+                map.remove(packageName);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void removeByUserIdLocked(final int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            mData.getMap().clear();
+            return;
+        }
+        forEachPackageLocked(
+                (packageName, records) -> {
+                    for (int i = records.size() - 1; i >= 0; i--) {
+                        if (UserHandle.getUserId(records.keyAt(i)) == userId) {
+                            records.removeAt(i);
+                            break;
+                        }
+                    }
+                    return records.size() == 0 ? FOREACH_ACTION_REMOVE_ITEM : FOREACH_ACTION_NONE;
+                });
+    }
+
+    @VisibleForTesting
+    void onUserRemoved(int userId) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            removeByUserIdLocked(userId);
+            schedulePersistProcessStartInfo(true);
+        }
+    }
+
+    @VisibleForTesting
+    void onPackageRemoved(String packageName, int uid, boolean allUsers) {
+        if (!mEnabled) {
+            return;
+        }
+        if (packageName != null) {
+            final boolean removeUid =
+                    TextUtils.isEmpty(mService.mPackageManagerInt.getNameForUid(uid));
+            synchronized (mLock) {
+                removePackageLocked(
+                        packageName,
+                        uid,
+                        removeUid,
+                        allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid));
+                schedulePersistProcessStartInfo(true);
+            }
+        }
+    }
+
+    private void registerForUserRemoval() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_REMOVED);
+        mService.mContext.registerReceiverForAllUsers(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                        if (userId < 1) return;
+                        onUserRemoved(userId);
+                    }
+                },
+                filter,
+                null,
+                mHandler);
+    }
+
+    private void registerForPackageRemoval() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mService.mContext.registerReceiverForAllUsers(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                        if (replacing) {
+                            return;
+                        }
+                        int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
+                        boolean allUsers =
+                                intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false);
+                        onPackageRemoved(intent.getData().getSchemeSpecificPart(), uid, allUsers);
+                    }
+                },
+                filter,
+                null,
+                mHandler);
+    }
+
+    /**
+     * Load the existing {@link android.app.ApplicationStartInfo} records from persistent storage.
+     */
+    @VisibleForTesting
+    void loadExistingProcessStartInfo() {
+        if (!mEnabled) {
+            return;
+        }
+        if (!mProcStartInfoFile.canRead()) {
+            // If file can't be read, mark complete so we can begin accepting new records.
+            mAppStartInfoLoaded.set(true);
+            return;
+        }
+
+        FileInputStream fin = null;
+        try {
+            AtomicFile af = new AtomicFile(mProcStartInfoFile);
+            fin = af.openRead();
+            ProtoInputStream proto = new ProtoInputStream(fin);
+            for (int next = proto.nextField();
+                    next != ProtoInputStream.NO_MORE_FIELDS;
+                    next = proto.nextField()) {
+                switch (next) {
+                    case (int) AppsStartInfoProto.LAST_UPDATE_TIMESTAMP:
+                        synchronized (mLock) {
+                            mLastAppStartInfoPersistTimestamp =
+                                    proto.readLong(AppsStartInfoProto.LAST_UPDATE_TIMESTAMP);
+                        }
+                        break;
+                    case (int) AppsStartInfoProto.PACKAGES:
+                        loadPackagesFromProto(proto, next);
+                        break;
+                }
+            }
+        } catch (IOException | IllegalArgumentException | WireTypeMismatchException
+                | ClassNotFoundException e) {
+            Slog.w(TAG, "Error in loading historical app start info from persistent storage: " + e);
+        } finally {
+            if (fin != null) {
+                try {
+                    fin.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        mAppStartInfoLoaded.set(true);
+    }
+
+    private void loadPackagesFromProto(ProtoInputStream proto, long fieldId)
+            throws IOException, WireTypeMismatchException, ClassNotFoundException {
+        long token = proto.start(fieldId);
+        String pkgName = "";
+        for (int next = proto.nextField();
+                next != ProtoInputStream.NO_MORE_FIELDS;
+                next = proto.nextField()) {
+            switch (next) {
+                case (int) AppsStartInfoProto.Package.PACKAGE_NAME:
+                    pkgName = proto.readString(AppsStartInfoProto.Package.PACKAGE_NAME);
+                    break;
+                case (int) AppsStartInfoProto.Package.USERS:
+                    AppStartInfoContainer container =
+                            new AppStartInfoContainer(mAppStartInfoHistoryListSize);
+                    int uid = container.readFromProto(proto, AppsStartInfoProto.Package.USERS);
+                    synchronized (mLock) {
+                        mData.put(pkgName, uid, container);
+                    }
+                    break;
+            }
+        }
+        proto.end(token);
+    }
+
+    /** Persist the existing {@link android.app.ApplicationStartInfo} records to storage. */
+    @VisibleForTesting
+    void persistProcessStartInfo() {
+        if (!mEnabled) {
+            return;
+        }
+        AtomicFile af = new AtomicFile(mProcStartInfoFile);
+        FileOutputStream out = null;
+        long now = System.currentTimeMillis();
+        try {
+            out = af.startWrite();
+            ProtoOutputStream proto = new ProtoOutputStream(out);
+            proto.write(AppsStartInfoProto.LAST_UPDATE_TIMESTAMP, now);
+            synchronized (mLock) {
+                forEachPackageLocked(
+                        (packageName, records) -> {
+                            long token = proto.start(AppsStartInfoProto.PACKAGES);
+                            proto.write(AppsStartInfoProto.Package.PACKAGE_NAME, packageName);
+                            int uidArraySize = records.size();
+                            for (int j = 0; j < uidArraySize; j++) {
+                                try {
+                                    records.valueAt(j)
+                                        .writeToProto(proto, AppsStartInfoProto.Package.USERS);
+                                } catch (IOException e) {
+                                    Slog.w(TAG, "Unable to write app start info into persistent"
+                                            + "storage: " + e);
+                                }
+                            }
+                            proto.end(token);
+                            return AppStartInfoTracker.FOREACH_ACTION_NONE;
+                        });
+                mLastAppStartInfoPersistTimestamp = now;
+            }
+            proto.flush();
+            af.finishWrite(out);
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to write historical app start info into persistent storage: " + e);
+            af.failWrite(out);
+        }
+        synchronized (mLock) {
+            mAppStartInfoPersistTask = null;
+        }
+    }
+
+    /**
+     * Schedule a task to persist the {@link android.app.ApplicationStartInfo} records to storage.
+     */
+    @VisibleForTesting
+    void schedulePersistProcessStartInfo(boolean immediately) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (mAppStartInfoPersistTask == null || immediately) {
+                if (mAppStartInfoPersistTask != null) {
+                    IoThread.getHandler().removeCallbacks(mAppStartInfoPersistTask);
+                }
+                mAppStartInfoPersistTask = this::persistProcessStartInfo;
+                IoThread.getHandler()
+                        .postDelayed(
+                                mAppStartInfoPersistTask,
+                                immediately ? 0 : APP_START_INFO_PERSIST_INTERVAL);
+            }
+        }
+    }
+
+    /** Helper function for testing only. */
+    @VisibleForTesting
+    void clearProcessStartInfo(boolean removeFile) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (mAppStartInfoPersistTask != null) {
+                IoThread.getHandler().removeCallbacks(mAppStartInfoPersistTask);
+                mAppStartInfoPersistTask = null;
+            }
+            if (removeFile && mProcStartInfoFile != null) {
+                mProcStartInfoFile.delete();
+            }
+            mData.getMap().clear();
+        }
+    }
+
+    /**
+     * Helper functions for shell command.
+     * > adb shell dumpsys activity clear-start-info [package-name]
+     */
+    void clearHistoryProcessStartInfo(String packageName, int userId) {
+        if (!mEnabled) {
+            return;
+        }
+        Optional<Integer> appId = Optional.empty();
+        if (TextUtils.isEmpty(packageName)) {
+            synchronized (mLock) {
+                removeByUserIdLocked(userId);
+            }
+        } else {
+            final int uid =
+                    mService.mPackageManagerInt.getPackageUid(
+                            packageName, PackageManager.MATCH_ALL, userId);
+            appId = Optional.of(UserHandle.getAppId(uid));
+            synchronized (mLock) {
+                removePackageLocked(packageName, uid, true, userId);
+            }
+        }
+        schedulePersistProcessStartInfo(true);
+    }
+
+    /**
+     * Helper functions for shell command.
+     * > adb shell dumpsys activity start-info [package-name]
+     */
+    void dumpHistoryProcessStartInfo(PrintWriter pw, String packageName) {
+        if (!mEnabled) {
+            return;
+        }
+        pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity start-info)");
+        SimpleDateFormat sdf = new SimpleDateFormat();
+        synchronized (mLock) {
+            pw.println("Last Timestamp of Persistence Into Persistent Storage: "
+                    + sdf.format(new Date(mLastAppStartInfoPersistTimestamp)));
+            if (TextUtils.isEmpty(packageName)) {
+                forEachPackageLocked((name, records) -> {
+                    dumpHistoryProcessStartInfoLocked(pw, "  ", name, records, sdf);
+                    return AppStartInfoTracker.FOREACH_ACTION_NONE;
+                });
+            } else {
+                SparseArray<AppStartInfoContainer> array = mData.getMap().get(packageName);
+                if (array != null) {
+                    dumpHistoryProcessStartInfoLocked(pw, "  ", packageName, array, sdf);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void dumpHistoryProcessStartInfoLocked(PrintWriter pw, String prefix,
+            String packageName, SparseArray<AppStartInfoContainer> array,
+            SimpleDateFormat sdf) {
+        pw.println(prefix + "package: " + packageName);
+        int size = array.size();
+        for (int i = 0; i < size; i++) {
+            pw.println(prefix + "  Historical Process Start for userId=" + array.keyAt(i));
+            array.valueAt(i).dumpLocked(pw, prefix + "    ", sdf);
+        }
+    }
+
+    /** Convenience method to obtain timestamp of beginning of start.*/
+    private static long getStartTimestamp(ApplicationStartInfo startInfo) {
+        return startInfo.getStartupTimestamps().get(START_TIMESTAMP_LAUNCH);
+    }
+
+    /** A container class of (@link android.app.ApplicationStartInfo) */
+    final class AppStartInfoContainer {
+        private List<ApplicationStartInfo> mInfos; // Always kept sorted by first timestamp.
+        private int mMaxCapacity;
+        private int mUid;
+
+        AppStartInfoContainer(final int maxCapacity) {
+            mInfos = new ArrayList<ApplicationStartInfo>();
+            mMaxCapacity = maxCapacity;
+        }
+
+        @GuardedBy("mLock")
+        void getStartInfoLocked(
+                final int filterPid, final int maxNum, ArrayList<ApplicationStartInfo> results) {
+            results.addAll(mInfos.size() <= maxNum ? 0 : mInfos.size() - maxNum, mInfos);
+        }
+
+        @GuardedBy("mLock")
+        void addStartInfoLocked(ApplicationStartInfo info) {
+            int size = mInfos.size();
+            if (size >= mMaxCapacity) {
+                // Remove oldest record if size is over max capacity.
+                int oldestIndex = -1;
+                long oldestTimeStamp = Long.MAX_VALUE;
+                for (int i = 0; i < size; i++) {
+                    ApplicationStartInfo startInfo = mInfos.get(i);
+                    if (getStartTimestamp(startInfo) < oldestTimeStamp) {
+                        oldestTimeStamp = getStartTimestamp(startInfo);
+                        oldestIndex = i;
+                    }
+                }
+                if (oldestIndex >= 0) {
+                    mInfos.remove(oldestIndex);
+                }
+                mInfos.remove(0);
+            }
+            mInfos.add(info);
+            Collections.sort(mInfos, (a, b) ->
+                    Long.compare(getStartTimestamp(b), getStartTimestamp(a)));
+        }
+
+        @GuardedBy("mLock")
+        void addTimestampToStartLocked(int key, long timestampNs) {
+            int index = mInfos.size() - 1;
+            int startupState = mInfos.get(index).getStartupState();
+            if (startupState == ApplicationStartInfo.STARTUP_STATE_STARTED
+                    || key == ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) {
+                mInfos.get(index).addStartupTimestamp(key, timestampNs);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
+            int size = mInfos.size();
+            for (int i = 0; i < size; i++) {
+                mInfos.get(i).dump(pw, prefix + "  ", "#" + i, sdf);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void writeToProto(ProtoOutputStream proto, long fieldId) throws IOException {
+            long token = proto.start(fieldId);
+            proto.write(AppsStartInfoProto.Package.User.UID, mUid);
+            int size = mInfos.size();
+            for (int i = 0; i < size; i++) {
+                mInfos.get(i)
+                        .writeToProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO);
+            }
+            proto.end(token);
+        }
+
+        int readFromProto(ProtoInputStream proto, long fieldId)
+                throws IOException, WireTypeMismatchException, ClassNotFoundException {
+            long token = proto.start(fieldId);
+            for (int next = proto.nextField();
+                    next != ProtoInputStream.NO_MORE_FIELDS;
+                    next = proto.nextField()) {
+                switch (next) {
+                    case (int) AppsStartInfoProto.Package.User.UID:
+                        mUid = proto.readInt(AppsStartInfoProto.Package.User.UID);
+                        break;
+                    case (int) AppsStartInfoProto.Package.User.APP_START_INFO:
+                        ApplicationStartInfo info = new ApplicationStartInfo();
+                        info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO);
+                        mInfos.add(info);
+                        break;
+                }
+            }
+            proto.end(token);
+            return mUid;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0ab81a5..9e48b0a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -121,6 +121,7 @@
 import com.android.server.power.stats.BatteryExternalStatsWorker;
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.power.stats.BatteryUsageStatsProvider;
+import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor;
 import com.android.server.power.stats.PowerStatsAggregator;
 import com.android.server.power.stats.PowerStatsScheduler;
 import com.android.server.power.stats.PowerStatsStore;
@@ -440,7 +441,9 @@
                 .trackUidStates(
                         AggregatedPowerStatsConfig.STATE_POWER,
                         AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(
+                        new CpuAggregatedPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
         return config;
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f04198e..614caffe 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -494,6 +494,10 @@
     @GuardedBy("mService")
     final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
 
+    /** Manages the {@link android.app.ApplicationStartInfo} records. */
+    @GuardedBy("mAppStartInfoTracker")
+    final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker();
+
     /**
      * The currently running SDK sandbox processes for a uid.
      */
@@ -956,12 +960,14 @@
                         mSystemServerSocketForZygote.getFileDescriptor(),
                         EVENT_INPUT, this::handleZygoteMessages);
             }
+            mAppStartInfoTracker.init(mService);
             mAppExitInfoTracker.init(mService);
             mImperceptibleKillRunner = new ImperceptibleKillRunner(sKillThread.getLooper());
         }
     }
 
     void onSystemReady() {
+        mAppStartInfoTracker.onSystemReady();
         mAppExitInfoTracker.onSystemReady();
     }
 
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a57a785..439fe0b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -123,6 +123,7 @@
         "angle",
         "arc_next",
         "bluetooth",
+        "build",
         "biometrics_framework",
         "biometrics_integration",
         "camera_platform",
@@ -136,10 +137,12 @@
         "context_hub",
         "core_experiments_team_internal",
         "core_graphics",
+        "game",
         "haptics",
         "hardware_backed_security_mainline",
         "input",
         "machine_learning",
+        "mainline_modularization",
         "mainline_sdk",
         "media_audio",
         "media_drm",
@@ -152,9 +155,12 @@
         "platform_security",
         "power",
         "preload_safety",
+        "privacy_infra_policy",
+        "resource_manager",
         "responsible_apis",
         "rust",
         "safety_center",
+        "sensors",
         "system_performance",
         "test_suites",
         "text",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0dd579f..728bace 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -658,8 +658,8 @@
         mInjector.getUserJourneyLogger()
                 .logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER,
                 EVENT_STATE_BEGIN);
-        // If the user key hasn't been unlocked yet, we cannot proceed.
-        if (!StorageManager.isUserKeyUnlocked(userId)) return false;
+        // If the user's CE storage hasn't been unlocked yet, we cannot proceed.
+        if (!StorageManager.isCeStorageUnlocked(userId)) return false;
         synchronized (mLock) {
             // Do not proceed if unexpected state or a stale user
             if (mStartedUsers.get(userId) != uss || uss.state != STATE_RUNNING_LOCKED) {
@@ -674,8 +674,8 @@
 
         // Call onBeforeUnlockUser on a worker thread that allows disk I/O
         FgThread.getHandler().post(() -> {
-            if (!StorageManager.isUserKeyUnlocked(userId)) {
-                Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
+            if (!StorageManager.isCeStorageUnlocked(userId)) {
+                Slogf.w(TAG, "User's CE storage got locked unexpectedly, leaving user locked.");
                 return;
             }
 
@@ -709,8 +709,8 @@
     private void finishUserUnlocked(final UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
         EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED, userId);
-        // Only keep marching forward if user is actually unlocked
-        if (!StorageManager.isUserKeyUnlocked(userId)) return;
+        // Only keep marching forward if the user's CE storage is unlocked.
+        if (!StorageManager.isCeStorageUnlocked(userId)) return;
         synchronized (mLock) {
             // Bail if we ended up with a stale user
             if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
@@ -796,8 +796,8 @@
         if (userInfo == null) {
             return;
         }
-        // Only keep marching forward if user is actually unlocked
-        if (!StorageManager.isUserKeyUnlocked(userId)) return;
+        // Only keep marching forward if the user's CE storage is unlocked.
+        if (!StorageManager.isCeStorageUnlocked(userId)) return;
 
         // Remember that we logged in
         mInjector.getUserManager().onUserLoggedIn(userId);
@@ -1330,7 +1330,7 @@
             }
             try {
                 Slogf.i(TAG, "Locking CE storage for user #" + userId);
-                mInjector.getStorageManager().lockUserKey(userId);
+                mInjector.getStorageManager().lockCeStorage(userId);
             } catch (RemoteException re) {
                 throw re.rethrowAsRuntimeException();
             }
@@ -1946,8 +1946,8 @@
         }
 
         UserState uss;
-        if (!StorageManager.isUserKeyUnlocked(userId)) {
-            // We always want to try to unlock the user key, even if the user is not started yet.
+        if (!StorageManager.isCeStorageUnlocked(userId)) {
+            // We always want to try to unlock CE storage, even if the user is not started yet.
             mLockPatternUtils.unlockUserKeyIfUnsecured(userId);
         }
         synchronized (mLock) {
@@ -2750,10 +2750,10 @@
                 case UserState.STATE_RUNNING_UNLOCKING:
                 case UserState.STATE_RUNNING_UNLOCKED:
                     return true;
-                // In the stopping/shutdown state return unlock state of the user key
+                // In the stopping/shutdown state, return unlock state of the user's CE storage.
                 case UserState.STATE_STOPPING:
                 case UserState.STATE_SHUTDOWN:
-                    return StorageManager.isUserKeyUnlocked(userId);
+                    return StorageManager.isCeStorageUnlocked(userId);
                 default:
                     return false;
             }
@@ -2762,10 +2762,10 @@
             switch (state.state) {
                 case UserState.STATE_RUNNING_UNLOCKED:
                     return true;
-                // In the stopping/shutdown state return unlock state of the user key
+                // In the stopping/shutdown state, return unlock state of the user's CE storage.
                 case UserState.STATE_STOPPING:
                 case UserState.STATE_SHUTDOWN:
-                    return StorageManager.isUserKeyUnlocked(userId);
+                    return StorageManager.isCeStorageUnlocked(userId);
                 default:
                     return false;
             }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 516293b..052b0c2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2680,8 +2680,17 @@
                 .filterAppAccess(packageName, callingUid, userId);
     }
 
+    /** @deprecated Use {@link #noteProxyOperationWithState} instead. */
     @Override
     public SyncNotedAppOp noteProxyOperation(int code,
+            AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp,
+            String message, boolean shouldCollectMessage, boolean skipProxyOperation) {
+        return mCheckOpsDelegateDispatcher.noteProxyOperation(code, attributionSource,
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
+    }
+
+    @Override
+    public SyncNotedAppOp noteProxyOperationWithState(int code,
             AttributionSourceState attributionSourceState, boolean shouldCollectAsyncNotedOp,
             String message, boolean shouldCollectMessage, boolean skipProxyOperation) {
         AttributionSource attributionSource = new AttributionSource(attributionSourceState);
@@ -3212,8 +3221,21 @@
                 attributionChainId);
     }
 
+    /** @deprecated Use {@link #startProxyOperationWithState} instead. */
     @Override
     public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code,
+            @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+            boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
+            @AttributionFlags int proxiedAttributionFlags, int attributionChainId) {
+        return mCheckOpsDelegateDispatcher.startProxyOperation(clientId, code, attributionSource,
+                startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags,
+                attributionChainId);
+    }
+
+    @Override
+    public SyncNotedAppOp startProxyOperationWithState(@NonNull IBinder clientId, int code,
             @NonNull AttributionSourceState attributionSourceState, boolean startIfModeDefault,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
@@ -3513,8 +3535,16 @@
         finishOperationUnchecked(clientId, code, uid, resolvedPackageName, attributionTag);
     }
 
+    /** @deprecated Use {@link #finishProxyOperationWithState} instead. */
     @Override
     public void finishProxyOperation(@NonNull IBinder clientId, int code,
+            @NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
+        mCheckOpsDelegateDispatcher.finishProxyOperation(clientId, code, attributionSource,
+                skipProxyOperation);
+    }
+
+    @Override
+    public void finishProxyOperationWithState(@NonNull IBinder clientId, int code,
             @NonNull AttributionSourceState attributionSourceState, boolean skipProxyOperation) {
         AttributionSource attributionSource = new AttributionSource(attributionSourceState);
         mCheckOpsDelegateDispatcher.finishProxyOperation(clientId, code, attributionSource,
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index 292fc14..51cb950 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -165,7 +165,8 @@
 
     @Override
     public String toString() {
-        return "type: " + mDeviceType + "internal type: " + mInternalDeviceType
+        return "type: " + mDeviceType
+                + " internal type: 0x" + Integer.toHexString(mInternalDeviceType)
                 + " addr: " + mDeviceAddress + " bt audio type: "
                 + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory)
                 + " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index eea3d38..2336753 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1252,8 +1252,8 @@
     }
 
     /*package*/ void registerStrategyPreferredDevicesDispatcher(
-            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
-        mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
+        mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher, isPrivileged);
     }
 
     /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -1262,8 +1262,8 @@
     }
 
     /*package*/ void registerStrategyNonDefaultDevicesDispatcher(
-            @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher) {
-        mDeviceInventory.registerStrategyNonDefaultDevicesDispatcher(dispatcher);
+            @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher, boolean isPrivileged) {
+        mDeviceInventory.registerStrategyNonDefaultDevicesDispatcher(dispatcher, isPrivileged);
     }
 
     /*package*/ void unregisterStrategyNonDefaultDevicesDispatcher(
@@ -1281,8 +1281,8 @@
     }
 
     /*package*/ void registerCapturePresetDevicesRoleDispatcher(
-            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
-        mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
+        mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher, isPrivileged);
     }
 
     /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -1290,6 +1290,11 @@
         mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
     }
 
+    /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
+            List<AudioDeviceAttributes> devices) {
+        return mAudioService.anonymizeAudioDeviceAttributesListUnchecked(devices);
+    }
+
     /*package*/ void registerCommunicationDeviceDispatcher(
             @NonNull ICommunicationDeviceDispatcher dispatcher) {
         mCommDevDispatchers.register(dispatcher);
@@ -2684,4 +2689,5 @@
     void clearDeviceInventory() {
         mDeviceInventory.clearDeviceInventory();
     }
+
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e08fdd6..d707689 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -121,6 +121,26 @@
     }
 
     /**
+     * Adds a new entry in mDeviceInventory if the AudioDeviceAttributes passed is an sink
+     * Bluetooth device and no corresponding entry already exists.
+     * @param ada the device to add if needed
+     */
+    void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
+        if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
+            return;
+        }
+        synchronized (mDeviceInventoryLock) {
+            if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
+                return;
+            }
+            AdiDeviceState ads = new AdiDeviceState(
+                    ada.getType(), ada.getInternalType(), ada.getAddress());
+            mDeviceInventory.put(ads.getDeviceId(), ads);
+        }
+        mDeviceBroker.persistAudioDeviceSettings();
+    }
+
+    /**
      * Adds a new AdiDeviceState or updates the audio device cateogory of the matching
      * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
      * @param deviceState the device to update
@@ -904,9 +924,6 @@
             @NonNull List<AudioDeviceAttributes> devices) {
         int status = AudioSystem.ERROR;
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
-                    "setPreferredDevicesForStrategy, strategy: " + strategy
-                            + " devices: " + devices)).printLog(TAG));
             status = setDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, false /* internal */);
         }
@@ -932,10 +949,6 @@
         int status = AudioSystem.ERROR;
 
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
-                            "removePreferredDevicesForStrategy, strategy: "
-                            + strategy)).printLog(TAG));
-
             status = clearDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_PREFERRED, false /*internal */);
         }
@@ -954,10 +967,6 @@
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
             List<AudioDeviceAttributes> devices = new ArrayList<>();
             devices.add(device);
-
-            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
-                            "setDeviceAsNonDefaultForStrategyAndSave, strategy: " + strategy
-                            + " device: " + device)).printLog(TAG));
             status = addDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */);
         }
@@ -975,11 +984,6 @@
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
             List<AudioDeviceAttributes> devices = new ArrayList<>();
             devices.add(device);
-
-            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
-                            "removeDeviceAsNonDefaultForStrategyAndSave, strategy: "
-                            + strategy + " devices: " + device)).printLog(TAG));
-
             status = removeDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */);
         }
@@ -992,8 +996,8 @@
 
 
     /*package*/ void registerStrategyPreferredDevicesDispatcher(
-            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
-        mPrefDevDispatchers.register(dispatcher);
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
+        mPrefDevDispatchers.register(dispatcher, isPrivileged);
     }
 
     /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -1002,8 +1006,8 @@
     }
 
     /*package*/ void registerStrategyNonDefaultDevicesDispatcher(
-            @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher) {
-        mNonDefDevDispatchers.register(dispatcher);
+            @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher, boolean isPrivileged) {
+        mNonDefDevDispatchers.register(dispatcher, isPrivileged);
     }
 
     /*package*/ void unregisterStrategyNonDefaultDevicesDispatcher(
@@ -1084,8 +1088,8 @@
     }
 
     /*package*/ void registerCapturePresetDevicesRoleDispatcher(
-            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
-        mDevRoleCapturePresetDispatchers.register(dispatcher);
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
+        mDevRoleCapturePresetDispatchers.register(dispatcher, isPrivileged);
     }
 
     /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -1414,6 +1418,8 @@
                     updateBluetoothPreferredModes_l(connect ? btDevice : null /*connectedDevice*/);
                     if (!connect) {
                         purgeDevicesRoles_l();
+                    } else {
+                        addAudioDeviceInInventoryIfNeeded(attributes);
                     }
                 }
                 mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
@@ -1702,6 +1708,7 @@
         setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
 
         updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
+        addAudioDeviceInInventoryIfNeeded(ada);
     }
 
     static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER,
@@ -2006,9 +2013,9 @@
         final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
                 AudioSystem.DEVICE_OUT_HEARING_AID);
         mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
-
-        mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
-                AudioSystem.DEVICE_OUT_HEARING_AID, address, name),
+        AudioDeviceAttributes ada = new AudioDeviceAttributes(
+                AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
+        mAudioSystem.setDeviceConnectionState(ada,
                 AudioSystem.DEVICE_STATE_AVAILABLE,
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
         mConnectedDevices.put(
@@ -2018,6 +2025,7 @@
         mDeviceBroker.postApplyVolumeOnDevice(streamType,
                 AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
         setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
+        addAudioDeviceInInventoryIfNeeded(ada);
         new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
                 .set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
                 .set(MediaMetrics.Property.DEVICE,
@@ -2128,6 +2136,7 @@
                             sensorUuid));
             mDeviceBroker.postAccessoryPlugMediaUnmute(device);
             setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
+            addAudioDeviceInInventoryIfNeeded(ada);
         }
 
         if (streamType == AudioSystem.STREAM_DEFAULT) {
@@ -2462,6 +2471,9 @@
         final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
             try {
+                if (!((Boolean) mPrefDevDispatchers.getBroadcastCookie(i))) {
+                    devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
+                }
                 mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
                         strategy, devices);
             } catch (RemoteException e) {
@@ -2475,6 +2487,9 @@
         final int nbDispatchers = mNonDefDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
             try {
+                if (!((Boolean) mNonDefDevDispatchers.getBroadcastCookie(i))) {
+                    devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
+                }
                 mNonDefDevDispatchers.getBroadcastItem(i).dispatchNonDefDevicesChanged(
                         strategy, devices);
             } catch (RemoteException e) {
@@ -2488,6 +2503,9 @@
         final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; ++i) {
             try {
+                if (!((Boolean) mDevRoleCapturePresetDispatchers.getBroadcastCookie(i))) {
+                    devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
+                }
                 mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
                         capturePreset, role, devices);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3243385..a8fa313 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -16,8 +16,8 @@
 
 package com.android.server.audio;
 
-import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
 import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
+import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
 import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
 import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -36,6 +36,7 @@
 import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
+
 import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
 import static com.android.server.utils.EventLogger.Event.ALOGE;
 import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -149,6 +150,7 @@
 import android.media.projection.IMediaProjection;
 import android.media.projection.IMediaProjectionCallback;
 import android.media.projection.IMediaProjectionManager;
+import android.media.session.MediaSessionManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -201,6 +203,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.media.audio.flags.Flags;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -307,6 +310,9 @@
     private final ContentResolver mContentResolver;
     private final AppOpsManager mAppOps;
 
+    /** do not use directly, use getMediaSessionManager() which handles lazy initialization */
+    @Nullable private volatile MediaSessionManager mMediaSessionManager;
+
     // the platform type affects volume and silent mode behavior
     private final int mPlatformType;
 
@@ -938,6 +944,8 @@
 
     private final SoundDoseHelper mSoundDoseHelper;
 
+    private final HardeningEnforcer mHardeningEnforcer;
+
     private final Object mSupportedSystemUsagesLock = new Object();
     @GuardedBy("mSupportedSystemUsagesLock")
     private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -1312,6 +1320,8 @@
         mDisplayManager = context.getSystemService(DisplayManager.class);
 
         mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
+
+        mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive());
     }
 
     private void initVolumeStreamStates() {
@@ -1381,7 +1391,6 @@
 
         // check on volume initialization
         checkVolumeRangeInitialization("AudioService()");
-
     }
 
     private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener =
@@ -1394,6 +1403,14 @@
                 }
             };
 
+    private MediaSessionManager getMediaSessionManager() {
+        if (mMediaSessionManager == null) {
+            mMediaSessionManager = (MediaSessionManager) mContext
+                    .getSystemService(Context.MEDIA_SESSION_SERVICE);
+        }
+        return mMediaSessionManager;
+    }
+
     /**
      * Initialize intent receives and settings observers for this service.
      * Must be called after createStreamStates() as the handling of some events
@@ -2881,7 +2898,7 @@
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
     /**
-     * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+     * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy, AudioDeviceAttributes)
      * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
      *                                                  List<AudioDeviceAttributes>)
      */
@@ -2891,8 +2908,11 @@
         if (devices == null) {
             return AudioSystem.ERROR;
         }
+
+        devices = retrieveBluetoothAddresses(devices);
+
         final String logString = String.format(
-                "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
+                "setPreferredDevicesForStrategy u/pid:%d/%d strat:%d dev:%s",
                 Binder.getCallingUid(), Binder.getCallingPid(), strategy,
                 devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
         sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
@@ -2916,11 +2936,11 @@
         super.removePreferredDevicesForStrategy_enforcePermission();
 
         final String logString =
-                String.format("removePreferredDeviceForStrategy strat:%d", strategy);
+                String.format("removePreferredDevicesForStrategy strat:%d", strategy);
         sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
 
         final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
-        if (status != AudioSystem.SUCCESS) {
+        if (status != AudioSystem.SUCCESS && status != AudioSystem.BAD_VALUE) {
             Log.e(TAG, String.format("Error %d in %s)", status, logString));
         }
         return status;
@@ -2948,7 +2968,7 @@
                     status, strategy));
             return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return devices;
+            return anonymizeAudioDeviceAttributesList(devices);
         }
     }
 
@@ -2963,6 +2983,9 @@
                                                 @NonNull AudioDeviceAttributes device) {
         super.setDeviceAsNonDefaultForStrategy_enforcePermission();
         Objects.requireNonNull(device);
+
+        device = retrieveBluetoothAddress(device);
+
         final String logString = String.format(
                 "setDeviceAsNonDefaultForStrategy u/pid:%d/%d strat:%d dev:%s",
                 Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
@@ -2989,6 +3012,9 @@
                                                    AudioDeviceAttributes device) {
         super.removeDeviceAsNonDefaultForStrategy_enforcePermission();
         Objects.requireNonNull(device);
+
+        device = retrieveBluetoothAddress(device);
+
         final String logString = String.format(
                 "removeDeviceAsNonDefaultForStrategy strat:%d dev:%s", strategy, device.toString());
         sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
@@ -2998,7 +3024,7 @@
         }
 
         final int status = mDeviceBroker.removeDeviceAsNonDefaultForStrategySync(strategy, device);
-        if (status != AudioSystem.SUCCESS) {
+        if (status != AudioSystem.SUCCESS && status != AudioSystem.BAD_VALUE) {
             Log.e(TAG, String.format("Error %d in %s)", status, logString));
         }
         return status;
@@ -3023,7 +3049,7 @@
                     status, strategy));
             return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return devices;
+            return anonymizeAudioDeviceAttributesList(devices);
         }
     }
 
@@ -3036,7 +3062,8 @@
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
+        mDeviceBroker.registerStrategyPreferredDevicesDispatcher(
+                dispatcher, isBluetoothPrividged());
     }
 
     /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
@@ -3060,7 +3087,8 @@
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerStrategyNonDefaultDevicesDispatcher(dispatcher);
+        mDeviceBroker.registerStrategyNonDefaultDevicesDispatcher(
+                dispatcher, isBluetoothPrividged());
     }
 
     /** @see AudioManager#removeOnNonDefaultDevicesForStrategyChangedListener(
@@ -3076,7 +3104,7 @@
     }
 
     /**
-     * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+     * @see AudioManager#setPreferredDevicesForCapturePreset(int, AudioDeviceAttributes)
      */
     public int setPreferredDevicesForCapturePreset(
             int capturePreset, List<AudioDeviceAttributes> devices) {
@@ -3095,6 +3123,8 @@
             return AudioSystem.ERROR;
         }
 
+        devices = retrieveBluetoothAddresses(devices);
+
         final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
                 capturePreset, devices);
         if (status != AudioSystem.SUCCESS) {
@@ -3114,7 +3144,7 @@
         sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
 
         final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
-        if (status != AudioSystem.SUCCESS) {
+        if (status != AudioSystem.SUCCESS && status != AudioSystem.BAD_VALUE) {
             Log.e(TAG, String.format("Error %d in %s", status, logString));
         }
         return status;
@@ -3141,7 +3171,7 @@
                     status, capturePreset));
             return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return devices;
+            return anonymizeAudioDeviceAttributesList(devices);
         }
     }
 
@@ -3155,7 +3185,8 @@
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+        mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(
+                dispatcher, isBluetoothPrividged());
     }
 
     /**
@@ -3175,7 +3206,9 @@
     public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
         enforceQueryStateOrModifyRoutingPermission();
-        return getDevicesForAttributesInt(attributes, false /* forVolume */);
+
+        return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
+                getDevicesForAttributesInt(attributes, false /* forVolume */)));
     }
 
     /** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
@@ -3185,7 +3218,8 @@
      */
     public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
             @NonNull AudioAttributes attributes) {
-        return getDevicesForAttributesInt(attributes, false /* forVolume */);
+        return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
+                getDevicesForAttributesInt(attributes, false /* forVolume */)));
     }
 
     /**
@@ -3405,6 +3439,10 @@
      * Part of service interface, check permissions here */
     public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
             String callingPackage, String attributionTag) {
+        if (mHardeningEnforcer.blockVolumeMethod(
+                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) {
+            return;
+        }
         if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
             Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
                     + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -4181,6 +4219,10 @@
      * Part of service interface, check permissions here */
     public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
             String callingPackage, String attributionTag) {
+        if (mHardeningEnforcer.blockVolumeMethod(
+                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) {
+            return;
+        }
         setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null,
                 callingPackage, attributionTag);
     }
@@ -5033,6 +5075,7 @@
     /** @see AudioManager#setMasterMute(boolean, int) */
     public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
             String attributionTag) {
+
         super.setMasterMute_enforcePermission();
 
         setMasterMuteInternal(mute, flags, callingPackage,
@@ -5398,6 +5441,10 @@
     }
 
     public void setRingerModeExternal(int ringerMode, String caller) {
+        if (mHardeningEnforcer.blockVolumeMethod(
+                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) {
+            return;
+        }
         if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
                 && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
             throw new SecurityException("Not allowed to change Do Not Disturb state");
@@ -6150,6 +6197,35 @@
                 AudioDeviceVolumeManager.ADJUST_MODE_NORMAL);
     }
 
+    /**
+      * @see AudioManager#adjustVolume(int, int)
+      * This method is redirected from AudioManager to AudioService for API hardening rules
+      * enforcement then to MediaSession for implementation.
+      */
+    @Override
+    public void adjustVolume(int direction, int flags) {
+        if (mHardeningEnforcer.blockVolumeMethod(
+                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) {
+            return;
+        }
+        getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
+                    direction, flags);
+    }
+
+    /**
+     * @see AudioManager#adjustSuggestedStreamVolume(int, int, int)
+     * This method is redirected from AudioManager to AudioService for API hardening rules
+     * enforcement then to MediaSession for implementation.
+     */
+    @Override
+    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
+        if (mHardeningEnforcer.blockVolumeMethod(
+                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) {
+            return;
+        }
+        getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags);
+    }
+
     /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
     @Override
     public void setStreamVolumeForUid(int streamType, int index, int flags,
@@ -7390,6 +7466,8 @@
         Objects.requireNonNull(device);
         AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
 
+        device = retrieveBluetoothAddress(device);
+
         sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
                 + AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
                 + device.getAddress() + " behavior:"
@@ -7473,6 +7551,8 @@
         // verify parameters
         Objects.requireNonNull(device);
 
+        device = retrieveBluetoothAddress(device);
+
         return getDeviceVolumeBehaviorInt(device);
     }
 
@@ -7547,9 +7627,12 @@
     /**
      * see AudioManager.setWiredDeviceConnectionState()
      */
-    public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes,
+    public void setWiredDeviceConnectionState(@NonNull AudioDeviceAttributes attributes,
             @ConnectionState int state, String caller) {
         super.setWiredDeviceConnectionState_enforcePermission();
+        Objects.requireNonNull(attributes);
+
+        attributes = retrieveBluetoothAddress(attributes);
 
         if (state != CONNECTION_STATE_CONNECTED
                 && state != CONNECTION_STATE_DISCONNECTED) {
@@ -7590,6 +7673,9 @@
             boolean connected) {
         Objects.requireNonNull(device);
         enforceModifyAudioRoutingPermission();
+
+        device = retrieveBluetoothAddress(device);
+
         mDeviceBroker.setTestDeviceConnectionState(device,
                 connected ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED);
         // simulate a routing update from native
@@ -10422,6 +10508,103 @@
         mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect);
     }
 
+    private boolean isBluetoothPrividged() {
+        if (!Flags.bluetoothMacAddressAnonymization()) {
+            return true;
+        }
+        return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.BLUETOOTH_CONNECT)
+                || Binder.getCallingUid() == Process.SYSTEM_UID;
+    }
+
+    List<AudioDeviceAttributes> retrieveBluetoothAddresses(List<AudioDeviceAttributes> devices) {
+        if (isBluetoothPrividged()) {
+            return devices;
+        }
+
+        List<AudioDeviceAttributes> checkedDevices = new ArrayList<AudioDeviceAttributes>();
+        for (AudioDeviceAttributes ada : devices) {
+            if (ada == null) {
+                continue;
+            }
+            checkedDevices.add(retrieveBluetoothAddressUncheked(ada));
+        }
+        return checkedDevices;
+    }
+
+    AudioDeviceAttributes retrieveBluetoothAddress(@NonNull AudioDeviceAttributes ada) {
+        if (isBluetoothPrividged()) {
+            return ada;
+        }
+        return retrieveBluetoothAddressUncheked(ada);
+    }
+
+    AudioDeviceAttributes retrieveBluetoothAddressUncheked(@NonNull AudioDeviceAttributes ada) {
+        Objects.requireNonNull(ada);
+        if (AudioSystem.isBluetoothDevice(ada.getInternalType())) {
+            String anonymizedAddress = anonymizeBluetoothAddress(ada.getAddress());
+            for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
+                if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
+                        && (ada.getInternalType() == ads.getInternalDeviceType())
+                        && anonymizedAddress.equals(anonymizeBluetoothAddress(
+                                ads.getDeviceAddress())))) {
+                    continue;
+                }
+                ada.setAddress(ads.getDeviceAddress());
+                break;
+            }
+        }
+        return ada;
+    }
+
+    /**
+     * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
+     * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
+     * @param address Mac address to be anonymized
+     * @return anonymized mac address
+     */
+    static String anonymizeBluetoothAddress(String address) {
+        if (address == null || address.length() != "AA:BB:CC:DD:EE:FF".length()) {
+            return null;
+        }
+        return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
+    }
+
+    private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
+                List<AudioDeviceAttributes> devices) {
+        if (isBluetoothPrividged()) {
+            return devices;
+        }
+        return anonymizeAudioDeviceAttributesListUnchecked(devices);
+    }
+
+    /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
+            List<AudioDeviceAttributes> devices) {
+        List<AudioDeviceAttributes> anonymizedDevices = new ArrayList<AudioDeviceAttributes>();
+        for (AudioDeviceAttributes ada : devices) {
+            anonymizedDevices.add(anonymizeAudioDeviceAttributesUnchecked(ada));
+        }
+        return anonymizedDevices;
+    }
+
+    private AudioDeviceAttributes anonymizeAudioDeviceAttributesUnchecked(
+            AudioDeviceAttributes ada) {
+        if (!AudioSystem.isBluetoothDevice(ada.getInternalType())) {
+            return ada;
+        }
+        AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
+        res.setAddress(anonymizeBluetoothAddress(ada.getAddress()));
+        return res;
+    }
+
+    private AudioDeviceAttributes anonymizeAudioDeviceAttributes(AudioDeviceAttributes ada) {
+        if (isBluetoothPrividged()) {
+            return ada;
+        }
+
+        return anonymizeAudioDeviceAttributesUnchecked(ada);
+    }
+
     //==========================================================================================
 
     // camera sound is forced if any of the resources corresponding to one active SIM
@@ -10469,13 +10652,16 @@
         Objects.requireNonNull(usages);
         Objects.requireNonNull(device);
         enforceModifyAudioRoutingPermission();
+
+        final AudioDeviceAttributes ada = retrieveBluetoothAddress(device);
+
         if (timeOutMs <= 0 || usages.length == 0) {
             throw new IllegalArgumentException("Invalid timeOutMs/usagesToMute");
         }
         Log.i(TAG, "muteAwaitConnection dev:" + device + " timeOutMs:" + timeOutMs
                 + " usages:" + Arrays.toString(usages));
 
-        if (mDeviceBroker.isDeviceConnected(device)) {
+        if (mDeviceBroker.isDeviceConnected(ada)) {
             // not throwing an exception as there could be a race between a connection (server-side,
             // notification of connection in flight) and a mute operation (client-side)
             Log.i(TAG, "muteAwaitConnection ignored, device (" + device + ") already connected");
@@ -10487,12 +10673,19 @@
                         + mMutingExpectedDevice);
                 throw new IllegalStateException("muteAwaitConnection already in progress");
             }
-            mMutingExpectedDevice = device;
+            mMutingExpectedDevice = ada;
             mMutedUsagesAwaitingConnection = usages;
-            mPlaybackMonitor.muteAwaitConnection(usages, device, timeOutMs);
+            mPlaybackMonitor.muteAwaitConnection(usages, ada, timeOutMs);
         }
-        dispatchMuteAwaitConnection(cb -> { try {
-            cb.dispatchOnMutedUntilConnection(device, usages); } catch (RemoteException e) { } });
+        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+            try {
+                AudioDeviceAttributes dev = ada;
+                if (!isPrivileged) {
+                    dev = anonymizeAudioDeviceAttributesUnchecked(ada);
+                }
+                cb.dispatchOnMutedUntilConnection(dev, usages);
+            } catch (RemoteException e) { }
+        });
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
@@ -10501,7 +10694,7 @@
         super.getMutingExpectedDevice_enforcePermission();
 
         synchronized (mMuteAwaitConnectionLock) {
-            return mMutingExpectedDevice;
+            return anonymizeAudioDeviceAttributes(mMutingExpectedDevice);
         }
     }
 
@@ -10510,6 +10703,9 @@
     public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device) {
         Objects.requireNonNull(device);
         enforceModifyAudioRoutingPermission();
+
+        final AudioDeviceAttributes ada = retrieveBluetoothAddress(device);
+
         Log.i(TAG, "cancelMuteAwaitConnection for device:" + device);
         final int[] mutedUsages;
         synchronized (mMuteAwaitConnectionLock) {
@@ -10519,7 +10715,7 @@
                 Log.i(TAG, "cancelMuteAwaitConnection ignored, no expected device");
                 return;
             }
-            if (!device.equalTypeAddress(mMutingExpectedDevice)) {
+            if (!ada.equalTypeAddress(mMutingExpectedDevice)) {
                 Log.e(TAG, "cancelMuteAwaitConnection ignored, got " + device
                         + "] but expected device is" + mMutingExpectedDevice);
                 throw new IllegalStateException("cancelMuteAwaitConnection for wrong device");
@@ -10529,8 +10725,14 @@
             mMutedUsagesAwaitingConnection = null;
             mPlaybackMonitor.cancelMuteAwaitConnection("cancelMuteAwaitConnection dev:" + device);
         }
-        dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
-                    AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, device, mutedUsages);
+        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+            try {
+                AudioDeviceAttributes dev = ada;
+                if (!isPrivileged) {
+                    dev = anonymizeAudioDeviceAttributesUnchecked(ada);
+                }
+                cb.dispatchOnUnmutedEvent(
+                        AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, dev, mutedUsages);
             } catch (RemoteException e) { } });
     }
 
@@ -10544,7 +10746,7 @@
         super.registerMuteAwaitConnectionDispatcher_enforcePermission();
 
         if (register) {
-            mMuteAwaitConnectionDispatchers.register(cb);
+            mMuteAwaitConnectionDispatchers.register(cb, isBluetoothPrividged());
         } else {
             mMuteAwaitConnectionDispatchers.unregister(cb);
         }
@@ -10568,8 +10770,14 @@
             mPlaybackMonitor.cancelMuteAwaitConnection(
                     "checkMuteAwaitConnection device " + device + " connected, unmuting");
         }
-        dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
-                AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION, device, mutedUsages);
+        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+            try {
+                AudioDeviceAttributes ada = device;
+                if (!isPrivileged) {
+                    ada = anonymizeAudioDeviceAttributesUnchecked(device);
+                }
+                cb.dispatchOnUnmutedEvent(AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION,
+                        ada, mutedUsages);
             } catch (RemoteException e) { } });
     }
 
@@ -10589,7 +10797,8 @@
             mMutingExpectedDevice = null;
             mMutedUsagesAwaitingConnection = null;
         }
-        dispatchMuteAwaitConnection(cb -> { try {
+        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+            try {
                 cb.dispatchOnUnmutedEvent(
                         AudioManager.MuteAwaitConnectionCallback.EVENT_TIMEOUT,
                         timedOutDevice, mutedUsages);
@@ -10597,13 +10806,14 @@
     }
 
     private void dispatchMuteAwaitConnection(
-            java.util.function.Consumer<IMuteAwaitConnectionCallback> callback) {
+            java.util.function.BiConsumer<IMuteAwaitConnectionCallback, Boolean> callback) {
         final int nbDispatchers = mMuteAwaitConnectionDispatchers.beginBroadcast();
         // lazy initialization as errors unlikely
         ArrayList<IMuteAwaitConnectionCallback> errorList = null;
         for (int i = 0; i < nbDispatchers; i++) {
             try {
-                callback.accept(mMuteAwaitConnectionDispatchers.getBroadcastItem(i));
+                callback.accept(mMuteAwaitConnectionDispatchers.getBroadcastItem(i),
+                        (Boolean) mMuteAwaitConnectionDispatchers.getBroadcastCookie(i));
             } catch (Exception e) {
                 if (errorList == null) {
                     errorList = new ArrayList<>(1);
@@ -13243,6 +13453,9 @@
             @NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
         Objects.requireNonNull(device, "device must not be null");
         enforceModifyAudioRoutingPermission();
+
+        device = retrieveBluetoothAddress(device);
+
         final String getterKey = "additional_output_device_delay="
                 + device.getInternalType() + "," + device.getAddress(); // "getter" key as an id.
         final String setterKey = getterKey + "," + delayMillis;     // append the delay for setter
@@ -13263,6 +13476,9 @@
     @IntRange(from = 0)
     public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
         Objects.requireNonNull(device, "device must not be null");
+
+        device = retrieveBluetoothAddress(device);
+
         final String key = "additional_output_device_delay";
         final String reply = AudioSystem.getParameters(
                 key + "=" + device.getInternalType() + "," + device.getAddress());
@@ -13290,6 +13506,9 @@
     @IntRange(from = 0)
     public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
         Objects.requireNonNull(device, "device must not be null");
+
+        device = retrieveBluetoothAddress(device);
+
         final String key = "max_additional_output_device_delay";
         final String reply = AudioSystem.getParameters(
                 key + "=" + device.getInternalType() + "," + device.getAddress());
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 6af409e..7d7e6d0 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -44,7 +44,9 @@
 
 import java.io.PrintWriter;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -66,6 +68,8 @@
 
     // Bluetooth headset device
     private @Nullable BluetoothDevice mBluetoothHeadsetDevice;
+    private final Map<BluetoothDevice, AudioDeviceAttributes> mResolvedScoAudioDevices =
+            new HashMap<>();
 
     private @Nullable BluetoothHearingAid mHearingAid;
 
@@ -590,7 +594,16 @@
         if (mBluetoothHeadsetDevice == null) {
             return null;
         }
-        return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
+        return getHeadsetAudioDevice(mBluetoothHeadsetDevice);
+    }
+
+    private @NonNull AudioDeviceAttributes getHeadsetAudioDevice(BluetoothDevice btDevice) {
+        AudioDeviceAttributes deviceAttr = mResolvedScoAudioDevices.get(btDevice);
+        if (deviceAttr != null) {
+            // Returns the cached device attributes so that it is consistent as the previous one.
+            return deviceAttr;
+        }
+        return btHeadsetDeviceToAudioDevice(btDevice);
     }
 
     private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
@@ -628,7 +641,7 @@
             return true;
         }
         int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-        AudioDeviceAttributes audioDevice =  btHeadsetDeviceToAudioDevice(btDevice);
+        AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
         boolean result = false;
         if (isActive) {
             result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
@@ -648,6 +661,13 @@
         result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
                         inDevice, audioDevice.getAddress(), audioDevice.getName()),
                 isActive, btDevice) && result;
+        if (result) {
+            if (isActive) {
+                mResolvedScoAudioDevices.put(btDevice, audioDevice);
+            } else {
+                mResolvedScoAudioDevices.remove(btDevice);
+            }
+        }
         return result;
     }
 
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
new file mode 100644
index 0000000..c7556da
--- /dev/null
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2023 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.audio;
+
+import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Class to encapsulate all audio API hardening operations
+ */
+public class HardeningEnforcer {
+
+    private static final String TAG = "AS.HardeningEnforcer";
+
+    final Context mContext;
+    final boolean mIsAutomotive;
+
+    /**
+     * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)}
+     */
+    public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100;
+    /**
+     * Matches calls from {@link AudioManager#adjustVolume(int, int)}
+     */
+    public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101;
+    /**
+     * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)}
+     */
+    public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102;
+    /**
+     * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)}
+     */
+    public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103;
+    /**
+     * Matches calls from {@link AudioManager#setRingerMode(int)}
+     */
+    public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200;
+
+    public HardeningEnforcer(Context ctxt, boolean isAutomotive) {
+        mContext = ctxt;
+        mIsAutomotive = isAutomotive;
+    }
+
+    /**
+     * Checks whether the call in the current thread should be allowed or blocked
+     * @param volumeMethod name of the method to check, for logging purposes
+     * @return false if the method call is allowed, true if it should be a no-op
+     */
+    protected boolean blockVolumeMethod(int volumeMethod) {
+        // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED
+        if (mIsAutomotive) {
+            if (!autoPublicVolumeApiHardening()) {
+                // automotive hardening flag disabled, no blocking on auto
+                return false;
+            }
+            if (mContext.checkCallingOrSelfPermission(
+                    Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+            if (Binder.getCallingUid() < UserHandle.AID_APP_START) {
+                return false;
+            }
+            // TODO metrics?
+            // TODO log for audio dumpsys?
+            Log.e(TAG, "Preventing volume method " + volumeMethod + " for "
+                    + getPackNameForUid(Binder.getCallingUid()));
+            return true;
+        }
+        // not blocking
+        return false;
+    }
+
+    private String getPackNameForUid(int uid) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final String[] names = mContext.getPackageManager().getPackagesForUid(uid);
+            if (names == null
+                    || names.length == 0
+                    || TextUtils.isEmpty(names[0])) {
+                return "[" + uid + "]";
+            }
+            return names[0];
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 1760bb3..4538cad 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -41,7 +41,6 @@
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IAuthService;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptStatusListener;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 import android.hardware.biometrics.IInvalidationCallback;
@@ -358,18 +357,6 @@
         }
 
         @Override
-        public void registerBiometricPromptStatusListener(
-                IBiometricPromptStatusListener listener) throws RemoteException {
-            checkInternalPermission();
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                mBiometricService.registerBiometricPromptStatusListener(listener);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
         public void invalidateAuthenticatorIds(int userId, int fromSensorId,
                 IInvalidationCallback callback) throws RemoteException {
             checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 9569f23..1898b80 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -41,7 +41,6 @@
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptStatusListener;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -89,7 +88,6 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Supplier;
 
@@ -107,8 +105,6 @@
     @VisibleForTesting
     final SettingObserver mSettingObserver;
     private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
-    private final ConcurrentLinkedQueue<BiometricPromptStatusListener>
-            mBiometricPromptStatusListeners;
     private final Random mRandom = new Random();
     @NonNull private final Supplier<Long> mRequestCounter;
     @NonNull private final BiometricContext mBiometricContext;
@@ -429,42 +425,6 @@
         }
     }
 
-    final class BiometricPromptStatusListener implements IBinder.DeathRecipient {
-        private final IBiometricPromptStatusListener mBiometricPromptStatusListener;
-
-        BiometricPromptStatusListener(IBiometricPromptStatusListener callback) {
-            mBiometricPromptStatusListener = callback;
-        }
-
-        void notifyBiometricPromptShowing() {
-            try {
-                mBiometricPromptStatusListener.onBiometricPromptShowing();
-            } catch (DeadObjectException e) {
-                Slog.w(TAG, "Death while invoking notifyHandleAuthenticate", e);
-                mBiometricPromptStatusListeners.remove(this);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to invoke notifyHandleAuthenticate", e);
-            }
-        }
-
-        void notifyBiometricPromptIdle() {
-            try {
-                mBiometricPromptStatusListener.onBiometricPromptIdle();
-            } catch (DeadObjectException e) {
-                Slog.w(TAG, "Death while invoking notifyDialogDismissed", e);
-                mBiometricPromptStatusListeners.remove(this);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to invoke notifyDialogDismissed", e);
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            Slog.e(TAG, "Biometric prompt callback binder died");
-            mBiometricPromptStatusListeners.remove(this);
-        }
-    }
-
     // Receives events from individual biometric sensors.
     private IBiometricSensorReceiver createBiometricSensorReceiver(final long requestId) {
         return new IBiometricSensorReceiver.Stub() {
@@ -745,22 +705,6 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override // Binder call
-        public void registerBiometricPromptStatusListener(IBiometricPromptStatusListener callback) {
-            super.registerBiometricPromptStatusListener_enforcePermission();
-
-            BiometricPromptStatusListener biometricPromptStatusListener =
-                    new BiometricPromptStatusListener(callback);
-            mBiometricPromptStatusListeners.add(biometricPromptStatusListener);
-
-            if (mAuthSession != null) {
-                biometricPromptStatusListener.notifyBiometricPromptShowing();
-            } else {
-                biometricPromptStatusListener.notifyBiometricPromptIdle();
-            }
-        }
-
-        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
-        @Override // Binder call
         public void invalidateAuthenticatorIds(int userId, int fromSensorId,
                 IInvalidationCallback callback) {
 
@@ -1100,7 +1044,6 @@
         mDevicePolicyManager = mInjector.getDevicePolicyManager(context);
         mImpl = new BiometricServiceWrapper();
         mEnabledOnKeyguardCallbacks = new ArrayList<>();
-        mBiometricPromptStatusListeners = new ConcurrentLinkedQueue<>();
         mSettingObserver = mInjector.getSettingObserver(context, mHandler,
                 mEnabledOnKeyguardCallbacks);
         mRequestCounter = mInjector.getRequestGenerator();
@@ -1215,7 +1158,6 @@
             if (finished) {
                 Slog.d(TAG, "handleOnError: AuthSession finished");
                 mAuthSession = null;
-                notifyAuthSessionChanged();
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "RemoteException", e);
@@ -1244,7 +1186,6 @@
 
         session.onDialogDismissed(reason, credentialAttestation);
         mAuthSession = null;
-        notifyAuthSessionChanged();
     }
 
     private void handleOnTryAgainPressed(long requestId) {
@@ -1294,7 +1235,6 @@
         final boolean finished = session.onClientDied();
         if (finished) {
             mAuthSession = null;
-            notifyAuthSessionChanged();
         }
     }
 
@@ -1409,16 +1349,6 @@
         });
     }
 
-    private void notifyAuthSessionChanged() {
-        for (BiometricPromptStatusListener listener : mBiometricPromptStatusListeners) {
-            if (mAuthSession == null) {
-                listener.notifyBiometricPromptIdle();
-            } else {
-                listener.notifyBiometricPromptShowing();
-            }
-        }
-    }
-
     /**
      * handleAuthenticate() (above) which is called from BiometricPrompt determines which
      * modality/modalities to start authenticating with. authenticateInternal() should only be
@@ -1456,7 +1386,6 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "RemoteException", e);
         }
-        notifyAuthSessionChanged();
     }
 
     private void handleCancelAuthentication(long requestId) {
@@ -1471,7 +1400,6 @@
         if (finished) {
             Slog.d(TAG, "handleCancelAuthentication: AuthSession finished");
             mAuthSession = null;
-            notifyAuthSessionChanged();
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index c29d9bd..f085647 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -108,7 +108,7 @@
         final boolean credentialRequested = Utils.isCredentialRequested(promptInfo);
 
         final boolean credentialAvailable = trustManager.isDeviceSecure(userId,
-                context.getAssociatedDisplayId());
+                context.getDeviceId());
 
         // Assuming that biometric authenticators are listed in priority-order, the rest of this
         // function will attempt to find the first authenticator that's as strong or stronger than
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 76dde54..e3c0cf7 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -646,7 +646,7 @@
                             intendingUid,
                             intendingUserId,
                             intendingDeviceId)
-                    || isDeviceLocked(intendingUserId)) {
+                    || isDeviceLocked(intendingUserId, deviceId)) {
                 return null;
             }
             synchronized (mLock) {
@@ -686,7 +686,7 @@
                             intendingUserId,
                             intendingDeviceId,
                             false)
-                    || isDeviceLocked(intendingUserId)) {
+                    || isDeviceLocked(intendingUserId, deviceId)) {
                 return null;
             }
             synchronized (mLock) {
@@ -710,7 +710,7 @@
                             intendingUserId,
                             intendingDeviceId,
                             false)
-                    || isDeviceLocked(intendingUserId)) {
+                    || isDeviceLocked(intendingUserId, deviceId)) {
                 return false;
             }
             synchronized (mLock) {
@@ -785,7 +785,7 @@
                             intendingUserId,
                             intendingDeviceId,
                             false)
-                    || isDeviceLocked(intendingUserId)) {
+                    || isDeviceLocked(intendingUserId, deviceId)) {
                 return false;
             }
             synchronized (mLock) {
@@ -814,7 +814,7 @@
                             intendingUserId,
                             intendingDeviceId,
                             false)
-                    || isDeviceLocked(intendingUserId)) {
+                    || isDeviceLocked(intendingUserId, deviceId)) {
                 return null;
             }
             synchronized (mLock) {
@@ -1150,12 +1150,12 @@
                 && text.equals(clipboard.primaryClip.getItemAt(0).getText());
     }
 
-    private boolean isDeviceLocked(@UserIdInt int userId) {
+    private boolean isDeviceLocked(@UserIdInt int userId, int deviceId) {
         final long token = Binder.clearCallingIdentity();
         try {
             final KeyguardManager keyguardManager = getContext().getSystemService(
                     KeyguardManager.class);
-            return keyguardManager != null && keyguardManager.isDeviceLocked(userId);
+            return keyguardManager != null && keyguardManager.isDeviceLocked(userId, deviceId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/core/java/com/android/server/compat/overrides/OWNERS b/services/core/java/com/android/server/compat/overrides/OWNERS
index b80f340..6ca7803 100644
--- a/services/core/java/com/android/server/compat/overrides/OWNERS
+++ b/services/core/java/com/android/server/compat/overrides/OWNERS
@@ -1,2 +1,2 @@
-tomnatan@google.com
+mcarli@google.com
 mariiasand@google.com
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 64b17e5..84be521 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -588,8 +588,7 @@
             return wakeLockName;
         }
         return (wakeLockName = target.provider
-                + "/" + target.account.type
-                + "/" + target.account.name);
+                + "/" + target.account.type);
     }
 
     // TODO: Test this to make sure that casting to object doesn't lose the type info for EventLog.
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
index 3581981..58a654a 100644
--- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -28,6 +28,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.display.utils.DebugUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -50,7 +51,10 @@
 public class AmbientBrightnessStatsTracker {
 
     private static final String TAG = "AmbientBrightnessStatsTracker";
-    private static final boolean DEBUG = false;
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.AmbientBrightnessStatsTracker DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     @VisibleForTesting
     static final float[] BUCKET_BOUNDARIES_FOR_NEW_STATS =
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index 59844e1..bba5ba3 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -38,6 +38,7 @@
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.utils.DebugUtils;
 import com.android.server.display.utils.DeviceConfigParsingUtils;
 
 import java.io.PrintWriter;
@@ -58,8 +59,10 @@
 @Deprecated
 class BrightnessThrottler {
     private static final String TAG = "BrightnessThrottler";
-    private static final boolean DEBUG = false;
 
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.BrightnessThrottler DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
     private static final int THROTTLING_INVALID = -1;
 
     private final Injector mInjector;
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index d550650..ac5dd20 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -64,6 +64,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.LocalServices;
+import com.android.server.display.utils.DebugUtils;
 
 import libcore.io.IoUtils;
 
@@ -91,8 +92,10 @@
 public class BrightnessTracker {
 
     static final String TAG = "BrightnessTracker";
-    static final boolean DEBUG = false;
 
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.BrightnessTracker DEBUG && adb reboot'
+    static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
     private static final String EVENTS_FILE = "brightness_events.xml";
     private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
     private static final int MAX_EVENTS = 100;
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 0d6635d..3de188f 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -42,6 +42,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.display.utils.DebugUtils;
 import com.android.server.policy.WindowManagerPolicy;
 
 import libcore.io.Streams;
@@ -66,7 +67,9 @@
 final class ColorFade {
     private static final String TAG = "ColorFade";
 
-    private static final boolean DEBUG = false;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.ColorFade DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     // The layer for the electron beam surface.
     // This is currently hardcoded to be one layer above the boot animation.
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index cfbe0c6..a0beedb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -77,6 +77,7 @@
 import com.android.server.display.config.ThresholdPoint;
 import com.android.server.display.config.UsiVersion;
 import com.android.server.display.config.XmlParser;
+import com.android.server.display.utils.DebugUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -519,7 +520,10 @@
  */
 public class DisplayDeviceConfig {
     private static final String TAG = "DisplayDeviceConfig";
-    private static final boolean DEBUG = false;
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayDeviceConfig DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN;
 
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index ea52a3d..67e612d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -24,6 +24,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.display.utils.DebugUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -39,7 +40,10 @@
  */
 class DisplayDeviceRepository implements DisplayAdapter.Listener {
     private static final String TAG = "DisplayDeviceRepository";
-    private static final Boolean DEBUG = false;
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayDeviceRepository DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
     public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 57b2c24..087cf20 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -161,6 +161,7 @@
 import com.android.server.display.layout.Layout;
 import com.android.server.display.mode.DisplayModeDirector;
 import com.android.server.display.notifications.DisplayNotificationManager;
+import com.android.server.display.utils.DebugUtils;
 import com.android.server.display.utils.SensorUtils;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.utils.FoldSettingProvider;
@@ -225,7 +226,10 @@
  */
 public final class DisplayManagerService extends SystemService {
     private static final String TAG = "DisplayManagerService";
-    private static final boolean DEBUG = false;
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayManagerService DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     // When this system property is set to 0, WFD is forcibly disabled on boot.
     // When this system property is set to 1, WFD is forcibly enabled on boot.
@@ -586,8 +590,7 @@
         mSystemReady = false;
         mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
         mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
-        // TODO: b/306170135 - return TextUtils package name check instead
-        mExtraDisplayEventLogging = true;
+        mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
     }
 
     public void setupSchedulerPolicies() {
@@ -3044,8 +3047,7 @@
     }
 
     private boolean extraLogging(String packageName) {
-        // TODO: b/306170135 - return mExtraDisplayLoggingPackageName & package name check instead
-        return true;
+        return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
     }
 
     // Runs on Handler thread.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ce98559..915f5db 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -79,6 +79,7 @@
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
+import com.android.server.display.utils.DebugUtils;
 import com.android.server.display.utils.SensorUtils;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
@@ -115,7 +116,11 @@
     private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
     private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
 
-    private static final boolean DEBUG = false;
+    private static final String TAG = "DisplayPowerController";
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayPowerController DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+
     private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
 
     // If true, uses the color fade on animation.
@@ -608,7 +613,7 @@
         mClock = mInjector.getClock();
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
-        mTag = "DisplayPowerController[" + mDisplayId + "]";
+        mTag = TAG + "[" + mDisplayId + "]";
         mHighBrightnessModeMetadata = hbmMetadata;
         mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
         mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0f00027..fc596dc 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -82,6 +82,7 @@
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
 import com.android.server.display.state.DisplayStateController;
+import com.android.server.display.utils.DebugUtils;
 import com.android.server.display.utils.SensorUtils;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
@@ -118,8 +119,10 @@
     private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
     private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
 
-    private static final boolean DEBUG = false;
-
+    private static final String TAG = "DisplayPowerController2";
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     // If true, uses the color fade on animation.
     // We might want to turn this off if we cannot get a guarantee that the screen
@@ -503,7 +506,7 @@
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
                 () -> updatePowerState(), mDisplayId, mSensorManager);
         mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
-        mTag = "DisplayPowerController2[" + mDisplayId + "]";
+        mTag = TAG + "[" + mDisplayId + "]";
         mThermalBrightnessThrottlingDataId =
                 logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 2c257a1..be03a80 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,6 +26,8 @@
 import android.view.Choreographer;
 import android.view.Display;
 
+import com.android.server.display.utils.DebugUtils;
+
 import java.io.PrintWriter;
 
 /**
@@ -48,7 +50,9 @@
 final class DisplayPowerState {
     private static final String TAG = "DisplayPowerState";
 
-    private static final boolean DEBUG = false;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayPowerState DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
     private static String COUNTER_COLOR_FADE = "ColorFadeLevel";
 
     private final Handler mHandler;
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 39172b8..a9f78fd 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -38,6 +38,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
 import com.android.server.display.DisplayManagerService.Clock;
+import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
@@ -54,7 +55,9 @@
 class HighBrightnessModeController {
     private static final String TAG = "HighBrightnessModeController";
 
-    private static final boolean DEBUG = false;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.HighBrightnessModeController DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     @VisibleForTesting
     static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 3c522e7..0521b8a 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,6 +18,8 @@
 
 import android.util.Slog;
 
+import com.android.server.display.utils.DebugUtils;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 
@@ -27,7 +29,9 @@
 public class HysteresisLevels {
     private static final String TAG = "HysteresisLevels";
 
-    private static final boolean DEBUG = false;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.HysteresisLevels DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     private final float[] mBrighteningThresholdsPercentages;
     private final float[] mDarkeningThresholdsPercentages;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index c55bc62..f3425d2e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -43,6 +43,7 @@
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.DisplayIdProducer;
 import com.android.server.display.layout.Layout;
+import com.android.server.display.utils.DebugUtils;
 import com.android.server.utils.FoldSettingProvider;
 
 import java.io.PrintWriter;
@@ -63,7 +64,9 @@
 class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
     private static final String TAG = "LogicalDisplayMapper";
 
-    private static final boolean DEBUG = false;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.LogicalDisplayMapper DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
     public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index cd3a453..3fd58e8 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -35,6 +35,7 @@
 import android.widget.TextView;
 
 import com.android.internal.util.DumpUtils;
+import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
 
@@ -47,8 +48,10 @@
  */
 final class OverlayDisplayWindow implements DumpUtils.Dump {
     private static final String TAG = "OverlayDisplayWindow";
-    private static final boolean DEBUG = false;
 
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.OverlayDisplayWindow DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
     private final float INITIAL_SCALE = 0.5f;
     private final float MIN_SCALE = 0.3f;
     private final float MAX_SCALE = 1.0f;
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 1e13974..7bc7971 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -21,6 +21,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -41,7 +42,11 @@
     @VisibleForTesting
     static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
 
-    private static final boolean DEBUG = false;
+    private static final String TAG = "WakelockController";
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.WakelockController DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     @IntDef(flag = true, prefix = "WAKE_LOCK_", value = {
             WAKE_LOCK_PROXIMITY_POSITIVE,
@@ -100,7 +105,7 @@
     public WakelockController(int displayId,
             DisplayManagerInternal.DisplayPowerCallbacks callbacks) {
         mDisplayId = displayId;
-        mTag = "WakelockController[" + mDisplayId + "]";
+        mTag = TAG + "[" + mDisplayId + "]";
         mDisplayPowerCallbacks = callbacks;
         mSuspendBlockerIdUnfinishedBusiness = "[" + displayId + "]unfinished business";
         mSuspendBlockerIdOnStateChanged = "[" + displayId + "]on state changed";
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index e3d38e7..7660cf8 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -65,7 +66,9 @@
 final class WifiDisplayAdapter extends DisplayAdapter {
     private static final String TAG = "WifiDisplayAdapter";
 
-    private static final boolean DEBUG = false;
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.WifiDisplayAdapter DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1;
 
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 955b8d9..873598a 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -45,6 +45,7 @@
 import android.view.Surface;
 
 import com.android.internal.util.DumpUtils;
+import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
 import java.net.Inet4Address;
@@ -69,7 +70,10 @@
  */
 final class WifiDisplayController implements DumpUtils.Dump {
     private static final String TAG = "WifiDisplayController";
-    private static final boolean DEBUG = false;
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.WifiDisplayController DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     private static final int DEFAULT_CONTROL_PORT = 7236;
     private static final int MAX_THROUGHPUT = 50;
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index d953e8e..7f3ea6a 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -21,6 +21,7 @@
 import android.util.Slog;
 
 import com.android.server.display.feature.flags.Flags;
+import com.android.server.display.utils.DebugUtils;
 
 import java.util.function.Supplier;
 
@@ -28,9 +29,13 @@
  * Utility class to read the flags used in the display manager server.
  */
 public class DisplayManagerFlags {
-    private static final boolean DEBUG = false;
     private static final String TAG = "DisplayManagerFlags";
 
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayManagerFlags DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+
+
     private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
             Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
             Flags::enableConnectedDisplayManagement);
diff --git a/services/core/java/com/android/server/display/utils/DebugUtils.java b/services/core/java/com/android/server/display/utils/DebugUtils.java
new file mode 100644
index 0000000..1496495
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/DebugUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 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.display.utils;
+
+import android.util.Log;
+
+public class DebugUtils {
+
+    public static final boolean DEBUG_ALL = Log.isLoggable("DisplayManager_All", Log.DEBUG);
+
+    /**
+     * Returns whether the specified tag has logging enabled. Use the tag name specified in the
+     * calling class, or DisplayManager_All to globally enable all tags in display.
+     * To enable:
+     * adb shell setprop persist.log.tag.DisplayManager_All DEBUG
+     * To disable:
+     * adb shell setprop persist.log.tag.DisplayManager_All \"\"
+     */
+    public static boolean isDebuggable(String tag) {
+        return Log.isLoggable(tag, Log.DEBUG) || DEBUG_ALL;
+    }
+}
diff --git a/services/core/java/com/android/server/feature/Android.bp b/services/core/java/com/android/server/feature/Android.bp
new file mode 100644
index 0000000..067288d
--- /dev/null
+++ b/services/core/java/com/android/server/feature/Android.bp
@@ -0,0 +1,12 @@
+aconfig_declarations {
+    name: "dropbox_flags",
+    package: "com.android.server.feature.flags",
+    srcs: [
+        "dropbox_flags.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "dropbox_flags_lib",
+    aconfig_declarations: "dropbox_flags",
+}
diff --git a/services/core/java/com/android/server/feature/dropbox_flags.aconfig b/services/core/java/com/android/server/feature/dropbox_flags.aconfig
new file mode 100644
index 0000000..fee4bf3
--- /dev/null
+++ b/services/core/java/com/android/server/feature/dropbox_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.feature.flags"
+
+flag{
+    name: "enable_read_dropbox_permission"
+    namespace: "preload_safety"
+    description: "Feature flag for permission to Read dropbox data"
+    bug: "287512663"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f35b045..568618e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -697,9 +697,9 @@
             return;
         }
 
-        if (isUserKeyUnlocked(userId)) {
-            // If storage is not locked, the user will be automatically unlocked so there is
-            // no need to show the notification.
+        if (isCeStorageUnlocked(userId)) {
+            // If the user's CE storage is already unlocked, then the user will be automatically
+            // unlocked, so there is no need to show the notification.
             return;
         }
 
@@ -1030,8 +1030,8 @@
             // they did have an SP then their CE key wasn't encrypted by it.
             //
             // If this gets interrupted (e.g. by the device powering off), there shouldn't be a
-            // problem since this will run again on the next boot, and setUserKeyProtection() is
-            // okay with the key being already protected by the given secret.
+            // problem since this will run again on the next boot, and setCeStorageProtection() is
+            // okay with the CE key being already protected by the given secret.
             if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
                 for (UserInfo user : mUserManager.getAliveUsers()) {
                     removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
@@ -1066,7 +1066,7 @@
                 Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
                 return;
             }
-            setUserKeyProtection(userId, result.syntheticPassword);
+            setCeStorageProtection(userId, result.syntheticPassword);
         }
     }
 
@@ -2005,11 +2005,11 @@
         mStorage.writeChildProfileLock(profileUserId, ArrayUtils.concat(iv, ciphertext));
     }
 
-    private void setUserKeyProtection(@UserIdInt int userId, SyntheticPassword sp) {
+    private void setCeStorageProtection(@UserIdInt int userId, SyntheticPassword sp) {
         final byte[] secret = sp.deriveFileBasedEncryptionKey();
         final long callingId = Binder.clearCallingIdentity();
         try {
-            mStorageManager.setUserKeyProtection(userId, secret);
+            mStorageManager.setCeStorageProtection(userId, secret);
         } catch (RemoteException e) {
             throw new IllegalStateException("Failed to protect CE key for user " + userId, e);
         } finally {
@@ -2017,11 +2017,11 @@
         }
     }
 
-    private boolean isUserKeyUnlocked(int userId) {
+    private boolean isCeStorageUnlocked(int userId) {
         try {
-            return mStorageManager.isUserKeyUnlocked(userId);
+            return mStorageManager.isCeStorageUnlocked(userId);
         } catch (RemoteException e) {
-            Slog.e(TAG, "failed to check user key locked state", e);
+            Slog.e(TAG, "Error checking whether CE storage is unlocked", e);
             return false;
         }
     }
@@ -2032,8 +2032,8 @@
      * This method doesn't throw exceptions because it is called opportunistically whenever a user
      * is started.  Whether it worked or not can be detected by whether the key got unlocked or not.
      */
-    private void unlockUserKey(@UserIdInt int userId, SyntheticPassword sp) {
-        if (isUserKeyUnlocked(userId)) {
+    private void unlockCeStorage(@UserIdInt int userId, SyntheticPassword sp) {
+        if (isCeStorageUnlocked(userId)) {
             Slogf.d(TAG, "CE storage for user %d is already unlocked", userId);
             return;
         }
@@ -2041,7 +2041,7 @@
         final String userType = isUserSecure(userId) ? "secured" : "unsecured";
         final byte[] secret = sp.deriveFileBasedEncryptionKey();
         try {
-            mStorageManager.unlockUserKey(userId, userInfo.serialNumber, secret);
+            mStorageManager.unlockCeStorage(userId, userInfo.serialNumber, secret);
             Slogf.i(TAG, "Unlocked CE storage for %s user %d", userType, userId);
         } catch (RemoteException e) {
             Slogf.wtf(TAG, e, "Failed to unlock CE storage for %s user %d", userType, userId);
@@ -2054,8 +2054,10 @@
     public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
         checkPasswordReadPermission();
         synchronized (mSpManager) {
-            if (isUserKeyUnlocked(userId)) {
+            if (isCeStorageUnlocked(userId)) {
                 Slogf.d(TAG, "CE storage for user %d is already unlocked", userId);
+                // This method actually does more than unlock CE storage.  However, if CE storage is
+                // already unlocked, then the other parts must have already been done too.
                 return;
             }
             if (isUserSecure(userId)) {
@@ -2072,7 +2074,7 @@
                 return;
             }
             onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
-            unlockUserKey(userId, result.syntheticPassword);
+            unlockCeStorage(userId, result.syntheticPassword);
         }
     }
 
@@ -2775,7 +2777,7 @@
             final long protectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(),
                     LockscreenCredential.createNone(), sp, userId);
             setCurrentLskfBasedProtectorId(protectorId, userId);
-            setUserKeyProtection(userId, sp);
+            setCeStorageProtection(userId, sp);
             onSyntheticPasswordCreated(userId, sp);
             Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
             return sp;
@@ -2836,7 +2838,7 @@
 
         unlockKeystore(userId, sp);
 
-        unlockUserKey(userId, sp);
+        unlockCeStorage(userId, sp);
 
         unlockUser(userId);
 
@@ -2900,7 +2902,7 @@
 
             mSpManager.clearSidForUser(userId);
             gateKeeperClearSecureUserId(userId);
-            unlockUserKey(userId, sp);
+            unlockCeStorage(userId, sp);
             unlockKeystore(userId, sp);
             setKeystorePassword(null, userId);
             removeBiometricsForUser(userId);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 67a1ccd..78077a8 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -40,6 +40,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.media.flags.Flags;
 
 import java.util.List;
 import java.util.Objects;
@@ -358,21 +359,8 @@
 
             RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
 
-            if (mPendingSessionCreationRequest != null) {
-                SessionCreationRequest sessionCreationRequest;
-                synchronized (mRequestLock) {
-                    sessionCreationRequest = mPendingSessionCreationRequest;
-                    mPendingSessionCreationRequest = null;
-                }
-                if (sessionCreationRequest != null) {
-                    if (TextUtils.equals(mSelectedRouteId, sessionCreationRequest.mRouteId)) {
-                        mCallback.onSessionCreated(this,
-                                sessionCreationRequest.mRequestId, newSessionInfo);
-                    } else {
-                        mCallback.onRequestFailed(this, sessionCreationRequest.mRequestId,
-                                MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
-                    }
-                }
+            synchronized (mRequestLock) {
+                reportPendingSessionRequestResultLockedIfNeeded(newSessionInfo);
             }
 
             if (Objects.equals(oldSessionInfo, newSessionInfo)) {
@@ -395,6 +383,59 @@
         }
     }
 
+    @GuardedBy("mRequestLock")
+    private void reportPendingSessionRequestResultLockedIfNeeded(
+            RoutingSessionInfo newSessionInfo) {
+        if (mPendingSessionCreationRequest == null) {
+            // No pending request, nothing to report.
+            return;
+        }
+
+        long pendingRequestId = mPendingSessionCreationRequest.mRequestId;
+        if (TextUtils.equals(mSelectedRouteId, mPendingSessionCreationRequest.mRouteId)) {
+            if (DEBUG) {
+                Slog.w(
+                        TAG,
+                        "Session creation success to route "
+                                + mPendingSessionCreationRequest.mRouteId);
+            }
+            mPendingSessionCreationRequest = null;
+            mCallback.onSessionCreated(this, pendingRequestId, newSessionInfo);
+        } else {
+            boolean isRequestedRouteConnectedBtRoute = isRequestedRouteConnectedBtRoute();
+            if (!Flags.enableWaitingStateForSystemSessionCreationRequest()
+                    || !isRequestedRouteConnectedBtRoute) {
+                if (DEBUG) {
+                    Slog.w(
+                            TAG,
+                            "Session creation failed to route "
+                                    + mPendingSessionCreationRequest.mRouteId);
+                }
+                mPendingSessionCreationRequest = null;
+                mCallback.onRequestFailed(
+                        this, pendingRequestId, MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
+            } else if (DEBUG) {
+                Slog.w(
+                        TAG,
+                        "Session creation waiting state to route "
+                                + mPendingSessionCreationRequest.mRouteId);
+            }
+        }
+    }
+
+    @GuardedBy("mRequestLock")
+    private boolean isRequestedRouteConnectedBtRoute() {
+        // Using AllRoutes instead of TransferableRoutes as BT Stack sends an intermediate update
+        // where two BT routes are active so the transferable routes list is empty.
+        // See b/307723189 for context
+        for (MediaRoute2Info btRoute : mBluetoothRouteController.getAllBluetoothRoutes()) {
+            if (TextUtils.equals(btRoute.getId(), mPendingSessionCreationRequest.mRouteId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void publishProviderState() {
         updateProviderState();
         notifyProviderState();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 58927d1..893ed61 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -470,6 +470,11 @@
     }
 
     @VisibleForTesting
+    void notifyPermissionRequestCancelled(int hostUid) {
+        mMediaProjectionMetricsLogger.logProjectionPermissionRequestCancelled(hostUid);
+    }
+
+    @VisibleForTesting
     void notifyAppSelectorDisplayed(int hostUid) {
         mMediaProjectionMetricsLogger.logAppSelectorDisplayed(hostUid);
     }
@@ -852,19 +857,6 @@
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
-        public void notifyPermissionRequestStateChange(int hostUid, int state,
-                int sessionCreationSource) {
-            notifyPermissionRequestStateChange_enforcePermission();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mMediaProjectionMetricsLogger.notifyProjectionStateChange(hostUid, state, sessionCreationSource);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override // Binder call
-        @EnforcePermission(MANAGE_MEDIA_PROJECTION)
         public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
             notifyPermissionRequestInitiated_enforcePermission();
             final long token = Binder.clearCallingIdentity();
@@ -890,6 +882,18 @@
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+        public void notifyPermissionRequestCancelled(int hostUid) {
+            notifyPermissionRequestCancelled_enforcePermission();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        @EnforcePermission(MANAGE_MEDIA_PROJECTION)
         public void notifyAppSelectorDisplayed(int hostUid) {
             notifyAppSelectorDisplayed_enforcePermission();
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index 55a30bf..d7fefeb 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -118,6 +118,23 @@
     }
 
     /**
+     * Logs that requesting permission for media projection was cancelled by the user.
+     *
+     * @param hostUid UID of the package that initiates MediaProjection.
+     */
+    public void logProjectionPermissionRequestCancelled(int hostUid) {
+        write(
+                mSessionIdGenerator.getCurrentSessionId(),
+                FrameworkStatsLog
+                        .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
+                hostUid,
+                TARGET_UID_UNKNOWN,
+                TIME_SINCE_LAST_ACTIVE_UNKNOWN,
+                FrameworkStatsLog
+                        .MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+    }
+
+    /**
      * Logs that the app selector dialog is shown for the user.
      *
      * @param hostUid UID of the package that initiates MediaProjection.
@@ -174,23 +191,6 @@
         }
     }
 
-    public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
-        write(hostUid, state, sessionCreationSource);
-    }
-
-    private void write(int hostUid, int state, int sessionCreationSource) {
-        mFrameworkStatsLogWrapper.write(
-                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
-                /* session_id */ 123,
-                /* state */ state,
-                /* previous_state */ FrameworkStatsLog
-                        .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
-                /* host_uid */ hostUid,
-                /* target_uid */ -1,
-                /* time_since_last_active */ 0,
-                /* creation_source */ sessionCreationSource);
-    }
-
     private void write(
             int sessionId,
             int state,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7ca5699..4b5d52f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -244,6 +244,7 @@
 import android.permission.PermissionManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.ConversationChannelWrapper;
@@ -2008,6 +2009,8 @@
                         Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
         private final Uri LOCK_SCREEN_SHOW_NOTIFICATIONS
                 = Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+        private final Uri SHOW_NOTIFICATION_SNOOZE
+                = Settings.Secure.getUriFor(Settings.Secure.SHOW_NOTIFICATION_SNOOZE);
 
         SettingsObserver(Handler handler) {
             super(handler);
@@ -2034,6 +2037,10 @@
                     false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS,
                     false, this, UserHandle.USER_ALL);
+
+            resolver.registerContentObserver(SHOW_NOTIFICATION_SNOOZE,
+                    false, this, UserHandle.USER_ALL);
+
             update(null);
         }
 
@@ -2083,6 +2090,14 @@
             if (uri == null || LOCK_SCREEN_SHOW_NOTIFICATIONS.equals(uri)) {
                 mPreferencesHelper.updateLockScreenShowNotifications();
             }
+            if (SHOW_NOTIFICATION_SNOOZE.equals(uri)) {
+                final boolean snoozeEnabled = Settings.Secure.getIntForUser(resolver,
+                        Secure.SHOW_NOTIFICATION_SNOOZE, 0, UserHandle.USER_CURRENT)
+                        != 0;
+                if (!snoozeEnabled) {
+                    unsnoozeAll();
+                }
+            }
         }
     }
 
@@ -7792,6 +7807,13 @@
         }
     }
 
+    private void unsnoozeAll() {
+        synchronized (mNotificationLock) {
+            mSnoozeHelper.repostAll(mUserProfiles.getCurrentProfileIds());
+            handleSavePolicyFile();
+        }
+    }
+
     protected class CancelNotificationRunnable implements Runnable {
         private final int mCallingUid;
         private final int mCallingPid;
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
index 72c4529..9f16662 100644
--- a/services/core/java/com/android/server/notification/OWNERS
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -1,6 +1,9 @@
-# Bug component: 1305560
+# Bug component: 78010
 
 juliacr@google.com
 yurilin@google.com
+aroederer@google.com
+matiashe@google.com
+valiiftime@google.com
 jeffdq@google.com
 dsandler@android.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 0176989..8f5676b3 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -296,6 +296,20 @@
         }
     }
 
+    /**
+     * Unsnooze & repost all snoozed notifications for userId and its profiles
+     */
+    protected void repostAll(IntArray userIds) {
+        synchronized (mLock) {
+            List<NotificationRecord> snoozedNotifications = getSnoozed();
+            for (NotificationRecord r : snoozedNotifications) {
+                if (userIds.binarySearch(r.getUserId()) >= 0) {
+                    repost(r.getKey(), r.getUserId(), false);
+                }
+            }
+        }
+    }
+
     protected void repost(String key, boolean muteOnReturn) {
         synchronized (mLock) {
             final NotificationRecord r = mSnoozedNotifications.get(key);
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 1134714..e57ea0f 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.os;
 
+import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -52,8 +54,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.OptionalInt;
+import java.util.Set;
 
 /**
  * Implementation of the service that provides a privileged API to capture and consume bugreports.
@@ -101,10 +105,12 @@
         private final ArrayMap<Pair<Integer, String>, ArraySet<String>> mBugreportFiles =
                 new ArrayMap<>();
 
+        @GuardedBy("mLock")
+        private final Set<String> mBugreportFilesToPersist = new HashSet<>();
+
         /**
          * Checks that a given file was generated on behalf of the given caller. If the file was
-         * generated on behalf of the caller, it is removed from the bugreport mapping so that it
-         * may not be retrieved again. If the file was not generated on behalf of the caller, an
+         * not generated on behalf of the caller, an
          * {@link IllegalArgumentException} is thrown.
          *
          * @param callingInfo a (uid, package name) pair identifying the caller
@@ -114,35 +120,76 @@
          * @throws IllegalArgumentException if {@code bugreportFile} is not associated with
          *                                  {@code callingInfo}.
          */
+        @RequiresPermission(value = android.Manifest.permission.INTERACT_ACROSS_USERS,
+                conditional = true)
         void ensureCallerPreviouslyGeneratedFile(
-                Pair<Integer, String> callingInfo, String bugreportFile) {
+                Context context, Pair<Integer, String> callingInfo, int userId,
+                String bugreportFile) {
             synchronized (mLock) {
-                ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(callingInfo);
-                if (bugreportFilesForCaller != null
-                        && bugreportFilesForCaller.contains(bugreportFile)) {
-                    bugreportFilesForCaller.remove(bugreportFile);
-                    if (bugreportFilesForCaller.isEmpty()) {
-                        mBugreportFiles.remove(callingInfo);
+                if (onboardingBugreportV2Enabled()) {
+                    final int uidForUser = Binder.withCleanCallingIdentity(() -> {
+                        try {
+                            return context.getPackageManager()
+                                    .getPackageUidAsUser(callingInfo.second, userId);
+                        } catch (PackageManager.NameNotFoundException exception) {
+                            throwInvalidBugreportFileForCallerException(
+                                    bugreportFile, callingInfo.second);
+                            return -1;
+                        }
+                    });
+                    if (uidForUser != callingInfo.first && context.checkCallingOrSelfPermission(
+                            Manifest.permission.INTERACT_ACROSS_USERS)
+                            != PackageManager.PERMISSION_GRANTED) {
+                        throw new SecurityException(
+                                callingInfo.second + " does not hold the "
+                                        + "INTERACT_ACROSS_USERS permission to access "
+                                        + "cross-user bugreports.");
+                    }
+                    ArraySet<String> bugreportFilesForUid = mBugreportFiles.get(
+                            new Pair<>(uidForUser, callingInfo.second));
+                    if (bugreportFilesForUid == null
+                            || !bugreportFilesForUid.contains(bugreportFile)) {
+                        throwInvalidBugreportFileForCallerException(
+                                bugreportFile, callingInfo.second);
                     }
                 } else {
-                    throw new IllegalArgumentException(
-                            "File " + bugreportFile + " was not generated"
-                                    + " on behalf of calling package " + callingInfo.second);
+                    ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(callingInfo);
+                    if (bugreportFilesForCaller != null
+                            && bugreportFilesForCaller.contains(bugreportFile)) {
+                        bugreportFilesForCaller.remove(bugreportFile);
+                        if (bugreportFilesForCaller.isEmpty()) {
+                            mBugreportFiles.remove(callingInfo);
+                        }
+                    } else {
+                        throwInvalidBugreportFileForCallerException(
+                                bugreportFile, callingInfo.second);
+
+                    }
                 }
             }
         }
 
+        private static void throwInvalidBugreportFileForCallerException(
+                String bugreportFile, String packageName) {
+            throw new IllegalArgumentException("File " + bugreportFile + " was not generated on"
+                    + " behalf of calling package " + packageName);
+        }
+
         /**
          * Associates a bugreport file with a caller, which is identified as a
          * (uid, package name) pair.
          */
-        void addBugreportFileForCaller(Pair<Integer, String> caller, String bugreportFile) {
+        void addBugreportFileForCaller(
+                Pair<Integer, String> caller, String bugreportFile, boolean keepOnRetrieval) {
             synchronized (mLock) {
                 if (!mBugreportFiles.containsKey(caller)) {
                     mBugreportFiles.put(caller, new ArraySet<>());
                 }
                 ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(caller);
                 bugreportFilesForCaller.add(bugreportFile);
+                if ((onboardingBugreportV2Enabled()) && keepOnRetrieval) {
+                    mBugreportFilesToPersist.add(bugreportFile);
+                }
             }
         }
     }
@@ -246,16 +293,17 @@
     }
 
     @Override
-    @RequiresPermission(Manifest.permission.DUMP)
-    public void retrieveBugreport(int callingUidUnused, String callingPackage,
-            FileDescriptor bugreportFd, String bugreportFile, IDumpstateListener listener) {
+    @RequiresPermission(value = Manifest.permission.DUMP, conditional = true)
+    public void retrieveBugreport(int callingUidUnused, String callingPackage, int userId,
+            FileDescriptor bugreportFd, String bugreportFile,
+            boolean keepBugreportOnRetrievalUnused, IDumpstateListener listener) {
         int callingUid = Binder.getCallingUid();
         enforcePermission(callingPackage, callingUid, false);
 
         Slogf.i(TAG, "Retrieving bugreport for %s / %d", callingPackage, callingUid);
         try {
             mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
-                    new Pair<>(callingUid, callingPackage), bugreportFile);
+                    mContext, new Pair<>(callingUid, callingPackage), userId, bugreportFile);
         } catch (IllegalArgumentException e) {
             Slog.e(TAG, e.getMessage());
             reportError(listener, IDumpstateListener.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
@@ -281,10 +329,17 @@
             // Wrap the listener so we can intercept binder events directly.
             DumpstateListener myListener = new DumpstateListener(listener, ds,
                     new Pair<>(callingUid, callingPackage), /* reportFinishedFile= */ true);
+
+            boolean keepBugreportOnRetrieval = false;
+            if (onboardingBugreportV2Enabled()) {
+                keepBugreportOnRetrieval = mBugreportFileManager.mBugreportFilesToPersist.contains(
+                        bugreportFile);
+            }
+
             setCurrentDumpstateListenerLocked(myListener);
             try {
-                ds.retrieveBugreport(callingUid, callingPackage, bugreportFd,
-                        bugreportFile, myListener);
+                ds.retrieveBugreport(callingUid, callingPackage, userId, bugreportFd,
+                        bugreportFile, keepBugreportOnRetrieval, myListener);
             } catch (RemoteException e) {
                 Slog.e(TAG, "RemoteException in retrieveBugreport", e);
             }
@@ -317,7 +372,8 @@
     private void validateBugreportFlags(int flags) {
         flags = clearBugreportFlag(flags,
                 BugreportParams.BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
-                        | BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT);
+                        | BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT
+                        | BugreportParams.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL);
         if (flags != 0) {
             Slog.w(TAG, "Unknown bugreport flags: " + flags);
             throw new IllegalArgumentException("Unknown bugreport flags: " + flags);
@@ -482,6 +538,9 @@
         boolean reportFinishedFile =
                 (bugreportFlags & BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT) != 0;
 
+        boolean keepBugreportOnRetrieval =
+                (bugreportFlags & BugreportParams.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL) != 0;
+
         IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
         if (ds == null) {
             Slog.w(TAG, "Unable to get bugreport service");
@@ -490,7 +549,8 @@
         }
 
         DumpstateListener myListener = new DumpstateListener(listener, ds,
-                new Pair<>(callingUid, callingPackage), reportFinishedFile);
+                new Pair<>(callingUid, callingPackage), reportFinishedFile,
+                keepBugreportOnRetrieval);
         setCurrentDumpstateListenerLocked(myListener);
         try {
             ds.startBugreport(callingUid, callingPackage, bugreportFd, screenshotFd, bugreportMode,
@@ -646,9 +706,16 @@
         private final boolean mReportFinishedFile;
         private int mProgress; // used for debugging purposes only
         private boolean mDone;
+        private boolean mKeepBugreportOnRetrieval;
 
         DumpstateListener(IDumpstateListener listener, IDumpstate ds,
                 Pair<Integer, String> caller, boolean reportFinishedFile) {
+            this(listener, ds, caller, reportFinishedFile, /* keepBugreportOnRetrieval= */ false);
+        }
+
+        DumpstateListener(IDumpstateListener listener, IDumpstate ds,
+                Pair<Integer, String> caller, boolean reportFinishedFile,
+                boolean keepBugreportOnRetrieval) {
             if (DEBUG) {
                 Slogf.d(TAG, "Starting DumpstateListener(id=%d) for caller %s", mId, caller);
             }
@@ -656,6 +723,7 @@
             mDs = ds;
             mCaller = caller;
             mReportFinishedFile = reportFinishedFile;
+            mKeepBugreportOnRetrieval = keepBugreportOnRetrieval;
             try {
                 mDs.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -690,7 +758,8 @@
                 reportFinishedLocked("File: " + bugreportFile);
             }
             if (mReportFinishedFile) {
-                mBugreportFileManager.addBugreportFileForCaller(mCaller, bugreportFile);
+                mBugreportFileManager.addBugreportFileForCaller(
+                        mCaller, bugreportFile, mKeepBugreportOnRetrieval);
             } else if (DEBUG) {
                 Slog.d(TAG, "Not reporting finished file");
             }
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index e53c436..ca149c5 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -219,6 +219,7 @@
         case Process.AUDIOSERVER_UID:  // fastcapture, fastmixer
         case Process.CAMERASERVER_UID: // camera high frame rate recording
         case Process.BLUETOOTH_UID:    // Bluetooth audio playback
+        case Process.PHONE_UID:        // phone call
             return true;
         default:
             return false;
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index bd9be30..167c17c 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -121,7 +121,7 @@
                 StorageManagerInternal.class);
         for (UserInfo user : umInternal.getUsers(false /*excludeDying*/)) {
             final int flags;
-            if (StorageManager.isUserKeyUnlocked(user.id)
+            if (StorageManager.isCeStorageUnlocked(user.id)
                     && smInternal.isCeStoragePrepared(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (umInternal.isUserRunning(user.id)) {
@@ -410,7 +410,7 @@
         // First look for stale data that doesn't belong, and check if things
         // have changed since we did our last restorecon
         if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
-            if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+            if (StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId)) {
                 throw new RuntimeException(
                         "Yikes, someone asked us to reconcile CE storage while " + userId
                                 + " was still locked; this would have caused massive data loss!");
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 510c06e..3ed9f02 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3556,7 +3556,7 @@
     @Override
     public int getPackageStartability(boolean safeMode, @NonNull String packageName, int callingUid,
             @UserIdInt int userId) {
-        final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
+        final boolean ceStorageUnlocked = StorageManager.isCeStorageUnlocked(userId);
         final PackageStateInternal ps = getPackageStateInternal(packageName);
         if (ps == null || shouldFilterApplication(ps, callingUid, userId)
                 || !ps.getUserStateOrDefault(userId).isInstalled()) {
@@ -3571,7 +3571,7 @@
             return PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
         }
 
-        if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
+        if (!ceStorageUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
             return PackageManagerService.PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED;
         }
         return PackageManagerService.PACKAGE_STARTABILITY_OK;
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 8bd2982..a10bae9 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -223,11 +223,6 @@
                 pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
             }
 
-            // TODO(b/251903639): Do this when ART Service is used, or remove it from here.
-            if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) {
-                mPm.mArtManagerService.compileLayouts(packageState, pkg);
-            }
-
             int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
 
             String filter = getCompilerFilterForReason(pkgCompilationReason);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7cac870..2951ef6 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -59,7 +59,6 @@
 import static com.android.server.pm.PackageManagerService.MIN_INSTALLABLE_TARGET_SDK;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.pm.PackageManagerService.POST_INSTALL;
-import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY;
@@ -135,7 +134,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SELinux;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -167,7 +165,6 @@
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
-import com.android.server.pm.dex.ViewCompiler;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -222,7 +219,6 @@
     private final Context mContext;
     private final PackageDexOptimizer mPackageDexOptimizer;
     private final PackageAbiHelper mPackageAbiHelper;
-    private final ViewCompiler mViewCompiler;
     private final SharedLibrariesImpl mSharedLibraries;
     private final PackageManagerServiceInjector mInjector;
     private final UpdateOwnershipHelper mUpdateOwnershipHelper;
@@ -246,7 +242,6 @@
         mContext = pm.mInjector.getContext();
         mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
         mPackageAbiHelper = pm.mInjector.getAbiHelper();
-        mViewCompiler = pm.mInjector.getViewCompiler();
         mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
         mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper();
     }
@@ -2119,24 +2114,6 @@
                             // ignore; not possible for non-system app
                         }
                     }
-                    // Successfully deleted the old package; proceed with replace.
-                    // Update the in-memory copy of the previous code paths.
-                    PackageSetting ps1 = mPm.mSettings.getPackageLPr(
-                            installRequest.getExistingPackageName());
-                    if ((installRequest.getInstallFlags() & PackageManager.DONT_KILL_APP)
-                            == 0) {
-                        Set<String> oldCodePaths = ps1.getOldCodePaths();
-                        if (oldCodePaths == null) {
-                            oldCodePaths = new ArraySet<>();
-                        }
-                        if (oldPackage != null) {
-                            Collections.addAll(oldCodePaths, oldPackage.getBaseApkPath());
-                            Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
-                        }
-                        ps1.setOldCodePaths(oldCodePaths);
-                    } else {
-                        ps1.setOldCodePaths(null);
-                    }
 
                     if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
                         PackageSetting ps2 = mPm.mSettings.getPackageLPr(
@@ -2397,12 +2374,6 @@
                 permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
                 mPm.mPermissionManager.onPackageInstalled(pkg, installRequest.getPreviousAppId(),
                         permissionParamsBuilder.build(), userId);
-                // Apply restricted settings on potentially dangerous packages.
-                if (installRequest.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
-                        || installRequest.getPackageSource()
-                        == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
-                    enableRestrictedSettings(pkgName, pkg.getUid());
-                }
             }
             installRequest.setName(pkgName);
             installRequest.setAppId(pkg.getUid());
@@ -2417,16 +2388,13 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    private void enableRestrictedSettings(String pkgName, int appId) {
+    private void enableRestrictedSettings(String pkgName, int appId, int userId) {
         final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class);
-        final int[] allUsersList = mPm.mUserManager.getUserIds();
-        for (int userId : allUsersList) {
-            final int uid = UserHandle.getUid(userId, appId);
-            appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
-                    uid,
-                    pkgName,
-                    AppOpsManager.MODE_ERRORED);
-        }
+        final int uid = UserHandle.getUid(userId, appId);
+        appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+                uid,
+                pkgName,
+                AppOpsManager.MODE_ERRORED);
     }
 
     /**
@@ -2539,13 +2507,6 @@
                             && !isApex;
 
             if (performDexopt) {
-                // Compile the layout resources.
-                if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
-                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
-                    mViewCompiler.compileLayouts(ps, pkg.getBaseApkPath());
-                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-                }
-
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
                 // This mirrors logic from commitReconciledScanResultLocked, where the library files
@@ -2850,13 +2811,10 @@
                     mPm.mRequiredInstallerPackage,
                     /* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
 
-
             // Work that needs to happen on first install within each user
-            if (firstUserIds.length > 0) {
-                for (int userId : firstUserIds) {
-                    mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
-                            userId);
-                }
+            for (int userId : firstUserIds) {
+                mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
+                        userId);
             }
 
             if (request.isAllNewUsers() && !update) {
@@ -2865,6 +2823,16 @@
                 mPm.notifyPackageChanged(packageName, request.getAppId());
             }
 
+            for (int userId : firstUserIds) {
+                // Apply restricted settings on potentially dangerous packages. Needs to happen
+                // after appOpsManager is notified of the new package
+                if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+                        || request.getPackageSource()
+                        == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
+                    enableRestrictedSettings(packageName, request.getAppId(), userId);
+                }
+            }
+
             // Log current value of "unknown sources" setting
             EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
                     getUnknownSourcesSettings());
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 0ebd33b..4ed3163 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -1128,14 +1128,6 @@
         throw new InstallerException("Invalid instruction set: " + instructionSet);
     }
 
-    public boolean compileLayouts(String apkPath, String packageName, String outDexFile, int uid) {
-        try {
-            return mInstalld.compileLayouts(apkPath, packageName, outDexFile, uid);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
     /**
      * Returns the visibility of the optimized artifacts.
      *
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 1135466..8b82a1c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -56,6 +56,7 @@
 import android.content.LocusId;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.IPackageInstallerCallback;
@@ -94,6 +95,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
@@ -112,6 +114,8 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.ArchiveState;
+import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.DataInputStream;
@@ -502,6 +506,9 @@
             if (!canAccessProfile(userId, "cannot get shouldHideFromSuggestions")) {
                 return false;
             }
+            if (Flags.archiving() && packageName != null && isPackageArchived(packageName, user)) {
+                return true;
+            }
             if (mPackageManagerInternal.filterAppAccess(
                     packageName, Binder.getCallingUid(), userId)) {
                 return false;
@@ -513,15 +520,24 @@
 
         @Override
         public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
-                String callingPackage, String packageName, UserHandle user) throws RemoteException {
+                String callingPackage, @Nullable String packageName, UserHandle user)
+                throws RemoteException {
             ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
-                    queryActivitiesForUser(callingPackage,
+                    queryActivitiesForUser(
+                            callingPackage,
                             new Intent(Intent.ACTION_MAIN)
                                     .addCategory(Intent.CATEGORY_LAUNCHER)
                                     .setPackage(packageName),
                             user);
-            if (Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
+            if (Flags.archiving()) {
+                launcherActivities =
+                        getActivitiesForArchivedApp(packageName, user, launcherActivities);
+            }
+            if (Settings.Global.getInt(
+                            mContext.getContentResolver(),
+                            Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED,
+                            1)
+                    == 0) {
                 return launcherActivities;
             }
             if (launcherActivities == null) {
@@ -565,15 +581,16 @@
                     visiblePackages.add(info.getActivityInfo().packageName);
                 }
                 final List<ApplicationInfo> installedPackages =
-                        mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,
-                                user.getIdentifier(), callingUid);
+                        mPackageManagerInternal.getInstalledApplications(
+                                /* flags= */ 0, user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
                     if (!visiblePackages.contains(applicationInfo.packageName)) {
                         if (!shouldShowSyntheticActivity(user, applicationInfo)) {
                             continue;
                         }
-                        LauncherActivityInfoInternal info = getHiddenAppActivityInfo(
-                                applicationInfo.packageName, callingUid, user);
+                        LauncherActivityInfoInternal info =
+                                getHiddenAppActivityInfo(
+                                        applicationInfo.packageName, callingUid, user);
                         if (info != null) {
                             result.add(info);
                         }
@@ -585,6 +602,23 @@
             }
         }
 
+        private ParceledListSlice<LauncherActivityInfoInternal> getActivitiesForArchivedApp(
+                @Nullable String packageName,
+                UserHandle user,
+                ParceledListSlice<LauncherActivityInfoInternal> launcherActivities) {
+            final List<LauncherActivityInfoInternal> archivedActivities =
+                    generateLauncherActivitiesForArchivedApp(packageName, user);
+            if (archivedActivities.isEmpty()) {
+                return launcherActivities;
+            }
+            if (launcherActivities == null) {
+                return new ParceledListSlice(archivedActivities);
+            }
+            List<LauncherActivityInfoInternal> result = launcherActivities.getList();
+            result.addAll(archivedActivities);
+            return new ParceledListSlice(result);
+        }
+
         private boolean shouldShowSyntheticActivity(UserHandle user, ApplicationInfo appInfo) {
             if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
                 return false;
@@ -650,23 +684,30 @@
                 return null;
             }
 
+            if (component == null || component.getPackageName() == null) {
+                // should not happen
+                return null;
+            }
+
             final int callingUid = injectBinderCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                final ActivityInfo activityInfo = mPackageManagerInternal.getActivityInfo(component,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        callingUid, user.getIdentifier());
+                ActivityInfo activityInfo =
+                        mPackageManagerInternal.getActivityInfo(
+                                component,
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                callingUid,
+                                user.getIdentifier());
                 if (activityInfo == null) {
-                    return null;
-                }
-                if (component == null || component.getPackageName() == null) {
-                    // should not happen
+                    if (Flags.archiving()) {
+                        return getMatchingArchivedAppActivityInfo(component, user);
+                    }
                     return null;
                 }
                 final IncrementalStatesInfo incrementalStatesInfo =
-                        mPackageManagerInternal.getIncrementalStatesInfo(component.getPackageName(),
-                                callingUid, user.getIdentifier());
+                        mPackageManagerInternal.getIncrementalStatesInfo(
+                                component.getPackageName(), callingUid, user.getIdentifier());
                 if (incrementalStatesInfo == null) {
                     // package does not exist; should not happen
                     return null;
@@ -677,6 +718,26 @@
             }
         }
 
+        private @Nullable LauncherActivityInfoInternal getMatchingArchivedAppActivityInfo(
+                @NonNull ComponentName component, UserHandle user) {
+            List<LauncherActivityInfoInternal> archivedActivities =
+                    generateLauncherActivitiesForArchivedApp(component.getPackageName(), user);
+            if (archivedActivities.isEmpty()) {
+                return null;
+            }
+            for (int i = 0; i < archivedActivities.size(); i++) {
+                if (archivedActivities.get(i).getComponentName().equals(component)) {
+                    return archivedActivities.get(i);
+                }
+            }
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Expected archived app component name: %s" + " is not available!",
+                            component));
+            return null;
+        }
+
         @Override
         public ParceledListSlice getShortcutConfigActivities(
                 String callingPackage, String packageName, UserHandle user)
@@ -700,6 +761,100 @@
             }
         }
 
+        private boolean isPackageArchived(@NonNull String packageName, UserHandle user) {
+            return !getApplicationInfoForArchivedApp(packageName, user).isEmpty();
+        }
+
+        @NonNull
+        private List<LauncherActivityInfoInternal> generateLauncherActivitiesForArchivedApp(
+                @Nullable String packageName, UserHandle user) {
+            List<ApplicationInfo> applicationInfoList =
+                    (packageName == null)
+                            ? getApplicationInfoListForAllArchivedApps(user)
+                            : getApplicationInfoForArchivedApp(packageName, user);
+            List<LauncherActivityInfoInternal> launcherActivityList = new ArrayList<>();
+            for (int i = 0; i < applicationInfoList.size(); i++) {
+                ApplicationInfo applicationInfo = applicationInfoList.get(i);
+                PackageStateInternal packageState =
+                        mPackageManagerInternal.getPackageStateInternal(
+                                applicationInfo.packageName);
+                if (packageState == null) {
+                    continue;
+                }
+                ArchiveState archiveState =
+                        packageState.getUserStateOrDefault(user.getIdentifier()).getArchiveState();
+                if (archiveState == null) {
+                    Slog.w(
+                            TAG,
+                            TextUtils.formatSimple(
+                                    "Expected package: %s to be archived but missing ArchiveState"
+                                            + " in PackageState.",
+                                    applicationInfo.packageName));
+                    continue;
+                }
+                List<ArchiveState.ArchiveActivityInfo> archiveActivityInfoList =
+                        archiveState.getActivityInfos();
+                for (int j = 0; j < archiveActivityInfoList.size(); j++) {
+                    launcherActivityList.add(
+                            constructLauncherActivityInfoForArchivedApp(
+                                    user, applicationInfo, archiveActivityInfoList.get(j)));
+                }
+            }
+            return launcherActivityList;
+        }
+
+        private static LauncherActivityInfoInternal constructLauncherActivityInfoForArchivedApp(
+                UserHandle user,
+                ApplicationInfo applicationInfo,
+                ArchiveState.ArchiveActivityInfo archiveActivityInfo) {
+            ActivityInfo activityInfo = new ActivityInfo();
+            activityInfo.isArchived = applicationInfo.isArchived;
+            activityInfo.applicationInfo = applicationInfo;
+            activityInfo.packageName =
+                    archiveActivityInfo.getOriginalComponentName().getPackageName();
+            activityInfo.name = archiveActivityInfo.getOriginalComponentName().getClassName();
+            activityInfo.nonLocalizedLabel = archiveActivityInfo.getTitle();
+
+            return new LauncherActivityInfoInternal(
+                    activityInfo,
+                    new IncrementalStatesInfo(
+                            false /* isLoading */, 1 /* progress */, 0 /* loadingCompletedTime */),
+                    user);
+        }
+
+        @NonNull
+        private List<ApplicationInfo> getApplicationInfoListForAllArchivedApps(UserHandle user) {
+            final int callingUid = injectBinderCallingUid();
+            List<ApplicationInfo> installedApplicationInfoList =
+                    mPackageManagerInternal.getInstalledApplications(
+                            PackageManager.MATCH_ARCHIVED_PACKAGES,
+                            user.getIdentifier(),
+                            callingUid);
+            List<ApplicationInfo> archivedApplicationInfos = new ArrayList<>();
+            for (int i = 0; i < installedApplicationInfoList.size(); i++) {
+                ApplicationInfo installedApplicationInfo = installedApplicationInfoList.get(i);
+                if (installedApplicationInfo != null && installedApplicationInfo.isArchived) {
+                    archivedApplicationInfos.add(installedApplicationInfo);
+                }
+            }
+            return archivedApplicationInfos;
+        }
+
+        @NonNull
+        private List<ApplicationInfo> getApplicationInfoForArchivedApp(
+                @NonNull String packageName, UserHandle user) {
+            final int callingUid = injectBinderCallingUid();
+            ApplicationInfo applicationInfo = mPackageManagerInternal.getApplicationInfo(
+                    packageName,
+                    PackageManager.MATCH_ARCHIVED_PACKAGES,
+                    callingUid,
+                    user.getIdentifier());
+            if (applicationInfo == null || !applicationInfo.isArchived) {
+                return Collections.EMPTY_LIST;
+            }
+            return List.of(applicationInfo);
+        }
+
         private List<LauncherActivityInfoInternal> queryIntentLauncherActivities(
                 Intent intent, int callingUid, UserHandle user) {
             final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(intent,
@@ -821,11 +976,17 @@
             final int callingUid = injectBinderCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PackageInfo info = mPackageManagerInternal.getPackageInfo(packageName,
+                long callingFlag =
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        callingUid, user.getIdentifier());
-                return info != null && info.applicationInfo.enabled;
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+                if (Flags.archiving()) {
+                    callingFlag |= PackageManager.MATCH_ARCHIVED_PACKAGES;
+                }
+                final PackageInfo info =
+                        mPackageManagerInternal.getPackageInfo(
+                                packageName, callingFlag, callingUid, user.getIdentifier());
+                return info != null
+                        && (info.applicationInfo.enabled || info.applicationInfo.isArchived);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1291,7 +1452,18 @@
             if (!canAccessProfile(user.getIdentifier(), "Cannot check component")) {
                 return false;
             }
-
+            if (Flags.archiving() && component != null && component.getPackageName() != null) {
+                List<LauncherActivityInfoInternal> archiveActivities =
+                        generateLauncherActivitiesForArchivedApp(component.getPackageName(), user);
+                if (!archiveActivities.isEmpty()) {
+                    for (int i = 0; i < archiveActivities.size(); i++) {
+                        if (archiveActivities.get(i).getComponentName().equals(component)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            }
             final int callingUid = injectBinderCallingUid();
             final int state = mPackageManagerInternal.getComponentEnabledSetting(component,
                     callingUid, user.getIdentifier());
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 9ad8318..f5f5577 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -196,7 +196,8 @@
         // If we're moving app data around, we need all the users unlocked
         if (moveCompleteApp) {
             for (int userId : installedUserIds) {
-                if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+                if (StorageManager.isFileEncrypted()
+                        && !StorageManager.isCeStorageUnlocked(userId)) {
                     freezer.close();
                     throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
                             "User " + userId + " must be unlocked");
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6a2ddc8..ea082cf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -159,6 +159,9 @@
             if (pkgSetting.getPkg().isCoreApp()) {
                 throw new IllegalStateException("Found a core app that's not important");
             }
+            // Use REASON_FIRST_BOOT to query "pm.dexopt.first-boot" for the compiler filter, but
+            // the reason itself won't make it into the actual compiler reason because it will be
+            // overridden in otapreopt.cpp.
             mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.getPkg(), pkgSetting,
                     PackageManagerService.REASON_FIRST_BOOT));
         }
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 781e5f8..42a97f7 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -17,6 +17,8 @@
 package com.android.server.pm;
 
 import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.content.pm.ArchivedActivity.bytesFromBitmap;
+import static android.content.pm.ArchivedActivity.drawableToBitmap;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
@@ -27,6 +29,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.Context;
@@ -43,9 +46,6 @@
 import android.content.pm.VersionedPackage;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -64,7 +64,6 @@
 import com.android.server.pm.pkg.PackageUserState;
 import com.android.server.pm.pkg.PackageUserStateInternal;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -216,10 +215,13 @@
     ArchiveState createArchiveStateInternal(String packageName, int userId,
             List<LauncherActivityInfo> mainActivities, String installerPackage)
             throws IOException {
+        final int iconSize = mContext.getSystemService(
+                ActivityManager.class).getLauncherLargeIconSize();
+
         List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.size());
         for (int i = 0, size = mainActivities.size(); i < size; i++) {
             LauncherActivityInfo mainActivity = mainActivities.get(i);
-            Path iconPath = storeIcon(packageName, mainActivity, userId, i);
+            Path iconPath = storeIcon(packageName, mainActivity, userId, i, iconSize);
             ArchiveActivityInfo activityInfo =
                     new ArchiveActivityInfo(
                             mainActivity.getLabel().toString(),
@@ -249,7 +251,7 @@
 
     @VisibleForTesting
     Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
-            @UserIdInt int userId, int index) throws IOException {
+            @UserIdInt int userId, int index, int iconSize) throws IOException {
         int iconResourceId = mainActivity.getActivityInfo().getIconResource();
         if (iconResourceId == 0) {
             // The app doesn't define an icon. No need to store anything.
@@ -257,7 +259,7 @@
         }
         File iconsDir = createIconsDir(userId);
         File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
-        Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0));
+        Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize);
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
             // Note: Quality is ignored for PNGs.
             if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) {
@@ -547,29 +549,6 @@
         return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR);
     }
 
-    private static Bitmap drawableToBitmap(Drawable drawable) {
-        if (drawable instanceof BitmapDrawable) {
-            return ((BitmapDrawable) drawable).getBitmap();
-
-        }
-
-        Bitmap bitmap;
-        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
-            // Needed for drawables that are just a single color.
-            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-        } else {
-            bitmap =
-                    Bitmap.createBitmap(
-                            drawable.getIntrinsicWidth(),
-                            drawable.getIntrinsicHeight(),
-                            Bitmap.Config.ARGB_8888);
-        }
-        Canvas canvas = new Canvas(bitmap);
-        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-        drawable.draw(canvas);
-        return bitmap;
-    }
-
     private static byte[] bytesFromBitmapFile(Path path) throws IOException {
         if (path == null) {
             return null;
@@ -579,18 +558,6 @@
         return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
     }
 
-    private static byte[] bytesFromBitmap(Bitmap bitmap) throws IOException {
-        if (bitmap == null) {
-            return null;
-        }
-
-        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(
-                bitmap.getByteCount())) {
-            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
-            return baos.toByteArray();
-        }
-    }
-
     /**
      * Creates serializable archived activities from existing ArchiveState.
      */
@@ -627,8 +594,8 @@
     /**
      * Creates serializable archived activities from launcher activities.
      */
-    static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos)
-            throws IOException {
+    static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos,
+            int iconSize) throws IOException {
         if (infos == null || infos.isEmpty()) {
             throw new IllegalArgumentException("No launcher activities");
         }
@@ -642,9 +609,8 @@
             var archivedActivity = new ArchivedActivityParcel();
             archivedActivity.title = info.getLabel().toString();
             archivedActivity.originalComponentName = info.getComponentName();
-            archivedActivity.iconBitmap =
-                    info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap(
-                            drawableToBitmap(info.getIcon(/* density= */ 0)));
+            archivedActivity.iconBitmap = info.getActivityInfo().getIconResource() == 0 ? null :
+                    bytesFromBitmap(drawableToBitmap(info.getIcon(/* density= */ 0), iconSize));
             // TODO(b/298452477) Handle monochrome icons.
             archivedActivity.monochromeIconBitmap = null;
             activities.add(archivedActivity);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1bb20b47..305e353 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
 import static android.os.Process.INVALID_UID;
 
 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
@@ -42,6 +43,8 @@
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
+import android.content.pm.Flags;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
@@ -621,6 +624,14 @@
     public int createSession(SessionParams params, String installerPackageName,
             String callingAttributionTag, int userId) {
         try {
+            if (params.dataLoaderParams != null
+                    && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("You need the "
+                        + "com.android.permission.USE_INSTALLER_V2 permission "
+                        + "to use a data loader");
+            }
+
             return createSessionInternal(params, installerPackageName, callingAttributionTag,
                     userId);
         } catch (IOException e) {
@@ -639,14 +650,6 @@
             throw new SecurityException("User restriction prevents installing");
         }
 
-        if (params.dataLoaderParams != null
-                && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("You need the "
-                    + "com.android.permission.USE_INSTALLER_V2 permission "
-                    + "to use a data loader");
-        }
-
         // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK
         // capability; ensure if this is set as the install reason the app has one of the necessary
         // signature permissions to perform the rollback.
@@ -743,6 +746,22 @@
             params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
         }
 
+        if (Flags.rollbackLifetime()) {
+            if (params.rollbackLifetimeMillis > 0) {
+                if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
+                    throw new IllegalArgumentException(
+                            "Can't set rollbackLifetimeMillis when rollback is not enabled");
+                }
+                if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException(
+                            "Setting rollback lifetime requires the MANAGE_ROLLBACKS permission");
+                }
+            } else if (params.rollbackLifetimeMillis < 0) {
+                throw new IllegalArgumentException("rollbackLifetimeMillis can't be negative.");
+            }
+        }
+
         boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
         if (isApex) {
             if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES)
@@ -1043,7 +1062,7 @@
         return false;
     }
 
-    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
+    private PackageInstallerSession openSessionInternal(int sessionId) throws IOException {
         synchronized (mSessions) {
             final PackageInstallerSession session = mSessions.get(sessionId);
             if (!checkOpenSessionAccess(session)) {
@@ -1523,6 +1542,61 @@
         mPackageArchiver.requestUnarchive(packageName, callerPackageName, userHandle);
     }
 
+    @Override
+    public void installPackageArchived(
+            @NonNull ArchivedPackageParcel archivedPackageParcel,
+            @NonNull SessionParams params,
+            @NonNull IntentSender statusReceiver,
+            @NonNull String installerPackageName,
+            @NonNull UserHandle userHandle) {
+        Objects.requireNonNull(params);
+        Objects.requireNonNull(archivedPackageParcel);
+        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(installerPackageName);
+        Objects.requireNonNull(userHandle);
+
+        final int callingUid = Binder.getCallingUid();
+        final int userId = userHandle.getIdentifier();
+        final Computer snapshot = mPm.snapshotComputer();
+        snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
+                "installPackageArchived");
+
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need the "
+                    + "com.android.permission.INSTALL_PACKAGES permission "
+                    + "to request archived package install");
+        }
+
+        params.installFlags |= PackageManager.INSTALL_ARCHIVED;
+        if (params.dataLoaderParams != null) {
+            throw new IllegalArgumentException(
+                    "Incompatible session param: dataLoaderParams has to be null");
+        }
+
+        params.setDataLoaderParams(
+                PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(null));
+        var metadata = PackageManagerShellCommandDataLoader.Metadata.forArchived(
+                archivedPackageParcel);
+
+        // Create and commit install archived session.
+        PackageInstallerSession session = null;
+        try {
+            var sessionId = createSessionInternal(params, installerPackageName,
+                    null /*installerAttributionTag*/, userId);
+            session = openSessionInternal(sessionId);
+            session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/, metadata.toByteArray(),
+                    null /*signature*/);
+            session.commit(statusReceiver, false /*forTransfer*/);
+        } catch (IOException e) {
+            throw ExceptionUtils.wrap(e);
+        } finally {
+            if (session != null) {
+                session.close();
+            }
+        }
+    }
+
     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
             int installerUid) {
         int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5dc7dab..1be28ca 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1282,6 +1282,7 @@
             info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
             info.autoRevokePermissionsMode = params.autoRevokePermissionsMode;
             info.installFlags = params.installFlags;
+            info.rollbackLifetimeMillis = params.rollbackLifetimeMillis;
             info.isMultiPackage = params.isMultiPackage;
             info.isStaged = params.isStaged;
             info.rollbackDataPolicy = params.rollbackDataPolicy;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 61b6b24..c9303f2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -215,7 +215,6 @@
 import com.android.server.pm.dex.ArtUtils;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.dex.ViewCompiler;
 import com.android.server.pm.local.PackageManagerLocalImpl;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.PackageParser2;
@@ -812,8 +811,6 @@
     private final DexManager mDexManager;
     private final DynamicCodeLogger mDynamicCodeLogger;
 
-    final ViewCompiler mViewCompiler;
-
     private final AtomicInteger mNextMoveId = new AtomicInteger();
     final MovePackageHelper.MoveCallbacks mMoveCallbacks;
 
@@ -1486,11 +1483,14 @@
                 archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
                         archiveState);
             } else {
+                final int iconSize = mContext.getSystemService(
+                        ActivityManager.class).getLauncherLargeIconSize();
+
                 var mainActivities =
                         mInstallerService.mPackageArchiver.getLauncherActivityInfos(packageName,
                                 userId);
                 archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
-                        mainActivities);
+                        mainActivities, iconSize);
             }
         } catch (Exception e) {
             throw new IllegalArgumentException("Package does not have a main activity", e);
@@ -1670,7 +1670,6 @@
                 (i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(),
                         i.getInstallLock()),
                 (i, pm) -> ApexManager.getInstance(),
-                (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
                 (i, pm) -> (IncrementalManager)
                         i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
                 (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
@@ -1881,7 +1880,6 @@
         mProcessLoggingHandler = testParams.processLoggingHandler;
         mProtectedPackages = testParams.protectedPackages;
         mSeparateProcesses = testParams.separateProcesses;
-        mViewCompiler = testParams.viewCompiler;
         mRequiredVerifierPackages = testParams.requiredVerifierPackages;
         mRequiredInstallerPackage = testParams.requiredInstallerPackage;
         mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
@@ -2044,7 +2042,6 @@
         mBackgroundDexOptService = injector.getBackgroundDexOptService();
         mArtManagerService = injector.getArtManagerService();
         mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper());
-        mViewCompiler = injector.getViewCompiler();
         mSharedLibraries = mInjector.getSharedLibrariesImpl();
         mBackgroundHandler = injector.getBackgroundHandler();
 
@@ -3360,7 +3357,7 @@
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
         StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
         final int flags;
-        if (StorageManager.isUserKeyUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
+        if (StorageManager.isCeStorageUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         } else if (umInternal.isUserRunning(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 5b770aab..ebf1c04 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -32,7 +32,6 @@
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.dex.ViewCompiler;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -112,7 +111,6 @@
     private final Singleton<ArtManagerService>
             mArtManagerServiceProducer;
     private final Singleton<ApexManager> mApexManagerProducer;
-    private final Singleton<ViewCompiler> mViewCompilerProducer;
     private final Singleton<IncrementalManager>
             mIncrementalManagerProducer;
     private final Singleton<DefaultAppProvider>
@@ -164,7 +162,6 @@
             Producer<DynamicCodeLogger> dynamicCodeLoggerProducer,
             Producer<ArtManagerService> artManagerServiceProducer,
             Producer<ApexManager> apexManagerProducer,
-            Producer<ViewCompiler> viewCompilerProducer,
             Producer<IncrementalManager> incrementalManagerProducer,
             Producer<DefaultAppProvider> defaultAppProviderProducer,
             Producer<DisplayMetrics> displayMetricsProducer,
@@ -214,7 +211,6 @@
         mArtManagerServiceProducer = new Singleton<>(
                 artManagerServiceProducer);
         mApexManagerProducer = new Singleton<>(apexManagerProducer);
-        mViewCompilerProducer = new Singleton<>(viewCompilerProducer);
         mIncrementalManagerProducer = new Singleton<>(
                 incrementalManagerProducer);
         mDefaultAppProviderProducer = new Singleton<>(
@@ -339,10 +335,6 @@
         return mApexManagerProducer.get(this, mPackageManager);
     }
 
-    public ViewCompiler getViewCompiler() {
-        return mViewCompilerProducer.get(this, mPackageManager);
-    }
-
     public Handler getBackgroundHandler() {
         return mBackgroundHandler;
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index ca57209..9428ef6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -34,7 +34,6 @@
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.dex.ViewCompiler;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -93,7 +92,6 @@
     public @Nullable String systemTextClassifierPackage;
     public @Nullable String overlayConfigSignaturePackage;
     public @NonNull String requiredSdkSandboxPackage;
-    public ViewCompiler viewCompiler;
     public @Nullable String retailDemoPackage;
     public @Nullable String recentsPackage;
     public @Nullable String ambientContextDetectionPackage;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 9e7f043..a09e713 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -304,10 +304,6 @@
         public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
                 @NonNull Collection<String> removedFiles) {
             ShellCommand shellCommand = lookupShellCommand(mParams.getArguments());
-            if (shellCommand == null) {
-                Slog.e(TAG, "Missing shell command.");
-                return false;
-            }
             try {
                 for (InstallationFile file : addedFiles) {
                     Metadata metadata = Metadata.fromByteArray(file.getMetadata());
@@ -317,11 +313,19 @@
                     }
                     switch (metadata.getMode()) {
                         case Metadata.STDIN: {
+                            if (shellCommand == null) {
+                                Slog.e(TAG, "Missing shell command for Metadata.STDIN.");
+                                return false;
+                            }
                             final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
                             mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
                             break;
                         }
                         case Metadata.LOCAL_FILE: {
+                            if (shellCommand == null) {
+                                Slog.e(TAG, "Missing shell command for Metadata.LOCAL_FILE.");
+                                return false;
+                            }
                             ParcelFileDescriptor incomingFd = null;
                             try {
                                 final String filePath = new String(metadata.getData(),
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index ad26d1f..3cf5481 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -121,10 +121,6 @@
     @Nullable
     private Map<String, Set<String>> mimeGroups;
 
-    @Deprecated
-    @Nullable
-    private Set<String> mOldCodePaths;
-
     @Nullable
     private String[] usesSdkLibraries;
 
@@ -722,15 +718,6 @@
             }
         }
 
-        if (mOldCodePaths != null) {
-            if (other.mOldCodePaths != null) {
-                mOldCodePaths.clear();
-                mOldCodePaths.addAll(other.mOldCodePaths);
-            } else {
-                mOldCodePaths = null;
-            }
-        }
-
         copyMimeGroups(other.mimeGroups);
         pkgState.updateFrom(other.pkgState);
         onChanged();
@@ -1428,12 +1415,6 @@
         return this;
     }
 
-    public PackageSetting setOldCodePaths(Set<String> oldCodePaths) {
-        mOldCodePaths = oldCodePaths;
-        onChanged();
-        return this;
-    }
-
     public PackageSetting setUsesSdkLibraries(String[] usesSdkLibraries) {
         this.usesSdkLibraries = usesSdkLibraries;
         onChanged();
@@ -1593,11 +1574,6 @@
     //@formatter:off
 
 
-    @DataClass.Generated.Member
-    public @Deprecated @Nullable Set<String> getOldCodePaths() {
-        return mOldCodePaths;
-    }
-
     /**
      * The path under which native libraries have been unpacked. This path is
      * always derived at runtime, and is only stored here for cleanup when a
@@ -1731,10 +1707,10 @@
     }
 
     @DataClass.Generated(
-            time = 1698097434269L,
+            time = 1698188444364L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic  com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic  com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  boolean isRequestLegacyExternalStorage()\npublic  boolean isUserDataFragile()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  int[] queryUsersInstalledOrHasData(int[])\n  long getCeDataInode(int)\n  long getDeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic  com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic  com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  boolean isRequestLegacyExternalStorage()\npublic  boolean isUserDataFragile()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  int[] queryUsersInstalledOrHasData(int[])\n  long getCeDataInode(int)\n  long getDeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index c725cdc..70aa19a 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -183,7 +183,7 @@
                 StorageManagerInternal.class);
         for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
             final int flags;
-            if (StorageManager.isUserKeyUnlocked(user.id)
+            if (StorageManager.isCeStorageUnlocked(user.id)
                     && smInternal.isCeStoragePrepared(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (umInternal.isUserRunning(user.id)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 154ee6e..3430bb4d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1427,7 +1427,7 @@
             }
             final boolean needToShowConfirmCredential = !dontAskCredential
                     && mLockPatternUtils.isSecure(userId)
-                    && (!hasUnifiedChallenge || !StorageManager.isUserKeyUnlocked(userId));
+                    && (!hasUnifiedChallenge || !StorageManager.isCeStorageUnlocked(userId));
             if (needToShowConfirmCredential) {
                 if (onlyIfCredentialNotRequired) {
                     return false;
@@ -2138,6 +2138,19 @@
     }
 
     @Override
+    public boolean isForegroundUserAdmin() {
+        // No permission requirements for this API.
+        synchronized (mUsersLock) {
+            final int currentUserId = getCurrentUserId();
+            if (currentUserId != UserHandle.USER_NULL) {
+                final UserInfo userInfo = getUserInfoLU(currentUserId);
+                return userInfo != null && userInfo.isAdmin();
+            }
+        }
+        return false;
+    }
+
+    @Override
     public @NonNull String getUserName() {
         final int callingUid = Binder.getCallingUid();
         if (!hasQueryOrCreateUsersPermission()
@@ -5095,9 +5108,9 @@
                 }
             }
 
-            t.traceBegin("createUserKey");
+            t.traceBegin("createUserStorageKeys");
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
+            storage.createUserStorageKeys(userId, userInfo.serialNumber, userInfo.isEphemeral());
             t.traceEnd();
 
             // Only prepare DE storage here.  CE storage will be prepared later, when the user is
@@ -5899,17 +5912,18 @@
     private void removeUserState(final @UserIdInt int userId) {
         Slog.i(LOG_TAG, "Removing user state of user " + userId);
 
-        // Cleanup lock settings.  This must happen before destroyUserKey(), since the user's DE
-        // storage must still be accessible for the lock settings state to be properly cleaned up.
+        // Cleanup lock settings.  This requires that the user's DE storage still be accessible, so
+        // this must happen before destroyUserStorageKeys().
         mLockPatternUtils.removeUser(userId);
 
         // Evict and destroy the user's CE and DE encryption keys.  At this point, the user's CE and
         // DE storage is made inaccessible, except to delete its contents.
         try {
-            mContext.getSystemService(StorageManager.class).destroyUserKey(userId);
+            mContext.getSystemService(StorageManager.class).destroyUserStorageKeys(userId);
         } catch (IllegalStateException e) {
             // This may be simply because the user was partially created.
-            Slog.i(LOG_TAG, "Destroying key for user " + userId + " failed, continuing anyway", e);
+            Slog.i(LOG_TAG, "Destroying storage keys for user " + userId
+                    + " failed, continuing anyway", e);
         }
 
         // Cleanup package manager settings
@@ -7177,9 +7191,9 @@
             synchronized (mUserStates) {
                 state = mUserStates.get(userId, -1);
             }
-            // Special case, in the stopping/shutdown state user key can still be unlocked
+            // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
             if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
-                return StorageManager.isUserKeyUnlocked(userId);
+                return StorageManager.isCeStorageUnlocked(userId);
             }
             return (state == UserState.STATE_RUNNING_UNLOCKING)
                     || (state == UserState.STATE_RUNNING_UNLOCKED);
@@ -7196,9 +7210,9 @@
             synchronized (mUserStates) {
                 state = mUserStates.get(userId, -1);
             }
-            // Special case, in the stopping/shutdown state user key can still be unlocked
+            // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
             if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
-                return StorageManager.isUserKeyUnlocked(userId);
+                return StorageManager.isCeStorageUnlocked(userId);
             }
             return state == UserState.STATE_RUNNING_UNLOCKED;
         }
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index f4f03f4..ae47aa8 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -540,44 +540,6 @@
     }
 
     /**
-     * Compile layout resources in a given package.
-     */
-    public boolean compileLayouts(@NonNull PackageStateInternal ps, @NonNull AndroidPackage pkg) {
-        try {
-            if (ps.isPrivileged() || pkg.isUseEmbeddedDex()
-                    || pkg.isDefaultToDeviceProtectedStorage()) {
-                // Privileged apps prefer to load trusted code so they don't use compiled views.
-                // If the app is not privileged but prefers code integrity, also avoid compiling
-                // views.
-                // Also disable the view compiler for protected storage apps since there are
-                // selinux permissions required for writing to user_de.
-                return false;
-            }
-            final String packageName = pkg.getPackageName();
-            final String apkPath = pkg.getSplits().get(0).getPath();
-            final File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
-            if (dataDir == null) {
-                // The app is not installed on the target user and doesn't have a data dir
-                return false;
-            }
-            final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
-            Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
-                    ") to " + outDexFile);
-            final long callingId = Binder.clearCallingIdentity();
-            try {
-                return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
-                        pkg.getUid());
-            } finally {
-                Binder.restoreCallingIdentity(callingId);
-            }
-        }
-        catch (Throwable e) {
-            Log.e("PackageManager", "Failed to compile layouts", e);
-            return false;
-        }
-    }
-
-    /**
      * Build the profiles names for all the package code paths (excluding resource only paths).
      * Return the map [code path -> profile name].
      */
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
deleted file mode 100644
index 00269224..0000000
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.pm.dex;
-
-import android.os.Binder;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.pm.Installer;
-import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.pkg.PackageStateInternal;
-
-import java.io.File;
-
-public class ViewCompiler {
-    private final Object mInstallLock;
-    @GuardedBy("mInstallLock")
-    private final Installer mInstaller;
-
-    public ViewCompiler(Object installLock, Installer installer) {
-        mInstallLock = installLock;
-        mInstaller = installer;
-    }
-
-    public boolean compileLayouts(PackageStateInternal ps, String apkPath) {
-        try {
-            final String packageName = ps.getPackageName();
-            File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
-            if (dataDir == null) {
-                // The app is not installed on the target user and doesn't have a data dir
-                return false;
-            }
-            final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
-            Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
-                ") to " + outDexFile);
-            final long callingId = Binder.clearCallingIdentity();
-            try {
-                synchronized (mInstallLock) {
-                    return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
-                        ps.getAppId());
-                }
-            } finally {
-                Binder.restoreCallingIdentity(callingId);
-            }
-        } catch (Throwable e) {
-            Log.e("PackageManager", "Failed to compile layouts", e);
-            return false;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b439681..077812b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -546,6 +546,7 @@
     int mLidKeyboardAccessibility;
     int mLidNavigationAccessibility;
     int mShortPressOnPowerBehavior;
+    private boolean mShouldEarlyShortPressOnPower;
     int mLongPressOnPowerBehavior;
     long mLongPressOnPowerAssistantTimeoutMs;
     int mVeryLongPressOnPowerBehavior;
@@ -2651,6 +2652,9 @@
 
         @Override
         void onPress(long downTime) {
+            if (mShouldEarlyShortPressOnPower) {
+                return;
+            }
             powerPress(downTime, 1 /*count*/);
         }
 
@@ -2684,6 +2688,13 @@
         void onMultiPress(long downTime, int count) {
             powerPress(downTime, count);
         }
+
+        @Override
+        void onKeyUp(long eventTime, int count) {
+            if (mShouldEarlyShortPressOnPower && count == 1) {
+                powerPress(eventTime, 1 /*pressCount*/);
+            }
+        }
     }
 
     /**
@@ -2913,6 +2924,9 @@
                     Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS,
                     mContext.getResources().getInteger(
                             com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior));
+            mShouldEarlyShortPressOnPower =
+                    mContext.getResources()
+                            .getBoolean(com.android.internal.R.bool.config_shortPressEarlyOnPower);
 
             mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
                     Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 88c2e09..dd39fb0 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -34,7 +34,7 @@
 import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.StatsEvent;
 
 import com.android.internal.annotations.GuardedBy;
@@ -256,10 +256,11 @@
 
     @VisibleForTesting
     final class MyUidObserver extends UidObserver {
-        private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
-
+        private final Object mCacheLock = new Object();
+        @GuardedBy("mCacheLock")
+        private final SparseIntArray mProcStatesCache = new SparseIntArray();
         public boolean isUidForeground(int uid) {
-            synchronized (mLock) {
+            synchronized (mCacheLock) {
                 return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                         <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
             }
@@ -268,6 +269,9 @@
         @Override
         public void onUidGone(int uid, boolean disabled) {
             FgThread.getHandler().post(() -> {
+                synchronized (mCacheLock) {
+                    mProcStatesCache.delete(uid);
+                }
                 synchronized (mLock) {
                     ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(uid);
                     if (tokenMap == null) {
@@ -280,7 +284,6 @@
                             sessionSet.valueAt(j).close();
                         }
                     }
-                    mProcStatesCache.delete(uid);
                 }
             });
         }
@@ -292,15 +295,18 @@
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
             FgThread.getHandler().post(() -> {
-                synchronized (mLock) {
+                synchronized (mCacheLock) {
                     mProcStatesCache.put(uid, procState);
+                }
+                boolean shouldAllowUpdate = isUidForeground(uid);
+                synchronized (mLock) {
                     ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(uid);
                     if (tokenMap == null) {
                         return;
                     }
                     for (ArraySet<AppHintSession> sessionSet : tokenMap.values()) {
                         for (AppHintSession s : sessionSet) {
-                            s.onProcStateChanged();
+                            s.onProcStateChanged(shouldAllowUpdate);
                         }
                     }
                 }
@@ -429,10 +435,10 @@
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
                 return;
             }
+            pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate);
+            pw.println("HAL Support: " + isHalSupported());
+            pw.println("Active Sessions:");
             synchronized (mLock) {
-                pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate);
-                pw.println("HAL Support: " + isHalSupported());
-                pw.println("Active Sessions:");
                 for (int i = 0; i < mActiveSessions.size(); i++) {
                     pw.println("Uid " + mActiveSessions.keyAt(i).toString() + ":");
                     ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
@@ -476,7 +482,8 @@
             mHalSessionPtr = halSessionPtr;
             mTargetDurationNanos = durationNanos;
             mUpdateAllowed = true;
-            updateHintAllowed();
+            final boolean allowed = mUidObserver.isUidForeground(mUid);
+            updateHintAllowed(allowed);
             try {
                 token.linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -486,9 +493,8 @@
         }
 
         @VisibleForTesting
-        boolean updateHintAllowed() {
-            synchronized (mLock) {
-                final boolean allowed = mUidObserver.isUidForeground(mUid);
+        boolean updateHintAllowed(boolean allowed) {
+            synchronized (this) {
                 if (allowed && !mUpdateAllowed) resume();
                 if (!allowed && mUpdateAllowed) pause();
                 mUpdateAllowed = allowed;
@@ -498,8 +504,8 @@
 
         @Override
         public void updateTargetWorkDuration(long targetDurationNanos) {
-            synchronized (mLock) {
-                if (mHalSessionPtr == 0 || !updateHintAllowed()) {
+            synchronized (this) {
+                if (mHalSessionPtr == 0 || !mUpdateAllowed) {
                     return;
                 }
                 Preconditions.checkArgument(targetDurationNanos > 0, "Expected"
@@ -511,8 +517,8 @@
 
         @Override
         public void reportActualWorkDuration(long[] actualDurationNanos, long[] timeStampNanos) {
-            synchronized (mLock) {
-                if (mHalSessionPtr == 0 || !updateHintAllowed()) {
+            synchronized (this) {
+                if (mHalSessionPtr == 0 || !mUpdateAllowed) {
                     return;
                 }
                 Preconditions.checkArgument(actualDurationNanos.length != 0, "the count"
@@ -534,11 +540,13 @@
         /** TODO: consider monitor session threads and close session if any thread is dead. */
         @Override
         public void close() {
-            synchronized (mLock) {
+            synchronized (this) {
                 if (mHalSessionPtr == 0) return;
                 mNativeWrapper.halCloseHintSession(mHalSessionPtr);
                 mHalSessionPtr = 0;
                 mToken.unlinkToDeath(this, 0);
+            }
+            synchronized (mLock) {
                 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
                 if (tokenMap == null) {
                     Slogf.w(TAG, "UID %d is not present in active session map", mUid);
@@ -557,8 +565,8 @@
 
         @Override
         public void sendHint(@PerformanceHintManager.Session.Hint int hint) {
-            synchronized (mLock) {
-                if (mHalSessionPtr == 0 || !updateHintAllowed()) {
+            synchronized (this) {
+                if (mHalSessionPtr == 0 || !mUpdateAllowed) {
                     return;
                 }
                 Preconditions.checkArgument(hint >= 0, "the hint ID value should be"
@@ -568,7 +576,7 @@
         }
 
         public void setThreads(@NonNull int[] tids) {
-            synchronized (mLock) {
+            synchronized (this) {
                 if (mHalSessionPtr == 0) {
                     return;
                 }
@@ -588,7 +596,7 @@
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
-                if (!updateHintAllowed()) {
+                if (!mUpdateAllowed) {
                     Slogf.v(TAG, "update hint not allowed, storing tids.");
                     mNewThreadIds = tids;
                     return;
@@ -599,13 +607,15 @@
         }
 
         public int[] getThreadIds() {
-            return mThreadIds;
+            synchronized (this) {
+                return Arrays.copyOf(mThreadIds, mThreadIds.length);
+            }
         }
 
         @Override
         public void setMode(int mode, boolean enabled) {
-            synchronized (mLock) {
-                if (mHalSessionPtr == 0 || !updateHintAllowed()) {
+            synchronized (this) {
+                if (mHalSessionPtr == 0 || !mUpdateAllowed) {
                     return;
                 }
                 Preconditions.checkArgument(mode >= 0, "the mode Id value should be"
@@ -614,19 +624,19 @@
             }
         }
 
-        private void onProcStateChanged() {
-            updateHintAllowed();
+        private void onProcStateChanged(boolean updateAllowed) {
+            updateHintAllowed(updateAllowed);
         }
 
         private void pause() {
-            synchronized (mLock) {
+            synchronized (this) {
                 if (mHalSessionPtr == 0) return;
                 mNativeWrapper.halPauseHintSession(mHalSessionPtr);
             }
         }
 
         private void resume() {
-            synchronized (mLock) {
+            synchronized (this) {
                 if (mHalSessionPtr == 0) return;
                 mNativeWrapper.halResumeHintSession(mHalSessionPtr);
                 if (mNewThreadIds != null) {
@@ -638,12 +648,12 @@
         }
 
         private void dump(PrintWriter pw, String prefix) {
-            synchronized (mLock) {
+            synchronized (this) {
                 pw.println(prefix + "SessionPID: " + mPid);
                 pw.println(prefix + "SessionUID: " + mUid);
                 pw.println(prefix + "SessionTIDs: " + Arrays.toString(mThreadIds));
                 pw.println(prefix + "SessionTargetDurationNanos: " + mTargetDurationNanos);
-                pw.println(prefix + "SessionAllowed: " + updateHintAllowed());
+                pw.println(prefix + "SessionAllowed: " + mUpdateAllowed);
             }
         }
 
diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING
new file mode 100644
index 0000000..10c5362
--- /dev/null
+++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+  "postsubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.power.hint"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index bc90f5c..aadd03b 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -19,7 +19,6 @@
 import android.annotation.CurrentTimeMillisLong;
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
-import android.os.BatteryConsumer;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.IndentingPrintWriter;
@@ -66,17 +65,7 @@
                 aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
         mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()];
         for (int i = 0; i < configs.size(); i++) {
-            mPowerComponentStats[i] = createPowerComponentAggregatedPowerStats(configs.get(i));
-        }
-    }
-
-    private PowerComponentAggregatedPowerStats createPowerComponentAggregatedPowerStats(
-            AggregatedPowerStatsConfig.PowerComponent config) {
-        switch (config.getPowerComponentId()) {
-            case BatteryConsumer.POWER_COMPONENT_CPU:
-                return new CpuAggregatedPowerStats(config);
-            default:
-                return new PowerComponentAggregatedPowerStats(config);
+            mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(configs.get(i));
         }
     }
 
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 477c228..43fd15d 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -16,13 +16,16 @@
 package com.android.server.power.stats;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.BatteryConsumer;
 
 import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -73,6 +76,7 @@
         private final int mPowerComponentId;
         private @TrackedState int[] mTrackedDeviceStates;
         private @TrackedState int[] mTrackedUidStates;
+        private AggregatedPowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
 
         PowerComponent(int powerComponentId) {
             this.mPowerComponentId = powerComponentId;
@@ -94,6 +98,16 @@
             return this;
         }
 
+        /**
+         * Takes an object that should be invoked for every aggregated stats span
+         * before giving the aggregates stats to consumers. The processor can complete the
+         * aggregation process, for example by computing estimated power usage.
+         */
+        public PowerComponent setProcessor(@NonNull AggregatedPowerStatsProcessor processor) {
+            mProcessor = processor;
+            return this;
+        }
+
         public int getPowerComponentId() {
             return mPowerComponentId;
         }
@@ -123,6 +137,11 @@
             };
         }
 
+        @NonNull
+        public AggregatedPowerStatsProcessor getProcessor() {
+            return mProcessor;
+        }
+
         private boolean isTracked(int[] trackedStates, int state) {
             if (trackedStates == null) {
                 return false;
@@ -153,4 +172,21 @@
     public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
         return mPowerComponents;
     }
+
+    private static final AggregatedPowerStatsProcessor NO_OP_PROCESSOR =
+            new AggregatedPowerStatsProcessor() {
+                @Override
+                public void finish(PowerComponentAggregatedPowerStats stats) {
+                }
+
+                @Override
+                public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+                    return Arrays.toString(stats);
+                }
+
+                @Override
+                public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+                    return Arrays.toString(stats);
+                }
+            };
 }
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
new file mode 100644
index 0000000..5fd8ddf
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2023 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.power.stats;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * The power estimation algorithm used by AggregatedPowerStatsProcessor can roughly be
+ * described like this:
+ *
+ * 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using
+ * a metric such as CPU time-in-state.
+ *
+ * 2. Combine estimates obtain in step 1, aggregating across states that are *not* tracked
+ * per UID.
+ *
+ * 2. For each UID, compute the proportion of the combined estimates in each state
+ * and attribute the corresponding portion of the total power estimate in that state to the UID.
+ */
+abstract class AggregatedPowerStatsProcessor {
+    private static final String TAG = "AggregatedPowerStatsProcessor";
+
+    private static final int INDEX_DOES_NOT_EXIST = -1;
+    private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
+
+    abstract void finish(PowerComponentAggregatedPowerStats stats);
+
+    abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats);
+
+    abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats);
+
+    protected static class PowerEstimationPlan {
+        private final AggregatedPowerStatsConfig.PowerComponent mConfig;
+        public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
+        public List<CombinedDeviceStateEstimate> combinedDeviceStateEstimations = new ArrayList<>();
+        public List<UidStateEstimate> uidStateEstimates = new ArrayList<>();
+
+        public PowerEstimationPlan(AggregatedPowerStatsConfig.PowerComponent config) {
+            mConfig = config;
+            addDeviceStateEstimations();
+            combineDeviceStateEstimations();
+            addUidStateEstimations();
+        }
+
+        private void addDeviceStateEstimations() {
+            MultiStateStats.States[] config = mConfig.getDeviceStateConfig();
+            int[][] deviceStateCombinations = getAllTrackedStateCombinations(config);
+            for (int[] deviceStateCombination : deviceStateCombinations) {
+                deviceStateEstimations.add(
+                        new DeviceStateEstimation(config, deviceStateCombination));
+            }
+        }
+
+        private void combineDeviceStateEstimations() {
+            MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
+            MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
+            MultiStateStats.States[] deviceStatesTrackedPerUid =
+                    new MultiStateStats.States[deviceStateConfig.length];
+
+            for (int i = 0; i < deviceStateConfig.length; i++) {
+                if (!deviceStateConfig[i].isTracked()) {
+                    continue;
+                }
+
+                int index = findTrackedStateByName(uidStateConfig, deviceStateConfig[i].getName());
+                if (index != INDEX_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) {
+                    deviceStatesTrackedPerUid[i] = deviceStateConfig[i];
+                }
+            }
+
+            combineDeviceStateEstimationsRecursively(deviceStateConfig, deviceStatesTrackedPerUid,
+                    new int[deviceStateConfig.length], 0);
+        }
+
+        private void combineDeviceStateEstimationsRecursively(
+                MultiStateStats.States[] deviceStateConfig,
+                MultiStateStats.States[] deviceStatesTrackedPerUid, int[] stateValues, int state) {
+            if (state >= deviceStateConfig.length) {
+                DeviceStateEstimation dse = getDeviceStateEstimate(stateValues);
+                CombinedDeviceStateEstimate cdse = getCombinedDeviceStateEstimate(
+                        deviceStatesTrackedPerUid, stateValues);
+                if (cdse == null) {
+                    cdse = new CombinedDeviceStateEstimate(deviceStatesTrackedPerUid, stateValues);
+                    combinedDeviceStateEstimations.add(cdse);
+                }
+                cdse.deviceStateEstimations.add(dse);
+                return;
+            }
+
+            if (deviceStateConfig[state].isTracked()) {
+                for (int stateValue = 0;
+                        stateValue < deviceStateConfig[state].getLabels().length;
+                        stateValue++) {
+                    stateValues[state] = stateValue;
+                    combineDeviceStateEstimationsRecursively(deviceStateConfig,
+                            deviceStatesTrackedPerUid, stateValues, state + 1);
+                }
+            } else {
+                combineDeviceStateEstimationsRecursively(deviceStateConfig,
+                        deviceStatesTrackedPerUid, stateValues, state + 1);
+            }
+        }
+
+        private void addUidStateEstimations() {
+            MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
+            MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
+            MultiStateStats.States[] uidStatesTrackedForDevice =
+                    new MultiStateStats.States[uidStateConfig.length];
+            MultiStateStats.States[] uidStatesNotTrackedForDevice =
+                    new MultiStateStats.States[uidStateConfig.length];
+
+            for (int i = 0; i < uidStateConfig.length; i++) {
+                if (!uidStateConfig[i].isTracked()) {
+                    continue;
+                }
+
+                int index = findTrackedStateByName(deviceStateConfig, uidStateConfig[i].getName());
+                if (index != INDEX_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) {
+                    uidStatesTrackedForDevice[i] = uidStateConfig[i];
+                } else {
+                    uidStatesNotTrackedForDevice[i] = uidStateConfig[i];
+                }
+            }
+
+            @AggregatedPowerStatsConfig.TrackedState
+            int[][] uidStateCombinations = getAllTrackedStateCombinations(uidStateConfig);
+            for (int[] stateValues : uidStateCombinations) {
+                CombinedDeviceStateEstimate combined =
+                        getCombinedDeviceStateEstimate(uidStatesTrackedForDevice, stateValues);
+                if (combined == null) {
+                    // This is not supposed to be possible
+                    Log.wtf(TAG, "Mismatch in UID and combined device states: "
+                                 + concatLabels(uidStatesTrackedForDevice, stateValues));
+                    continue;
+                }
+                UidStateEstimate uidStateEstimate = getUidStateEstimate(combined);
+                if (uidStateEstimate == null) {
+                    uidStateEstimate = new UidStateEstimate(combined, uidStatesNotTrackedForDevice);
+                    uidStateEstimates.add(uidStateEstimate);
+                }
+                uidStateEstimate.proportionalEstimates.add(
+                        new UidStateProportionalEstimate(stateValues));
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Step 1. Compute device-wide power estimates for state combinations:\n");
+            for (DeviceStateEstimation deviceStateEstimation : deviceStateEstimations) {
+                sb.append("    ").append(deviceStateEstimation.id).append("\n");
+            }
+            sb.append("Step 2. Combine device-wide estimates that are untracked per UID:\n");
+            boolean any = false;
+            for (CombinedDeviceStateEstimate cdse : combinedDeviceStateEstimations) {
+                if (cdse.deviceStateEstimations.size() <= 1) {
+                    continue;
+                }
+                any = true;
+                sb.append("    ").append(cdse.id).append(": ");
+                for (int i = 0; i < cdse.deviceStateEstimations.size(); i++) {
+                    if (i != 0) {
+                        sb.append(" + ");
+                    }
+                    sb.append(cdse.deviceStateEstimations.get(i).id);
+                }
+                sb.append("\n");
+            }
+            if (!any) {
+                sb.append("    N/A\n");
+            }
+            sb.append("Step 3. Proportionally distribute power estimates to UIDs:\n");
+            for (UidStateEstimate uidStateEstimate : uidStateEstimates) {
+                sb.append("    ").append(uidStateEstimate.combinedDeviceStateEstimate.id)
+                        .append("\n        among: ");
+                for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
+                    UidStateProportionalEstimate uspe =
+                            uidStateEstimate.proportionalEstimates.get(i);
+                    if (i != 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(concatLabels(uidStateEstimate.states, uspe.stateValues));
+                }
+                sb.append("\n");
+            }
+            return sb.toString();
+        }
+
+        @Nullable
+        public DeviceStateEstimation getDeviceStateEstimate(
+                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+            String label = concatLabels(mConfig.getDeviceStateConfig(), stateValues);
+            for (int i = 0; i < deviceStateEstimations.size(); i++) {
+                DeviceStateEstimation deviceStateEstimation = this.deviceStateEstimations.get(i);
+                if (deviceStateEstimation.id.equals(label)) {
+                    return deviceStateEstimation;
+                }
+            }
+            return null;
+        }
+
+        public CombinedDeviceStateEstimate getCombinedDeviceStateEstimate(
+                MultiStateStats.States[] deviceStates,
+                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+            String label = concatLabels(deviceStates, stateValues);
+            for (int i = 0; i < combinedDeviceStateEstimations.size(); i++) {
+                CombinedDeviceStateEstimate cdse = combinedDeviceStateEstimations.get(i);
+                if (cdse.id.equals(label)) {
+                    return cdse;
+                }
+            }
+            return null;
+        }
+
+        public UidStateEstimate getUidStateEstimate(CombinedDeviceStateEstimate combined) {
+            for (int i = 0; i < uidStateEstimates.size(); i++) {
+                UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
+                if (uidStateEstimate.combinedDeviceStateEstimate == combined) {
+                    return uidStateEstimate;
+                }
+            }
+            return null;
+        }
+
+        public void resetIntermediates() {
+            for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+                deviceStateEstimations.get(i).intermediates = null;
+            }
+            for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+                deviceStateEstimations.get(i).intermediates = null;
+            }
+            for (int i = uidStateEstimates.size() - 1; i >= 0; i--) {
+                UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
+                List<UidStateProportionalEstimate> proportionalEstimates =
+                        uidStateEstimate.proportionalEstimates;
+                for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+                    proportionalEstimates.get(j).intermediates = null;
+                }
+            }
+        }
+    }
+
+    protected static class DeviceStateEstimation {
+        public final String id;
+        @AggregatedPowerStatsConfig.TrackedState
+        public final int[] stateValues;
+        public Object intermediates;
+
+        public DeviceStateEstimation(MultiStateStats.States[] config,
+                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+            id = concatLabels(config, stateValues);
+            this.stateValues = stateValues;
+        }
+    }
+
+    protected static class CombinedDeviceStateEstimate {
+        public final String id;
+        public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
+        public Object intermediates;
+
+        public CombinedDeviceStateEstimate(MultiStateStats.States[] config,
+                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+            id = concatLabels(config, stateValues);
+        }
+    }
+
+    protected static class UidStateEstimate {
+        public final MultiStateStats.States[] states;
+        public CombinedDeviceStateEstimate combinedDeviceStateEstimate;
+        public List<UidStateProportionalEstimate> proportionalEstimates = new ArrayList<>();
+
+        public UidStateEstimate(CombinedDeviceStateEstimate combined,
+                MultiStateStats.States[] states) {
+            combinedDeviceStateEstimate = combined;
+            this.states = states;
+        }
+    }
+
+    protected static class UidStateProportionalEstimate {
+        @AggregatedPowerStatsConfig.TrackedState
+        public final int[] stateValues;
+        public Object intermediates;
+
+        protected UidStateProportionalEstimate(
+                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+            this.stateValues = stateValues;
+        }
+    }
+
+    private static int findTrackedStateByName(MultiStateStats.States[] states, String name) {
+        for (int i = 0; i < states.length; i++) {
+            if (states[i].getName().equals(name)) {
+                return i;
+            }
+        }
+        return INDEX_DOES_NOT_EXIST;
+    }
+
+    @NonNull
+    private static String concatLabels(MultiStateStats.States[] config,
+            @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+        List<String> labels = new ArrayList<>();
+        for (int state = 0; state < config.length; state++) {
+            if (config[state] != null && config[state].isTracked()) {
+                labels.add(config[state].getName()
+                           + "=" + config[state].getLabels()[stateValues[state]]);
+            }
+        }
+        Collections.sort(labels);
+        return labels.toString();
+    }
+
+    @AggregatedPowerStatsConfig.TrackedState
+    private static int[][] getAllTrackedStateCombinations(MultiStateStats.States[] states) {
+        List<int[]> combinations = new ArrayList<>();
+        MultiStateStats.States.forEachTrackedStateCombination(states, stateValues -> {
+            combinations.add(Arrays.copyOf(stateValues, stateValues.length));
+        });
+        return combinations.toArray(new int[combinations.size()][0]);
+    }
+
+    public static double uCtoMah(long chargeUC) {
+        return chargeUC * MILLIAMPHOUR_PER_MICROCOULOMB;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index a9c2bc2..a6558e0 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -10937,7 +10937,8 @@
         }
 
         mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
-                mHandler, mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
+                () -> mBatteryVoltageMv, mHandler,
+                mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
         mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
 
         mStartCount++;
@@ -14437,6 +14438,7 @@
             final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
             final int chargeFullUah, final long chargeTimeToFullSeconds,
             final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) {
+
         // Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
         temp = Math.max(0, temp);
 
@@ -15621,18 +15623,6 @@
         }
     }
 
-    @GuardedBy("this")
-    private void dumpCpuPowerBracketsLocked(PrintWriter pw) {
-        pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):");
-        final int bracketCount = mPowerProfile.getCpuPowerBracketCount();
-        for (int bracket = 0; bracket < bracketCount; bracket++) {
-            pw.print("    ");
-            pw.print(bracket);
-            pw.print(": ");
-            pw.println(mPowerProfile.getCpuPowerBracketDescription(mCpuScalingPolicies, bracket));
-        }
-    }
-
     /**
      * Dump EnergyConsumer stats
      */
@@ -16989,8 +16979,10 @@
             pw.println();
             dumpConstantsLocked(pw);
 
-            pw.println();
-            dumpCpuPowerBracketsLocked(pw);
+            if (mCpuPowerStatsCollector != null) {
+                pw.println();
+                mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw);
+            }
 
             pw.println();
             dumpEnergyConsumerStatsLocked(pw);
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
deleted file mode 100644
index fbf6928..0000000
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2023 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.power.stats;
-
-class CpuAggregatedPowerStats extends PowerComponentAggregatedPowerStats {
-    CpuAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
-        super(config);
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
new file mode 100644
index 0000000..f40eef2
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2023 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.power.stats;
+
+import android.os.BatteryStats;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProcessor {
+    private static final String TAG = "CpuAggregatedPowerStatsProcessor";
+
+    private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
+    private static final int UNKNOWN = -1;
+
+    private final CpuScalingPolicies mCpuScalingPolicies;
+    // Number of CPU core clusters
+    private final int mCpuClusterCount;
+    // Total number of CPU scaling steps across all clusters
+    private final int mCpuScalingStepCount;
+    // Map of scaling step to the corresponding core cluster mScalingStepToCluster[step]->cluster
+    private final int[] mScalingStepToCluster;
+    // Average power consumed by the CPU when it is powered up (per power_profile.xml)
+    private final double mPowerMultiplierForCpuActive;
+    // Average power consumed by each cluster when it is powered up (per power_profile.xml)
+    private final double[] mPowerMultipliersByCluster;
+    // Average power consumed by each scaling step when running code (per power_profile.xml)
+    private final double[] mPowerMultipliersByScalingStep;
+    // A map used to combine energy consumers into a smaller set, in case power brackets
+    // are defined in a way that does not allow an unambiguous mapping of energy consumers to
+    // brackets
+    private int[] mEnergyConsumerToCombinedEnergyConsumerMap;
+    // A map of combined energy consumers to the corresponding collections of power brackets.
+    // For example, if there are two CPU_CLUSTER rails and each maps to three brackets,
+    // this map will look like this:
+    //     0 : [0, 1, 2]
+    //     1 : [3, 4, 5]
+    private int[][] mCombinedEnergyConsumerToPowerBracketMap;
+
+    // Cached reference to a PowerStats descriptor. Almost never changes in practice,
+    // helping to avoid reparsing the descriptor for every PowerStats span.
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    // Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
+    // mLastUsedDescriptor changes
+    private CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+    // Sequence of steps for power estimation and intermediate results.
+    private PowerEstimationPlan mPlan;
+
+    // Temp array for retrieval of device power stats, to avoid repeated allocations
+    private long[] mTmpDeviceStatsArray;
+    // Temp array for retrieval of UID power stats, to avoid repeated allocations
+    private long[] mTmpUidStatsArray;
+
+    public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile,
+            CpuScalingPolicies scalingPolicies) {
+        mCpuScalingPolicies = scalingPolicies;
+        mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
+        mScalingStepToCluster = new int[mCpuScalingStepCount];
+        mPowerMultipliersByScalingStep = new double[mCpuScalingStepCount];
+
+        int step = 0;
+        int[] policies = scalingPolicies.getPolicies();
+        mCpuClusterCount = policies.length;
+        mPowerMultipliersByCluster = new double[mCpuClusterCount];
+        for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+            int policy = policies[cluster];
+            mPowerMultipliersByCluster[cluster] =
+                    powerProfile.getAveragePowerForCpuScalingPolicy(policy) / HOUR_IN_MILLIS;
+            int[] frequencies = scalingPolicies.getFrequencies(policy);
+            for (int i = 0; i < frequencies.length; i++) {
+                mScalingStepToCluster[step] = cluster;
+                mPowerMultipliersByScalingStep[step] =
+                        powerProfile.getAveragePowerForCpuScalingStep(policy, i) / HOUR_IN_MILLIS;
+                step++;
+            }
+        }
+        mPowerMultiplierForCpuActive =
+                powerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE) / HOUR_IN_MILLIS;
+    }
+
+    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+        mStatsLayout.fromExtras(descriptor.extras);
+
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+    }
+
+    /**
+     * Temporary struct to capture intermediate results of power estimation.
+     */
+    private static final class Intermediates {
+        public long uptime;
+        // Sum of all time-in-step values, combining all time-in-step durations across all cores.
+        public long cumulativeTime;
+        // CPU activity durations per cluster
+        public long[] timeByCluster;
+        // Sums of time-in-step values, aggregated by cluster, combining all cores in the cluster.
+        public long[] cumulativeTimeByCluster;
+        public long[] timeByScalingStep;
+        public double[] powerByCluster;
+        public double[] powerByScalingStep;
+        public long[] powerByEnergyConsumer;
+    }
+
+    /**
+     * Temporary struct to capture intermediate results of power estimation.
+     */
+    private static class DeviceStatsIntermediates {
+        public double power;
+        public long[] timeByBracket;
+        public double[] powerByBracket;
+    }
+
+    @Override
+    public void finish(PowerComponentAggregatedPowerStats stats) {
+        if (stats.getPowerStatsDescriptor() == null) {
+            return;
+        }
+
+        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+            if (mStatsLayout.getCpuClusterEnergyConsumerCount() != 0) {
+                initEnergyConsumerToPowerBracketMaps();
+            }
+        }
+
+        Intermediates intermediates = new Intermediates();
+
+        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+        if (cpuScalingStepCount != mCpuScalingStepCount) {
+            Log.e(TAG, "Mismatched CPU scaling step count in PowerStats: " + cpuScalingStepCount
+                       + ", expected: " + mCpuScalingStepCount);
+            return;
+        }
+
+        int clusterCount = mStatsLayout.getCpuClusterCount();
+        if (clusterCount != mCpuClusterCount) {
+            Log.e(TAG, "Mismatched CPU cluster count in PowerStats: " + clusterCount
+                       + ", expected: " + mCpuClusterCount);
+            return;
+        }
+
+        computeTotals(stats, intermediates);
+        if (intermediates.cumulativeTime == 0) {
+            return;
+        }
+
+        estimatePowerByScalingStep(intermediates);
+        estimatePowerByDeviceState(stats, intermediates);
+        combineDeviceStateEstimates();
+
+        ArrayList<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+        if (!uids.isEmpty()) {
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+        }
+        mPlan.resetIntermediates();
+    }
+
+    /*
+     * Populate data structures (two maps) needed to use power rail data, aka energy consumers,
+     * to attribute power usage to apps.
+     *
+     * At this point, the algorithm covers only the most basic cases:
+     * - Each cluster is mapped to unique power brackets (possibly multiple for each cluster):
+     *          CL_0: [bracket0, bracket1]
+     *          CL_1: [bracket3]
+     *      In this case, the consumed energy is distributed  to the corresponding brackets
+     *      proportionally.
+     * - Brackets span multiple clusters:
+     *          CL_0: [bracket0, bracket1]
+     *          CL_1: [bracket1, bracket2]
+     *          CL_2: [bracket3, bracket4]
+     *      In this case, we combine energy consumers into groups unambiguously mapped to
+     *      brackets. In the above example, consumed energy for CL_0 and CL_1 will be combined
+     *      because they both map to the same power bracket (bracket1):
+     *          (CL_0+CL_1): [bracket0, bracket1, bracket2]
+     *          CL_2: [bracket3, bracket4]
+     */
+    private void initEnergyConsumerToPowerBracketMaps() {
+        int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+
+        mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
+        mCombinedEnergyConsumerToPowerBracketMap = new int[energyConsumerCount][];
+
+        int[] policies = mCpuScalingPolicies.getPolicies();
+        if (energyConsumerCount == policies.length) {
+            int[] scalingStepToPowerBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
+            ArraySet<Integer>[] clusterToBrackets = new ArraySet[policies.length];
+            int step = 0;
+            for (int cluster = 0; cluster < policies.length; cluster++) {
+                int[] freqs = mCpuScalingPolicies.getFrequencies(policies[cluster]);
+                clusterToBrackets[cluster] = new ArraySet<>(freqs.length);
+                for (int j = 0; j < freqs.length; j++) {
+                    clusterToBrackets[cluster].add(scalingStepToPowerBracketMap[step++]);
+                }
+            }
+
+            ArraySet<Integer>[] combinedEnergyConsumers = new ArraySet[policies.length];
+            int combinedEnergyConsumersCount = 0;
+
+            for (int cluster = 0; cluster < clusterToBrackets.length; cluster++) {
+                int combineWith = UNKNOWN;
+                for (int i = 0; i < combinedEnergyConsumersCount; i++) {
+                    if (containsAny(combinedEnergyConsumers[i], clusterToBrackets[cluster])) {
+                        combineWith = i;
+                        break;
+                    }
+                }
+                if (combineWith != UNKNOWN) {
+                    mEnergyConsumerToCombinedEnergyConsumerMap[cluster] = combineWith;
+                    combinedEnergyConsumers[combineWith].addAll(clusterToBrackets[cluster]);
+                } else {
+                    mEnergyConsumerToCombinedEnergyConsumerMap[cluster] =
+                            combinedEnergyConsumersCount;
+                    combinedEnergyConsumers[combinedEnergyConsumersCount++] =
+                            clusterToBrackets[cluster];
+                }
+            }
+
+            for (int i = combinedEnergyConsumers.length - 1; i >= 0; i--) {
+                mCombinedEnergyConsumerToPowerBracketMap[i] =
+                        new int[combinedEnergyConsumers[i].size()];
+                for (int j = combinedEnergyConsumers[i].size() - 1; j >= 0; j--) {
+                    mCombinedEnergyConsumerToPowerBracketMap[i][j] =
+                            combinedEnergyConsumers[i].valueAt(j);
+                }
+            }
+        } else {
+            // All CPU cluster energy consumers are combined into one, which is
+            // distributed proportionally to all power brackets.
+            int[] map = new int[powerBracketCount];
+            for (int i = 0; i < map.length; i++) {
+                map[i] = i;
+            }
+            mCombinedEnergyConsumerToPowerBracketMap[0] = map;
+        }
+    }
+
+    private static boolean containsAny(ArraySet<Integer> set1, ArraySet<Integer> set2) {
+        for (int i = 0; i < set2.size(); i++) {
+            if (set1.contains(set2.valueAt(i))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void computeTotals(PowerComponentAggregatedPowerStats stats,
+            Intermediates intermediates) {
+        intermediates.timeByScalingStep = new long[mCpuScalingStepCount];
+        intermediates.timeByCluster = new long[mCpuClusterCount];
+        intermediates.cumulativeTimeByCluster = new long[mCpuClusterCount];
+
+        List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
+        for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues)) {
+                continue;
+            }
+
+            intermediates.uptime += mStatsLayout.getUptime(mTmpDeviceStatsArray);
+
+            for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+                intermediates.timeByCluster[cluster] +=
+                        mStatsLayout.getTimeByCluster(mTmpDeviceStatsArray, cluster);
+            }
+
+            for (int step = 0; step < mCpuScalingStepCount; step++) {
+                long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
+                intermediates.cumulativeTime += timeInStep;
+                intermediates.timeByScalingStep[step] += timeInStep;
+                intermediates.cumulativeTimeByCluster[mScalingStepToCluster[step]] += timeInStep;
+            }
+        }
+    }
+
+    private void estimatePowerByScalingStep(Intermediates intermediates) {
+        // CPU consumes some power when it's on - no matter which cores are running.
+        double cpuActivePower = mPowerMultiplierForCpuActive * intermediates.uptime;
+
+        // Additionally, every cluster consumes some power when any of its cores are running
+        intermediates.powerByCluster = new double[mCpuClusterCount];
+        for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+            intermediates.powerByCluster[cluster] =
+                    mPowerMultipliersByCluster[cluster] * intermediates.timeByCluster[cluster];
+        }
+
+        // Finally, additional power is consumed depending on the frequency scaling
+        intermediates.powerByScalingStep = new double[mCpuScalingStepCount];
+        for (int step = 0; step < mCpuScalingStepCount; step++) {
+            int cluster = mScalingStepToCluster[step];
+
+            double power;
+
+            // Distribute base power proportionally
+            power = cpuActivePower * intermediates.timeByScalingStep[step]
+                    / intermediates.cumulativeTime;
+
+            // Distribute per-cluster power proportionally
+            long cumulativeTimeInCluster = intermediates.cumulativeTimeByCluster[cluster];
+            if (cumulativeTimeInCluster != 0) {
+                power += intermediates.powerByCluster[cluster]
+                         * intermediates.timeByScalingStep[step]
+                         / cumulativeTimeInCluster;
+            }
+
+            power += mPowerMultipliersByScalingStep[step] * intermediates.timeByScalingStep[step];
+
+            intermediates.powerByScalingStep[step] = power;
+        }
+    }
+
+    private void estimatePowerByDeviceState(PowerComponentAggregatedPowerStats stats,
+            Intermediates intermediates) {
+        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+        int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
+        int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+        List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
+        for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) {
+            DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse);
+            deviceStateEstimation.intermediates = new DeviceStatsIntermediates();
+            DeviceStatsIntermediates deviceStatsIntermediates =
+                    (DeviceStatsIntermediates) deviceStateEstimation.intermediates;
+            deviceStatsIntermediates.timeByBracket = new long[powerBracketCount];
+            deviceStatsIntermediates.powerByBracket = new double[powerBracketCount];
+
+            stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues);
+            for (int step = 0; step < cpuScalingStepCount; step++) {
+                if (intermediates.timeByScalingStep[step] == 0) {
+                    continue;
+                }
+
+                long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
+                double stepPower = intermediates.powerByScalingStep[step] * timeInStep
+                                   / intermediates.timeByScalingStep[step];
+
+                int bracket = scalingStepToBracketMap[step];
+                deviceStatsIntermediates.timeByBracket[bracket] += timeInStep;
+                deviceStatsIntermediates.powerByBracket[bracket] += stepPower;
+            }
+
+            if (energyConsumerCount != 0) {
+                adjustEstimatesUsingEnergyConsumers(intermediates, deviceStatsIntermediates);
+            }
+
+            double power = 0;
+            for (int i = deviceStatsIntermediates.powerByBracket.length - 1; i >= 0; i--) {
+                power += deviceStatsIntermediates.powerByBracket[i];
+            }
+            deviceStatsIntermediates.power = power;
+            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+            stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+
+    private void adjustEstimatesUsingEnergyConsumers(
+            Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) {
+        int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+        if (energyConsumerCount == 0) {
+            return;
+        }
+
+        if (intermediates.powerByEnergyConsumer == null) {
+            intermediates.powerByEnergyConsumer = new long[energyConsumerCount];
+        } else {
+            Arrays.fill(intermediates.powerByEnergyConsumer, 0);
+        }
+        for (int i = 0; i < energyConsumerCount; i++) {
+            intermediates.powerByEnergyConsumer[mEnergyConsumerToCombinedEnergyConsumerMap[i]] +=
+                    mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+        }
+
+        for (int combinedConsumer = mCombinedEnergyConsumerToPowerBracketMap.length - 1;
+                combinedConsumer >= 0; combinedConsumer--) {
+            int[] combinedEnergyConsumerToPowerBracketMap =
+                    mCombinedEnergyConsumerToPowerBracketMap[combinedConsumer];
+            if (combinedEnergyConsumerToPowerBracketMap == null) {
+                continue;
+            }
+
+            double consumedEnergy = uCtoMah(intermediates.powerByEnergyConsumer[combinedConsumer]);
+
+            double totalModeledPower = 0;
+            for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
+                totalModeledPower += deviceStatsIntermediates.powerByBracket[bracket];
+            }
+            if (totalModeledPower == 0) {
+                continue;
+            }
+
+            for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
+                deviceStatsIntermediates.powerByBracket[bracket] =
+                        consumedEnergy * deviceStatsIntermediates.powerByBracket[bracket]
+                        / totalModeledPower;
+            }
+        }
+    }
+
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            DeviceStatsIntermediates cdseIntermediates = new DeviceStatsIntermediates();
+            cdse.intermediates = cdseIntermediates;
+            int bracketCount = mStatsLayout.getCpuPowerBracketCount();
+            cdseIntermediates.timeByBracket = new long[bracketCount];
+            cdseIntermediates.powerByBracket = new double[bracketCount];
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                DeviceStatsIntermediates intermediates =
+                        (DeviceStatsIntermediates) dse.intermediates;
+                cdseIntermediates.power += intermediates.power;
+                for (int k = 0; k < bracketCount; k++) {
+                    cdseIntermediates.timeByBracket[k] += intermediates.timeByBracket[k];
+                    cdseIntermediates.powerByBracket[k] += intermediates.powerByBracket[k];
+                }
+            }
+        }
+    }
+
+    private void estimateUidPowerConsumption(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        CombinedDeviceStateEstimate combinedDeviceStateEstimate =
+                uidStateEstimate.combinedDeviceStateEstimate;
+        DeviceStatsIntermediates cdsIntermediates =
+                (DeviceStatsIntermediates) combinedDeviceStateEstimate.intermediates;
+        for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
+            UidStateProportionalEstimate proportionalEstimate =
+                    uidStateEstimate.proportionalEstimates.get(i);
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            for (int bracket = 0; bracket < mStatsLayout.getCpuPowerBracketCount(); bracket++) {
+                if (cdsIntermediates.timeByBracket[bracket] == 0) {
+                    continue;
+                }
+
+                long timeInBracket = mStatsLayout.getUidTimeByPowerBracket(mTmpUidStatsArray,
+                        bracket);
+                if (timeInBracket == 0) {
+                    continue;
+                }
+
+                power += cdsIntermediates.powerByBracket[bracket] * timeInBracket
+                            / cdsIntermediates.timeByBracket[bracket];
+            }
+
+            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+        }
+    }
+
+    @Override
+    public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+        unpackPowerStatsDescriptor(descriptor);
+        StringBuilder sb = new StringBuilder();
+        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+        sb.append("steps: [");
+        for (int step = 0; step < cpuScalingStepCount; step++) {
+            if (step != 0) {
+                sb.append(", ");
+            }
+            sb.append(mStatsLayout.getTimeByScalingStep(stats, step));
+        }
+        int clusterCount = mStatsLayout.getCpuClusterCount();
+        sb.append("] clusters: [");
+        for (int cluster = 0; cluster < clusterCount; cluster++) {
+            if (cluster != 0) {
+                sb.append(", ");
+            }
+            sb.append(mStatsLayout.getTimeByCluster(stats, cluster));
+        }
+        sb.append("] uptime: ").append(mStatsLayout.getUptime(stats));
+        int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+        if (energyConsumerCount > 0) {
+            sb.append(" energy: [");
+            for (int i = 0; i < energyConsumerCount; i++) {
+                if (i != 0) {
+                    sb.append(", ");
+                }
+                sb.append(mStatsLayout.getConsumedEnergy(stats, i));
+            }
+            sb.append("]");
+        }
+        sb.append(" power: ").append(
+                BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats)));
+        return sb.toString();
+    }
+
+    @Override
+    public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+        unpackPowerStatsDescriptor(descriptor);
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+        for (int bracket = 0; bracket < powerBracketCount; bracket++) {
+            if (bracket != 0) {
+                sb.append(", ");
+            }
+            sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket));
+        }
+        sb.append("] power: ").append(
+                BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats)));
+        return sb.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index 376ca89..a388932 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -16,19 +16,39 @@
 
 package com.android.server.power.stats;
 
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 import android.os.Handler;
 import android.os.PersistableBundle;
+import android.power.PowerStatsInternal;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.Keep;
 import com.android.internal.annotations.VisibleForNative;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.Clock;
 import com.android.internal.os.CpuScalingPolicies;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.PowerStats;
+import com.android.server.LocalServices;
 import com.android.server.power.optimization.Flags;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.IntSupplier;
+import java.util.function.Supplier;
+
 /**
  * Collects snapshots of power-related system statistics.
  * <p>
@@ -36,45 +56,350 @@
  * constructor. Thus the object is not thread-safe except where noted.
  */
 public class CpuPowerStatsCollector extends PowerStatsCollector {
+    private static final String TAG = "CpuPowerStatsCollector";
     private static final long NANOS_PER_MILLIS = 1000000;
+    private static final long ENERGY_UNSPECIFIED = -1;
+    private static final int DEFAULT_CPU_POWER_BRACKETS = 3;
+    private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2;
+    private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
 
+    private boolean mIsInitialized;
+    private final CpuScalingPolicies mCpuScalingPolicies;
+    private final PowerProfile mPowerProfile;
     private final KernelCpuStatsReader mKernelCpuStatsReader;
-    private final int[] mScalingStepToPowerBracketMap;
-    private final long[] mTempUidStats;
+    private final Supplier<PowerStatsInternal> mPowerStatsSupplier;
+    private final IntSupplier mVoltageSupplier;
+    private final int mDefaultCpuPowerBrackets;
+    private final int mDefaultCpuPowerBracketsPerEnergyConsumer;
+    private long[] mCpuTimeByScalingStep;
+    private long[] mTempCpuTimeByScalingStep;
+    private long[] mTempUidStats;
     private final SparseArray<UidStats> mUidStats = new SparseArray<>();
-    private final int mUidStatsSize;
+    private boolean mIsPerUidTimeInStateSupported;
+    private PowerStatsInternal mPowerStatsInternal;
+    private int[] mCpuEnergyConsumerIds;
+    private PowerStats.Descriptor mPowerStatsDescriptor;
     // Reusable instance
-    private final PowerStats mCpuPowerStats;
+    private PowerStats mCpuPowerStats;
+    private StatsArrayLayout mLayout;
     private long mLastUpdateTimestampNanos;
+    private long mLastUpdateUptimeMillis;
+    private int mLastVoltageMv;
+    private long[] mLastConsumedEnergyUws;
+
+    /**
+     * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+     * power usage estimates etc.
+     */
+    public static class StatsArrayLayout {
+        private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
+        private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
+        private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
+        private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
+        private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
+        private static final String EXTRA_DEVICE_UPTIME_POSITION = "du";
+        private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
+        private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+        private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
+        private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
+        private static final String EXTRA_UID_POWER_POSITION = "up";
+
+        private static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+
+        private int mDeviceStatsArrayLength;
+        private int mUidStatsArrayLength;
+
+        private int mDeviceCpuTimeByScalingStepPosition;
+        private int mDeviceCpuTimeByScalingStepCount;
+        private int mDeviceCpuTimeByClusterPosition;
+        private int mDeviceCpuTimeByClusterCount;
+        private int mDeviceCpuUptimePosition;
+        private int mDeviceEnergyConsumerPosition;
+        private int mDeviceEnergyConsumerCount;
+        private int mDevicePowerEstimatePosition;
+
+        private int mUidPowerBracketsPosition;
+        private int mUidPowerBracketCount;
+        private int[][] mEnergyConsumerToPowerBucketMaps;
+        private int mUidPowerEstimatePosition;
+
+        private int[] mScalingStepToPowerBracketMap;
+
+        public int getDeviceStatsArrayLength() {
+            return mDeviceStatsArrayLength;
+        }
+
+        public int getUidStatsArrayLength() {
+            return mUidStatsArrayLength;
+        }
+
+        /**
+         * Declare that the stats array has a section capturing CPU time per scaling step
+         */
+        public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
+            mDeviceCpuTimeByScalingStepPosition = mDeviceStatsArrayLength;
+            mDeviceCpuTimeByScalingStepCount = scalingStepCount;
+            mDeviceStatsArrayLength += scalingStepCount;
+        }
+
+        public int getCpuScalingStepCount() {
+            return mDeviceCpuTimeByScalingStepCount;
+        }
+
+        /**
+         * Saves the time duration in the <code>stats</code> element
+         * corresponding to the CPU scaling <code>state</code>.
+         */
+        public void setTimeByScalingStep(long[] stats, int step, long value) {
+            stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
+        }
+
+        /**
+         * Extracts the time duration from the <code>stats</code> element
+         * corresponding to the CPU scaling <code>step</code>.
+         */
+        public long getTimeByScalingStep(long[] stats, int step) {
+            return stats[mDeviceCpuTimeByScalingStepPosition + step];
+        }
+
+        /**
+         * Declare that the stats array has a section capturing CPU time in each cluster
+         */
+        public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+            mDeviceCpuTimeByClusterCount = clusterCount;
+            mDeviceCpuTimeByClusterPosition = mDeviceStatsArrayLength;
+            mDeviceStatsArrayLength += clusterCount;
+        }
+
+        public int getCpuClusterCount() {
+            return mDeviceCpuTimeByClusterCount;
+        }
+
+        /**
+         * Saves the time duration in the <code>stats</code> element
+         * corresponding to the CPU <code>cluster</code>.
+         */
+        public void setTimeByCluster(long[] stats, int cluster, long value) {
+            stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
+        }
+
+        /**
+         * Extracts the time duration from the <code>stats</code> element
+         * corresponding to the CPU <code>cluster</code>.
+         */
+        public long getTimeByCluster(long[] stats, int cluster) {
+            return stats[mDeviceCpuTimeByClusterPosition + cluster];
+        }
+
+        /**
+         * Declare that the stats array has a section capturing CPU uptime
+         */
+        public void addDeviceSectionUptime() {
+            mDeviceCpuUptimePosition = mDeviceStatsArrayLength++;
+        }
+
+        /**
+         * Saves the CPU uptime duration in the corresponding <code>stats</code> element.
+         */
+        public void setUptime(long[] stats, long value) {
+            stats[mDeviceCpuUptimePosition] = value;
+        }
+
+        /**
+         * Extracts the CPU uptime duration from the corresponding <code>stats</code> element.
+         */
+        public long getUptime(long[] stats) {
+            return stats[mDeviceCpuUptimePosition];
+        }
+
+        /**
+         * Declares that the stats array has a section capturing EnergyConsumer data from
+         * PowerStatsService.
+         */
+        public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+            mDeviceEnergyConsumerPosition = mDeviceStatsArrayLength;
+            mDeviceEnergyConsumerCount = energyConsumerCount;
+            mDeviceStatsArrayLength += energyConsumerCount;
+        }
+
+        public int getCpuClusterEnergyConsumerCount() {
+            return mDeviceEnergyConsumerCount;
+        }
+
+        /**
+         * Saves the accumulated energy for the specified rail the corresponding
+         * <code>stats</code> element.
+         */
+        public void setConsumedEnergy(long[] stats, int index, long energy) {
+            stats[mDeviceEnergyConsumerPosition + index] = energy;
+        }
+
+        /**
+         * Extracts the EnergyConsumer data from a device stats array for the specified
+         * EnergyConsumer.
+         */
+        public long getConsumedEnergy(long[] stats, int index) {
+            return stats[mDeviceEnergyConsumerPosition + index];
+        }
+
+        /**
+         * Declare that the stats array has a section capturing a power estimate
+         */
+        public void addDeviceSectionPowerEstimate() {
+            mDevicePowerEstimatePosition = mDeviceStatsArrayLength++;
+        }
+
+        /**
+         * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+         * element of <code>stats</code>.
+         */
+        public void setDevicePowerEstimate(long[] stats, double power) {
+            stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+        }
+
+        /**
+         * Extracts the power estimate from a device stats array and converts it to mAh.
+         */
+        public double getDevicePowerEstimate(long[] stats) {
+            return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+        }
+
+        /**
+         * Declare that the UID stats array has a section capturing CPU time per power bracket.
+         */
+        public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
+            mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
+            mUidPowerBracketsPosition = mUidStatsArrayLength;
+            updatePowerBracketCount();
+            mUidStatsArrayLength += mUidPowerBracketCount;
+        }
+
+        private void updatePowerBracketCount() {
+            mUidPowerBracketCount = 1;
+            for (int bracket : mScalingStepToPowerBracketMap) {
+                if (bracket >= mUidPowerBracketCount) {
+                    mUidPowerBracketCount = bracket + 1;
+                }
+            }
+        }
+
+        public int[] getScalingStepToPowerBracketMap() {
+            return mScalingStepToPowerBracketMap;
+        }
+
+        public int getCpuPowerBracketCount() {
+            return mUidPowerBracketCount;
+        }
+
+        /**
+         * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
+         */
+        public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
+            stats[mUidPowerBracketsPosition + bracket] = value;
+        }
+
+        /**
+         * Extracts the time in <code>bracket</code> from a UID stats array.
+         */
+        public long getUidTimeByPowerBracket(long[] stats, int bracket) {
+            return stats[mUidPowerBracketsPosition + bracket];
+        }
+
+        /**
+         * Declare that the UID stats array has a section capturing a power estimate
+         */
+        public void addUidSectionPowerEstimate() {
+            mUidPowerEstimatePosition = mUidStatsArrayLength++;
+        }
+
+        /**
+         * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+         * element of <code>stats</code>.
+         */
+        public void setUidPowerEstimate(long[] stats, double power) {
+            stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+        }
+
+        /**
+         * Extracts the power estimate from a UID stats array and converts it to mAh.
+         */
+        public double getUidPowerEstimate(long[] stats) {
+            return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+        }
+
+        /**
+         * Copies the elements of the stats array layout into <code>extras</code>
+         */
+        public void toExtras(PersistableBundle extras) {
+            extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
+                    mDeviceCpuTimeByScalingStepPosition);
+            extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
+                    mDeviceCpuTimeByScalingStepCount);
+            extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
+                    mDeviceCpuTimeByClusterPosition);
+            extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
+                    mDeviceCpuTimeByClusterCount);
+            extras.putInt(EXTRA_DEVICE_UPTIME_POSITION, mDeviceCpuUptimePosition);
+            extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
+                    mDeviceEnergyConsumerPosition);
+            extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
+                    mDeviceEnergyConsumerCount);
+            extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+            extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
+            extras.putIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+                    mScalingStepToPowerBracketMap);
+            extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+        }
+
+        /**
+         * Retrieves elements of the stats array layout from <code>extras</code>
+         */
+        public void fromExtras(PersistableBundle extras) {
+            mDeviceCpuTimeByScalingStepPosition =
+                    extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
+            mDeviceCpuTimeByScalingStepCount =
+                    extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
+            mDeviceCpuTimeByClusterPosition =
+                    extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
+            mDeviceCpuTimeByClusterCount =
+                    extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
+            mDeviceCpuUptimePosition = extras.getInt(EXTRA_DEVICE_UPTIME_POSITION);
+            mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+            mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+            mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+            mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
+            mScalingStepToPowerBracketMap =
+                    extras.getIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+            if (mScalingStepToPowerBracketMap == null) {
+                mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
+            }
+            updatePowerBracketCount();
+            mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+        }
+    }
 
     public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
-                                  Handler handler, long throttlePeriodMs) {
+            IntSupplier voltageSupplier, Handler handler, long throttlePeriodMs) {
         this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(),
-                throttlePeriodMs, Clock.SYSTEM_CLOCK);
+                () -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier,
+                throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS,
+                DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER);
     }
 
     public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
                                   Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
-                                  long throttlePeriodMs, Clock clock) {
+            Supplier<PowerStatsInternal> powerStatsSupplier,
+            IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock,
+            int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
         super(handler, throttlePeriodMs, clock);
+        mCpuScalingPolicies = cpuScalingPolicies;
+        mPowerProfile = powerProfile;
         mKernelCpuStatsReader = kernelCpuStatsReader;
+        mPowerStatsSupplier = powerStatsSupplier;
+        mVoltageSupplier = voltageSupplier;
+        mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
+        mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
 
-        int scalingStepCount = cpuScalingPolicies.getScalingStepCount();
-        mScalingStepToPowerBracketMap = new int[scalingStepCount];
-        int index = 0;
-        for (int policy : cpuScalingPolicies.getPolicies()) {
-            int[] frequencies = cpuScalingPolicies.getFrequencies(policy);
-            for (int step = 0; step < frequencies.length; step++) {
-                int bracket = powerProfile.getCpuPowerBracketForScalingStep(policy, step);
-                mScalingStepToPowerBracketMap[index++] = bracket;
-            }
-        }
-        mUidStatsSize = powerProfile.getCpuPowerBracketCount();
-        mTempUidStats = new long[mUidStatsSize];
-
-        mCpuPowerStats = new PowerStats(
-                new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 0, mUidStatsSize,
-                        new PersistableBundle()));
     }
 
     /**
@@ -84,43 +409,355 @@
         setEnabled(Flags.streamlinedBatteryStats());
     }
 
+    private void ensureInitialized() {
+        if (mIsInitialized) {
+            return;
+        }
+
+        if (!isEnabled()) {
+            return;
+        }
+
+        mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature();
+        mPowerStatsInternal = mPowerStatsSupplier.get();
+
+        if (mPowerStatsInternal != null) {
+            readCpuEnergyConsumerIds();
+        } else {
+            mCpuEnergyConsumerIds = new int[0];
+        }
+
+        int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
+        mCpuTimeByScalingStep = new long[cpuScalingStepCount];
+        mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
+        int[] scalingStepToPowerBracketMap = initPowerBrackets();
+
+        mLayout = new StatsArrayLayout();
+        mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
+        mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
+        mLayout.addDeviceSectionUptime();
+        mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length);
+        mLayout.addDeviceSectionPowerEstimate();
+        mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
+        mLayout.addUidSectionPowerEstimate();
+
+        PersistableBundle extras = new PersistableBundle();
+        mLayout.toExtras(extras);
+
+        mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+                mLayout.getDeviceStatsArrayLength(), mLayout.getUidStatsArrayLength(), extras);
+        mCpuPowerStats = new PowerStats(mPowerStatsDescriptor);
+
+        mTempUidStats = new long[mLayout.getCpuPowerBracketCount()];
+
+        mIsInitialized = true;
+    }
+
+    private void readCpuEnergyConsumerIds() {
+        EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
+        if (energyConsumerInfo == null) {
+            mCpuEnergyConsumerIds = new int[0];
+            return;
+        }
+
+        List<EnergyConsumer> cpuEnergyConsumers = new ArrayList<>();
+        for (EnergyConsumer energyConsumer : energyConsumerInfo) {
+            if (energyConsumer.type == EnergyConsumerType.CPU_CLUSTER) {
+                cpuEnergyConsumers.add(energyConsumer);
+            }
+        }
+        if (cpuEnergyConsumers.isEmpty()) {
+            return;
+        }
+
+        cpuEnergyConsumers.sort(Comparator.comparing(c -> c.ordinal));
+
+        mCpuEnergyConsumerIds = new int[cpuEnergyConsumers.size()];
+        for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) {
+            mCpuEnergyConsumerIds[i] = cpuEnergyConsumers.get(i).id;
+        }
+        mLastConsumedEnergyUws = new long[cpuEnergyConsumers.size()];
+        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+    }
+
+    private int[] initPowerBrackets() {
+        if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) {
+            return initPowerBracketsFromPowerProfile();
+        } else if (mCpuEnergyConsumerIds.length == 0 || mCpuEnergyConsumerIds.length == 1) {
+            return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
+        } else if (mCpuScalingPolicies.getPolicies().length == mCpuEnergyConsumerIds.length) {
+            return initPowerBracketsByCluster(mDefaultCpuPowerBracketsPerEnergyConsumer);
+        } else {
+            Slog.i(TAG, "Assigning a single power brackets to each CPU_CLUSTER energy consumer."
+                        + " Number of CPU clusters ("
+                        + mCpuScalingPolicies.getPolicies().length
+                        + ") does not match the number of energy consumers ("
+                        + mCpuEnergyConsumerIds.length + "). "
+                        + " Using default power bucket assignment.");
+            return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
+        }
+    }
+
+    private int[] initPowerBracketsFromPowerProfile() {
+        int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
+        int index = 0;
+        for (int policy : mCpuScalingPolicies.getPolicies()) {
+            int[] frequencies = mCpuScalingPolicies.getFrequencies(policy);
+            for (int step = 0; step < frequencies.length; step++) {
+                int bracket = mPowerProfile.getCpuPowerBracketForScalingStep(policy, step);
+                stepToBracketMap[index++] = bracket;
+            }
+        }
+        return stepToBracketMap;
+    }
+
+    private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) {
+        int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
+        int index = 0;
+        int bracketBase = 0;
+        int[] policies = mCpuScalingPolicies.getPolicies();
+        for (int policy : policies) {
+            int[] frequencies = mCpuScalingPolicies.getFrequencies(policy);
+            double[] powerByStep = new double[frequencies.length];
+            for (int step = 0; step < frequencies.length; step++) {
+                powerByStep[step] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step);
+            }
+
+            int[] policyStepToBracketMap = new int[frequencies.length];
+            mapScalingStepsToDefaultBrackets(policyStepToBracketMap, powerByStep,
+                    defaultBracketCountPerCluster);
+            int maxBracket = 0;
+            for (int step = 0; step < frequencies.length; step++) {
+                int bracket = bracketBase + policyStepToBracketMap[step];
+                stepToBracketMap[index++] = bracket;
+                if (bracket > maxBracket) {
+                    maxBracket = bracket;
+                }
+            }
+            bracketBase = maxBracket + 1;
+        }
+        return stepToBracketMap;
+    }
+
+    private int[] initDefaultPowerBrackets(int defaultCpuPowerBracketCount) {
+        int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
+        double[] powerByStep = new double[mCpuScalingPolicies.getScalingStepCount()];
+        int index = 0;
+        int[] policies = mCpuScalingPolicies.getPolicies();
+        for (int policy : policies) {
+            int[] frequencies = mCpuScalingPolicies.getFrequencies(policy);
+            for (int step = 0; step < frequencies.length; step++) {
+                powerByStep[index++] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step);
+            }
+        }
+        mapScalingStepsToDefaultBrackets(stepToBracketMap, powerByStep,
+                defaultCpuPowerBracketCount);
+        return stepToBracketMap;
+    }
+
+    private static void mapScalingStepsToDefaultBrackets(int[] stepToBracketMap,
+            double[] powerByStep, int defaultCpuPowerBracketCount) {
+        double minPower = Double.MAX_VALUE;
+        double maxPower = Double.MIN_VALUE;
+        for (final double power : powerByStep) {
+            if (power < minPower) {
+                minPower = power;
+            }
+            if (power > maxPower) {
+                maxPower = power;
+            }
+        }
+        if (powerByStep.length <= defaultCpuPowerBracketCount) {
+            for (int index = 0; index < stepToBracketMap.length; index++) {
+                stepToBracketMap[index] = index;
+            }
+        } else {
+            final double minLogPower = Math.log(minPower);
+            final double logBracket = (Math.log(maxPower) - minLogPower)
+                                      / defaultCpuPowerBracketCount;
+
+            for (int step = 0; step < powerByStep.length; step++) {
+                int bracket = (int) ((Math.log(powerByStep[step]) - minLogPower) / logBracket);
+                if (bracket >= defaultCpuPowerBracketCount) {
+                    bracket = defaultCpuPowerBracketCount - 1;
+                }
+                stepToBracketMap[step] = bracket;
+            }
+        }
+    }
+
+    /**
+     * Prints the definitions of power brackets.
+     */
+    public void dumpCpuPowerBracketsLocked(PrintWriter pw) {
+        ensureInitialized();
+
+        pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):");
+        for (int bracket = 0; bracket < mLayout.getCpuPowerBracketCount(); bracket++) {
+            pw.print("    ");
+            pw.print(bracket);
+            pw.print(": ");
+            pw.println(getCpuPowerBracketDescription(bracket));
+        }
+    }
+
+    /**
+     * Description of a CPU power bracket: which cluster/frequency combinations are included.
+     */
+    @VisibleForTesting
+    public String getCpuPowerBracketDescription(int powerBracket) {
+        ensureInitialized();
+
+        int[] stepToPowerBracketMap = mLayout.getScalingStepToPowerBracketMap();
+        StringBuilder sb = new StringBuilder();
+        int index = 0;
+        int[] policies = mCpuScalingPolicies.getPolicies();
+        for (int policy : policies) {
+            int[] freqs = mCpuScalingPolicies.getFrequencies(policy);
+            for (int step = 0; step < freqs.length; step++) {
+                if (stepToPowerBracketMap[index] != powerBracket) {
+                    index++;
+                    continue;
+                }
+
+                if (sb.length() != 0) {
+                    sb.append(", ");
+                }
+                if (policies.length > 1) {
+                    sb.append(policy).append('/');
+                }
+                sb.append(freqs[step] / 1000);
+                sb.append('(');
+                sb.append(String.format(Locale.US, "%.1f",
+                        mPowerProfile.getAveragePowerForCpuScalingStep(policy, step)));
+                sb.append(')');
+
+                index++;
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns the descriptor of PowerStats produced by this collector.
+     */
+    @VisibleForTesting
+    public PowerStats.Descriptor getPowerStatsDescriptor() {
+        ensureInitialized();
+
+        return mPowerStatsDescriptor;
+    }
+
     @Override
     protected PowerStats collectStats() {
+        ensureInitialized();
+
+        if (!mIsPerUidTimeInStateSupported) {
+            return null;
+        }
+
         mCpuPowerStats.uidStats.clear();
-        long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(
-                this::processUidStats, mScalingStepToPowerBracketMap, mLastUpdateTimestampNanos,
-                mTempUidStats);
+        // TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster
+        long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(this::processUidStats,
+                mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos,
+                mTempCpuTimeByScalingStep, mTempUidStats);
+        for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) {
+            mLayout.setTimeByScalingStep(mCpuPowerStats.stats, step,
+                    mTempCpuTimeByScalingStep[step] - mCpuTimeByScalingStep[step]);
+            mCpuTimeByScalingStep[step] = mTempCpuTimeByScalingStep[step];
+        }
+
         mCpuPowerStats.durationMs =
                 (newTimestampNanos - mLastUpdateTimestampNanos) / NANOS_PER_MILLIS;
         mLastUpdateTimestampNanos = newTimestampNanos;
+
+        long uptimeMillis = mClock.uptimeMillis();
+        long uptimeDelta = uptimeMillis - mLastUpdateUptimeMillis;
+        mLastUpdateUptimeMillis = uptimeMillis;
+
+        if (uptimeDelta > mCpuPowerStats.durationMs) {
+            uptimeDelta = mCpuPowerStats.durationMs;
+        }
+        mLayout.setUptime(mCpuPowerStats.stats, uptimeDelta);
+
+        if (mCpuEnergyConsumerIds.length != 0) {
+            collectEnergyConsumers();
+        }
+
         return mCpuPowerStats;
     }
 
+    private void collectEnergyConsumers() {
+        int voltageMv = mVoltageSupplier.getAsInt();
+        if (voltageMv <= 0) {
+            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+                          + " mV) when querying energy consumers");
+            return;
+        }
+
+        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+        mLastVoltageMv = voltageMv;
+
+        CompletableFuture<EnergyConsumerResult[]> future =
+                mPowerStatsInternal.getEnergyConsumedAsync(mCpuEnergyConsumerIds);
+        EnergyConsumerResult[] results = null;
+        try {
+            results = future.get(
+                    POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e);
+        }
+        if (results == null) {
+            return;
+        }
+
+        for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) {
+            int id = mCpuEnergyConsumerIds[i];
+            for (EnergyConsumerResult result : results) {
+                if (result.id == id) {
+                    long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
+                            ? result.energyUWs - mLastConsumedEnergyUws[i] : 0;
+                    if (energyDelta < 0) {
+                        // Likely, restart of powerstats HAL
+                        energyDelta = 0;
+                    }
+                    mLayout.setConsumedEnergy(mCpuPowerStats.stats, i,
+                            uJtoUc(energyDelta, averageVoltage));
+                    mLastConsumedEnergyUws[i] = result.energyUWs;
+                    break;
+                }
+            }
+        }
+    }
+
     @VisibleForNative
     interface KernelCpuStatsCallback {
         @Keep // Called from native
-        void processUidStats(int uid, long[] stats);
+        void processUidStats(int uid, long[] timeByPowerBracket);
     }
 
-    private void processUidStats(int uid, long[] stats) {
+    private void processUidStats(int uid, long[] timeByPowerBracket) {
+        int powerBracketCount = mLayout.getCpuPowerBracketCount();
+
         UidStats uidStats = mUidStats.get(uid);
         if (uidStats == null) {
             uidStats = new UidStats();
-            uidStats.stats = new long[mUidStatsSize];
-            uidStats.delta = new long[mUidStatsSize];
+            uidStats.timeByPowerBracket = new long[powerBracketCount];
+            uidStats.stats = new long[mLayout.getUidStatsArrayLength()];
             mUidStats.put(uid, uidStats);
         }
 
         boolean nonzero = false;
-        for (int i = mUidStatsSize - 1; i >= 0; i--) {
-            long delta = uidStats.delta[i] = stats[i] - uidStats.stats[i];
+        for (int bracket = powerBracketCount - 1; bracket >= 0; bracket--) {
+            long delta = timeByPowerBracket[bracket] - uidStats.timeByPowerBracket[bracket];
             if (delta != 0) {
                 nonzero = true;
             }
-            uidStats.stats[i] = stats[i];
+            mLayout.setUidTimeByPowerBracket(uidStats.stats, bracket, delta);
+            uidStats.timeByPowerBracket[bracket] = timeByPowerBracket[bracket];
         }
         if (nonzero) {
-            mCpuPowerStats.uidStats.put(uid, uidStats.delta);
+            mCpuPowerStats.uidStats.put(uid, uidStats.stats);
         }
     }
 
@@ -128,13 +765,15 @@
      * Native class that retrieves CPU stats from the kernel.
      */
     public static class KernelCpuStatsReader {
+        protected native boolean nativeIsSupportedFeature();
+
         protected native long nativeReadCpuStats(KernelCpuStatsCallback callback,
                 int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos,
-                long[] tempForUidStats);
+                long[] outCpuTimeByScalingStep, long[] tempForUidStats);
     }
 
     private static class UidStats {
         public long[] stats;
-        public long[] delta;
+        public long[] timeByPowerBracket;
     }
 }
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 05c0a13..2c7843e 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -16,6 +16,8 @@
 
 package com.android.server.power.stats;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 
@@ -46,6 +48,8 @@
     public final int powerComponentId;
     private final MultiStateStats.States[] mDeviceStateConfig;
     private final MultiStateStats.States[] mUidStateConfig;
+    @NonNull
+    private final AggregatedPowerStatsConfig.PowerComponent mConfig;
     private final int[] mDeviceStates;
     private final long[] mDeviceStateTimestamps;
 
@@ -62,13 +66,20 @@
     }
 
     PowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
-        this.powerComponentId = config.getPowerComponentId();
+        mConfig = config;
+        powerComponentId = config.getPowerComponentId();
         mDeviceStateConfig = config.getDeviceStateConfig();
         mUidStateConfig = config.getUidStateConfig();
         mDeviceStates = new int[mDeviceStateConfig.length];
         mDeviceStateTimestamps = new long[mDeviceStateConfig.length];
     }
 
+    @NonNull
+    public AggregatedPowerStatsConfig.PowerComponent getConfig() {
+        return mConfig;
+    }
+
+    @Nullable
     public PowerStats.Descriptor getPowerStatsDescriptor() {
         return mPowerStatsDescriptor;
     }
@@ -108,6 +119,16 @@
         }
     }
 
+    void setDeviceStats(@AggregatedPowerStatsConfig.TrackedState int[] states, long[] values) {
+        mDeviceStats.setStats(states, values);
+    }
+
+    void setUidStats(int uid, @AggregatedPowerStatsConfig.TrackedState int[] states,
+            long[] values) {
+        UidStats uidStats = getUidStats(uid);
+        uidStats.stats.setStats(states, values);
+    }
+
     boolean isCompatible(PowerStats powerStats) {
         return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor);
     }
@@ -298,7 +319,8 @@
         if (mDeviceStats != null) {
             ipw.println(mPowerStatsDescriptor.name);
             ipw.increaseIndent();
-            mDeviceStats.dump(ipw);
+            mDeviceStats.dump(ipw, stats ->
+                    mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats));
             ipw.decreaseIndent();
         }
     }
@@ -308,7 +330,8 @@
         if (uidStats != null && uidStats.stats != null) {
             ipw.println(mPowerStatsDescriptor.name);
             ipw.increaseIndent();
-            uidStats.stats.dump(ipw);
+            uidStats.stats.dump(ipw, stats ->
+                    mConfig.getProcessor().uidStatsToString(mPowerStatsDescriptor, stats));
             ipw.decreaseIndent();
         }
     }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index f374fb7..2f9d567 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -16,6 +16,7 @@
 package com.android.server.power.stats;
 
 import android.os.BatteryStats;
+import android.util.SparseArray;
 
 import com.android.internal.os.BatteryStatsHistory;
 import com.android.internal.os.BatteryStatsHistoryIterator;
@@ -30,11 +31,17 @@
 public class PowerStatsAggregator {
     private final AggregatedPowerStats mStats;
     private final BatteryStatsHistory mHistory;
+    private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>();
 
     public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
             BatteryStatsHistory history) {
         mStats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
         mHistory = history;
+        for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
+                aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
+            AggregatedPowerStatsProcessor processor = powerComponentsConfig.getProcessor();
+            mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor);
+        }
     }
 
     /**
@@ -100,6 +107,7 @@
                     if (!mStats.isCompatible(item.powerStats)) {
                         if (lastTime > baseTime) {
                             mStats.setDuration(lastTime - baseTime);
+                            finish(mStats);
                             consumer.accept(mStats);
                         }
                         mStats.reset();
@@ -112,9 +120,20 @@
         }
         if (lastTime > baseTime) {
             mStats.setDuration(lastTime - baseTime);
+            finish(mStats);
             consumer.accept(mStats);
         }
 
         mStats.reset();     // to free up memory
     }
+
+    private void finish(AggregatedPowerStats stats) {
+        for (int i = 0; i < mProcessors.size(); i++) {
+            PowerComponentAggregatedPowerStats component =
+                    stats.getPowerComponentStats(mProcessors.keyAt(i));
+            if (component != null) {
+                mProcessors.valueAt(i).finish(component);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index b49c89f..84cc21e 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -38,8 +38,9 @@
  * except where noted.
  */
 public abstract class PowerStatsCollector {
+    private static final int MILLIVOLTS_PER_VOLT = 1000;
     private final Handler mHandler;
-    private final Clock mClock;
+    protected final Clock mClock;
     private final long mThrottlePeriodMs;
     private final Runnable mCollectAndDeliverStats = this::collectAndDeliverStats;
     private boolean mEnabled;
@@ -100,6 +101,9 @@
     @SuppressWarnings("GuardedBy")  // Field is volatile
     private void collectAndDeliverStats() {
         PowerStats stats = collectStats();
+        if (stats == null) {
+            return;
+        }
         for (Consumer<PowerStats> consumer : mConsumerList) {
             consumer.accept(stats);
         }
@@ -180,4 +184,11 @@
         mHandler.post(done::open);
         done.block();
     }
+
+    /** Calculate charge consumption (in microcoulombs) from a given energy and voltage */
+    protected long uJtoUc(long deltaEnergyUj, int avgVoltageMv) {
+        // To overflow, a 3.7V 10000mAh battery would need to completely drain 69244 times
+        // since the last snapshot. Round off to the nearest whole long.
+        return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv;
+    }
 }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index 58619c7..551302e 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -108,6 +108,9 @@
         long currentTimeMillis = mClock.currentTimeMillis();
         long currentMonotonicTime = mMonotonicClock.monotonicTime();
         long startTime = getLastSavedSpanEndMonotonicTime();
+        if (startTime < 0) {
+            startTime = mBatteryStats.getHistory().getStartTime();
+        }
         long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
                 mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
         while (endTimeMs <= currentMonotonicTime) {
@@ -214,6 +217,7 @@
             return mLastSavedSpanEndMonotonicTime;
         }
 
+        mLastSavedSpanEndMonotonicTime = -1;
         for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
             if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
                 for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 7b0fe9a..a01bac6 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -266,10 +266,10 @@
     }
 
     /**
-     * @return {@code true} iff. {@code userId} is locked on an FBE device.
+     * @return {@code true} iff the credential-encrypted storage for {@code userId} is locked.
      */
     @VisibleForTesting
     public boolean isUserCredentialLocked(int userId) {
-        return StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId);
+        return StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId);
     }
 }
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 85d93f4..a5b90f1 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -174,6 +174,11 @@
     @Nullable private final String mInstallerPackageName;
 
     /**
+     * Time after which rollback expires.
+     */
+    private long mRollbackLifetimeMillis = 0;
+
+    /**
      * Session ids for all packages in the install. For multi-package sessions, this is the list
      * of child session ids. For normal sessions, this list is a single element with the normal
      * session id.
@@ -286,6 +291,24 @@
     }
 
     /**
+     * Sets rollback lifetime in milliseconds, for purposes of expiring rollback data.
+     */
+    @WorkerThread
+    void setRollbackLifetimeMillis(long lifetimeMillis) {
+        assertInWorkerThread();
+        mRollbackLifetimeMillis = lifetimeMillis;
+    }
+
+    /**
+     * Returns rollback lifetime in milliseconds, for purposes of expiring rollback data.
+     */
+    @WorkerThread
+    long getRollbackLifetimeMillis() {
+        assertInWorkerThread();
+        return mRollbackLifetimeMillis;
+    }
+
+    /**
      * Returns the session ID associated with this rollback, or {@code -1} if unknown.
      */
     @AnyThread
@@ -930,6 +953,7 @@
         ipw.println("-state: " + getStateAsString());
         ipw.println("-stateDescription: " + mStateDescription);
         ipw.println("-timestamp: " + getTimestamp());
+        ipw.println("-rollbackLifetimeMillis: " + getRollbackLifetimeMillis());
         ipw.println("-isStaged: " + isStaged());
         ipw.println("-originalSessionId: " + getOriginalSessionId());
         ipw.println("-packages:");
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 720c773..8d93408 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
 import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -702,6 +703,15 @@
     // Schedules future expiration as appropriate.
     @WorkerThread
     private void runExpiration() {
+        if (Flags.rollbackLifetime()) {
+            runExpirationCustomRollbackLifetime();
+        } else {
+            runExpirationDefaultRollbackLifetime();
+        }
+    }
+
+    @WorkerThread
+    private void runExpirationDefaultRollbackLifetime() {
         getHandler().removeCallbacks(mRunExpiration);
         assertInWorkerThread();
         Instant now = Instant.now();
@@ -729,6 +739,44 @@
         }
     }
 
+    @WorkerThread
+    private void runExpirationCustomRollbackLifetime() {
+        getHandler().removeCallbacks(mRunExpiration);
+        assertInWorkerThread();
+        Instant now = Instant.now();
+        long minDelay = 0;
+        Iterator<Rollback> iter = mRollbacks.iterator();
+        while (iter.hasNext()) {
+            Rollback rollback = iter.next();
+            if (!rollback.isAvailable() && !rollback.isCommitted()) {
+                continue;
+            }
+            long rollbackLifetimeMillis = rollback.getRollbackLifetimeMillis();
+            if (rollbackLifetimeMillis <= 0) {
+                rollbackLifetimeMillis = mRollbackLifetimeDurationInMillis;
+            }
+
+            Instant rollbackExpiryTimestamp = rollback.getTimestamp()
+                    .plusMillis(rollbackLifetimeMillis);
+            if (!now.isBefore(rollbackExpiryTimestamp)) {
+                Slog.i(TAG, "runExpiration id=" + rollback.info.getRollbackId());
+                iter.remove();
+                deleteRollback(rollback, "Expired by timeout");
+                continue;
+            }
+
+            long delay = now.until(
+                    rollbackExpiryTimestamp, ChronoUnit.MILLIS);
+            if (minDelay == 0 || delay < minDelay) {
+                minDelay = delay;
+            }
+        }
+
+        if (minDelay != 0) {
+            getHandler().postDelayed(mRunExpiration, minDelay);
+        }
+    }
+
     @AnyThread
     private Handler getHandler() {
         return mHandler;
@@ -1277,6 +1325,7 @@
         }
 
         final Rollback rollback;
+
         if (parentSession.isStaged()) {
             rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
                     installerPackageName, packageSessionIds, getExtensionVersions());
@@ -1285,6 +1334,11 @@
                     installerPackageName, packageSessionIds, getExtensionVersions());
         }
 
+        if (Flags.rollbackLifetime()) {
+            rollback.setRollbackLifetimeMillis(parentSession.rollbackLifetimeMillis);
+        }
+
+
         mRollbacks.add(rollback);
         return rollback;
     }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 8068c6f..0af137f 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -19,6 +19,7 @@
 import static com.android.server.rollback.Rollback.rollbackStateFromString;
 
 import android.annotation.NonNull;
+import android.content.pm.Flags;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
@@ -312,6 +313,9 @@
             JSONObject dataJson = new JSONObject();
             dataJson.put("info", rollbackInfoToJson(rollback.info));
             dataJson.put("timestamp", rollback.getTimestamp().toString());
+            if (Flags.rollbackLifetime()) {
+                dataJson.put("rollbackLifetimeMillis", rollback.getRollbackLifetimeMillis());
+            }
             dataJson.put("originalSessionId", rollback.getOriginalSessionId());
             dataJson.put("state", rollback.getStateAsString());
             dataJson.put("stateDescription", rollback.getStateDescription());
@@ -375,7 +379,7 @@
     @VisibleForTesting
     static Rollback rollbackFromJson(JSONObject dataJson, File backupDir)
             throws JSONException, ParseException {
-        return new Rollback(
+        Rollback rollback = new Rollback(
                 rollbackInfoFromJson(dataJson.getJSONObject("info")),
                 backupDir,
                 Instant.parse(dataJson.getString("timestamp")),
@@ -388,6 +392,10 @@
                 dataJson.optInt("userId", UserHandle.SYSTEM.getIdentifier()),
                 dataJson.optString("installerPackageName", ""),
                 extensionVersionsFromJson(dataJson.optJSONArray("extensionVersions")));
+        if (Flags.rollbackLifetime()) {
+            rollback.setRollbackLifetimeMillis(dataJson.optLong("rollbackLifetimeMillis"));
+        }
+        return rollback;
     }
 
     private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 635e11b..f82f08b 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -70,7 +70,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
-import android.view.Display;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -79,7 +78,6 @@
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 
@@ -1523,57 +1521,13 @@
             mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
         }
 
-        /**
-         * @param uid: uid of the calling app (obtained via getCallingUid())
-         * @param displayId: the id of a Display
-         * @return Returns true if both of the following conditions hold -
-         * 1) the uid belongs to an app instead of a system core component; and
-         * 2) either the uid is running on a virtual device or the displayId
-         *    is owned by a virtual device
-         */
-        private boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
-            if (UserHandle.isCore(uid)) {
-                return false;
-            }
-
-            if (mVirtualDeviceManager == null) {
-                mVirtualDeviceManager = LocalServices.getService(
-                        VirtualDeviceManagerInternal.class);
-                if (mVirtualDeviceManager == null) {
-                    // VirtualDeviceManager service may not have been published
-                    return false;
-                }
-            }
-
-            switch (displayId) {
-                case Display.INVALID_DISPLAY:
-                    // There is no Display object associated with the Context of the calling app.
-                    if (mVirtualDeviceManager.isAppRunningOnAnyVirtualDevice(uid)) {
-                        return true;
-                    }
-                    break;
-                case Display.DEFAULT_DISPLAY:
-                    // The DEFAULT_DISPLAY is by definition not virtual.
-                    break;
-                default:
-                    // Other display IDs can belong to logical displays created for other purposes.
-                    if (mVirtualDeviceManager.isDisplayOwnedByAnyVirtualDevice(displayId)) {
-                        return true;
-                    }
-                    break;
-            }
-            return false;
-        }
-
         @Override
-        public boolean isDeviceLocked(int userId, int displayId) throws RemoteException {
-            int uid = getCallingUid();
-            if (isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
-                // Virtual displays are considered insecure because they may be used for streaming
-                // to other devices.
+        public boolean isDeviceLocked(int userId, int deviceId) throws RemoteException {
+            if (deviceId != Context.DEVICE_ID_DEFAULT) {
+                // Virtual devices are considered insecure.
                 return false;
             }
-            userId = ActivityManager.handleIncomingUser(getCallingPid(), uid, userId,
+            userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
 
             final long token = Binder.clearCallingIdentity();
@@ -1588,15 +1542,12 @@
         }
 
         @Override
-        public boolean isDeviceSecure(int userId, int displayId) throws RemoteException {
-            int uid = getCallingUid();
-            if (isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
-                // Virtual displays are considered insecure because they may be used for streaming
-                // to other devices.
+        public boolean isDeviceSecure(int userId, int deviceId) throws RemoteException {
+            if (deviceId != Context.DEVICE_ID_DEFAULT) {
+                // Virtual devices are considered insecure.
                 return false;
             }
-
-            userId = ActivityManager.handleIncomingUser(getCallingPid(), uid, userId,
+            userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
 
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fd8f6bf..bdab4d4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9317,8 +9317,9 @@
         final Task rootTask = getRootTask();
         final float minAspectRatio = getMinAspectRatio();
         final TaskFragment organizedTf = getOrganizedTaskFragment();
+        float aspectRatioToApply = desiredAspectRatio;
         if (task == null || rootTask == null
-                || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
+                || (maxAspectRatio < 1 && minAspectRatio < 1 && aspectRatioToApply < 1)
                 // Don't set aspect ratio if we are in VR mode.
                 || isInVrUiMode(getConfiguration())
                 // TODO(b/232898850): Always respect aspect ratio requests.
@@ -9331,30 +9332,30 @@
         final int containingAppHeight = containingAppBounds.height();
         final float containingRatio = computeAspectRatio(containingAppBounds);
 
-        if (desiredAspectRatio < 1) {
-            desiredAspectRatio = containingRatio;
+        if (aspectRatioToApply < 1) {
+            aspectRatioToApply = containingRatio;
         }
 
-        if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
-            desiredAspectRatio = maxAspectRatio;
-        } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
-            desiredAspectRatio = minAspectRatio;
+        if (maxAspectRatio >= 1 && aspectRatioToApply > maxAspectRatio) {
+            aspectRatioToApply = maxAspectRatio;
+        } else if (minAspectRatio >= 1 && aspectRatioToApply < minAspectRatio) {
+            aspectRatioToApply = minAspectRatio;
         }
 
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
 
-        if (containingRatio - desiredAspectRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
+        if (containingRatio - aspectRatioToApply > ASPECT_RATIO_ROUNDING_TOLERANCE) {
             if (containingAppWidth < containingAppHeight) {
                 // Width is the shorter side, so we use that to figure-out what the max. height
                 // should be given the aspect ratio.
-                activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth * aspectRatioToApply) + 0.5f);
             } else {
                 // Height is the shorter side, so we use that to figure-out what the max. width
                 // should be given the aspect ratio.
-                activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight * aspectRatioToApply) + 0.5f);
             }
-        } else if (desiredAspectRatio - containingRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
+        } else if (aspectRatioToApply - containingRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
             boolean adjustWidth;
             switch (getRequestedConfigurationOrientation()) {
                 case ORIENTATION_LANDSCAPE:
@@ -9382,9 +9383,9 @@
                     break;
             }
             if (adjustWidth) {
-                activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight / aspectRatioToApply) + 0.5f);
             } else {
-                activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth / aspectRatioToApply) + 0.5f);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7cccf6b..34bf8ed 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1097,23 +1097,22 @@
                         "shouldAbortBackgroundActivityStart");
                 BackgroundActivityStartController balController =
                         mSupervisor.getBackgroundActivityLaunchController();
-                balCode =
+                BackgroundActivityStartController.BalVerdict balVerdict =
                         balController.checkBackgroundActivityStart(
-                                callingUid,
-                                callingPid,
-                                callingPackage,
-                                realCallingUid,
-                                realCallingPid,
-                                callerApp,
-                                request.originatingPendingIntent,
-                                request.backgroundStartPrivileges,
-                                intent,
-                                checkedOptions);
-                if (balCode != BAL_ALLOW_DEFAULT) {
-                    request.logMessage.append(" (").append(
-                                    BackgroundActivityStartController.balCodeToString(balCode))
-                            .append(")");
-                }
+                            callingUid,
+                            callingPid,
+                            callingPackage,
+                            realCallingUid,
+                            realCallingPid,
+                            callerApp,
+                            request.originatingPendingIntent,
+                            request.backgroundStartPrivileges,
+                            intent,
+                            checkedOptions);
+                balCode = balVerdict.getCode();
+                request.logMessage.append(" (").append(
+                                BackgroundActivityStartController.balCodeToString(balCode))
+                        .append(")");
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index e97bda2..9b7b8de 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -22,7 +22,6 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
 
-import static com.android.internal.util.Preconditions.checkState;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -40,7 +39,6 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.BackgroundStartPrivileges;
-import android.app.ComponentOptions;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -73,14 +71,18 @@
 
     private static final String TAG =
             TAG_WITH_CLASS_NAME ? "BackgroundActivityStartController" : TAG_ATM;
-    public static final String VERDICT_ALLOWED = "Activity start allowed";
-    public static final String VERDICT_WOULD_BE_ALLOWED_IF_SENDER_GRANTS_BAL =
-            "Activity start would be allowed if the sender granted BAL privileges";
 
     private static final long ASM_GRACEPERIOD_TIMEOUT_MS = TIMEOUT_MS;
     private static final int ASM_GRACEPERIOD_MAX_REPEATS = 5;
+    public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
+            ActivityOptions.makeBasic()
+                    .setPendingIntentBackgroundActivityStartMode(
+                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED)
+                    .setPendingIntentCreatorBackgroundActivityStartMode(
+                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
 
     private final ActivityTaskManagerService mService;
+
     private final ActivityTaskSupervisor mSupervisor;
 
     // TODO(b/263368846) Rename when ASM logic is moved in
@@ -202,15 +204,181 @@
         return checkBackgroundActivityStart(callingUid, callingPid, callingPackage,
                 realCallingUid, realCallingPid,
                 callerApp, originatingPendingIntent,
-                backgroundStartPrivileges, intent, checkedOptions) == BAL_BLOCK;
+                backgroundStartPrivileges, intent, checkedOptions).blocks();
+    }
+
+    private class BalState {
+
+        private final String mCallingPackage;
+        private final int mCallingUid;
+        private final int mCallingPid;
+        private final @ActivityTaskManagerService.AppSwitchState int mAppSwitchState;
+        private final boolean mCallingUidHasAnyVisibleWindow;
+        private final @ActivityManager.ProcessState int mCallingUidProcState;
+        private final boolean mIsCallingUidPersistentSystemProcess;
+        private final BackgroundStartPrivileges mBalAllowedByPiSender;
+        private final BackgroundStartPrivileges mBalAllowedByPiCreator;
+        private final String mRealCallingPackage;
+        private final int mRealCallingUid;
+        private final int mRealCallingPid;
+        private final boolean mRealCallingUidHasAnyVisibleWindow;
+        private final @ActivityManager.ProcessState int mRealCallingUidProcState;
+        private final boolean mIsRealCallingUidPersistentSystemProcess;
+        private final PendingIntentRecord mOriginatingPendingIntent;
+        private final BackgroundStartPrivileges mBackgroundStartPrivileges;
+        private final Intent mIntent;
+        private final WindowProcessController mCallerApp;
+        private final WindowProcessController mRealCallerApp;
+
+        private BalState(int callingUid, int callingPid, final String callingPackage,
+                 int realCallingUid, int realCallingPid,
+                 WindowProcessController callerApp,
+                 PendingIntentRecord originatingPendingIntent,
+                 BackgroundStartPrivileges backgroundStartPrivileges,
+                 Intent intent,
+                 ActivityOptions checkedOptions) {
+            this.mCallingPackage = callingPackage;
+            mCallingUid = callingUid;
+            mCallingPid = callingPid;
+            mRealCallingUid = realCallingUid;
+            mRealCallingPid = realCallingPid;
+            mCallerApp = callerApp;
+            mBackgroundStartPrivileges = backgroundStartPrivileges;
+            mOriginatingPendingIntent = originatingPendingIntent;
+            mIntent = intent;
+            mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
+            mBalAllowedByPiSender =
+                    PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(checkedOptions,
+                            realCallingUid, mRealCallingPackage);
+            mBalAllowedByPiCreator =
+                    checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                            == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+                            ? BackgroundStartPrivileges.NONE : BackgroundStartPrivileges.ALLOW_BAL;
+            mAppSwitchState = mService.getBalAppSwitchesState();
+            mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
+            mIsCallingUidPersistentSystemProcess =
+                    mCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+            mCallingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
+            if (callingUid == realCallingUid) {
+                mRealCallingUidProcState = mCallingUidProcState;
+                mRealCallingUidHasAnyVisibleWindow = mCallingUidHasAnyVisibleWindow;
+                // In the PendingIntent case callerApp is not passed in, so resolve it ourselves.
+                mRealCallerApp = callerApp == null
+                        ? mService.getProcessController(realCallingPid, realCallingUid)
+                        : callerApp;
+                mIsRealCallingUidPersistentSystemProcess = mIsCallingUidPersistentSystemProcess;
+            } else {
+                mRealCallingUidProcState = mService.mActiveUids.getUidState(realCallingUid);
+                mRealCallingUidHasAnyVisibleWindow =
+                        mService.hasActiveVisibleWindow(realCallingUid);
+                mRealCallerApp = mService.getProcessController(realCallingPid, realCallingUid);
+                mIsRealCallingUidPersistentSystemProcess =
+                        mRealCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+            }
+        }
+
+        private String getDebugPackageName(String packageName, int uid) {
+            if (packageName != null) {
+                return packageName; // use actual package
+            }
+            if (uid == 0) {
+                return "root[debugOnly]";
+            }
+            String name = mService.mContext.getPackageManager().getNameForUid(uid);
+            if (name == null) {
+                name = "uid=" + uid;
+            }
+            return name + "[debugOnly]";
+        }
+
+        private String dump(BalVerdict resultIfPiCreatorAllowsBal,
+                           BalVerdict resultIfPiSenderAllowsBal) {
+            return " [callingPackage: " + getDebugPackageName(mCallingPackage, mCallingUid)
+                    + "; callingUid: " + mCallingUid
+                    + "; appSwitchState: " + mAppSwitchState
+                    + "; callingUidHasAnyVisibleWindow: " + mCallingUidHasAnyVisibleWindow
+                    + "; callingUidProcState: " + DebugUtils.valueToString(
+                        ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState)
+                    + "; isCallingUidPersistentSystemProcess: "
+                        + mIsCallingUidPersistentSystemProcess
+                    + "; balAllowedByPiCreator: " + mBalAllowedByPiCreator
+                    + "; balAllowedByPiSender: " + mBalAllowedByPiSender
+                    + "; realCallingPackage: "
+                        + getDebugPackageName(mRealCallingPackage, mRealCallingUid)
+                    + "; realCallingUid: " + mRealCallingUid
+                    + "; realCallingPid: " + mRealCallingPid
+                    + "; realCallingUidHasAnyVisibleWindow: " + mRealCallingUidHasAnyVisibleWindow
+                    + "; realCallingUidProcState: " + DebugUtils.valueToString(
+                        ActivityManager.class, "PROCESS_STATE_", mRealCallingUidProcState)
+                    + "; isRealCallingUidPersistentSystemProcess: "
+                        + mIsRealCallingUidPersistentSystemProcess
+                    + "; originatingPendingIntent: " + mOriginatingPendingIntent
+                    + "; backgroundStartPrivileges: " + mBackgroundStartPrivileges
+                    + "; intent: " + mIntent
+                    + "; callerApp: " + mCallerApp
+                    + "; realCallerApp: " + mRealCallerApp
+                    + "; inVisibleTask: "
+                        + (mCallerApp != null && mCallerApp.hasActivityInVisibleTask())
+                    + "; realInVisibleTask: "
+                        + (mRealCallerApp != null && mRealCallerApp.hasActivityInVisibleTask())
+                    + "; resultIfPiSenderAllowsBal: " + resultIfPiSenderAllowsBal
+                    + "; resultIfPiCreatorAllowsBal: " + resultIfPiCreatorAllowsBal
+                    + "]";
+        }
+    }
+
+    static class BalVerdict {
+
+        static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked");
+        private final @BalCode int mCode;
+        private final boolean mBackground;
+        private final String mMessage;
+        private String mProcessInfo;
+
+        BalVerdict(@BalCode int balCode, boolean background, String message) {
+            this.mBackground = background;
+            this.mCode = balCode;
+            this.mMessage = message;
+        }
+
+        public BalVerdict withProcessInfo(String msg, WindowProcessController process) {
+            mProcessInfo = msg + " (uid=" + process.mUid + ",pid=" + process.getPid() + ")";
+            return this;
+        }
+
+        boolean blocks() {
+            return mCode == BAL_BLOCK;
+        }
+
+        boolean allows() {
+            return !blocks();
+        }
+
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            if (mBackground) {
+                builder.append("Background ");
+            }
+            builder.append("Activity start allowed: " + mMessage + ".");
+            builder.append("BAL Code: ");
+            builder.append(balCodeToString(mCode));
+            if (mProcessInfo != null) {
+                builder.append(" ");
+                builder.append(mProcessInfo);
+            }
+            return builder.toString();
+        }
+
+        public @BalCode int getCode() {
+            return mCode;
+        }
     }
 
     /**
      * @return A code denoting which BAL rule allows an activity to be started,
-     * or {@link BAL_BLOCK} if the launch should be blocked
+     * or {@link #BAL_BLOCK} if the launch should be blocked
      */
-    @BalCode
-    int checkBackgroundActivityStart(
+    BalVerdict checkBackgroundActivityStart(
             int callingUid,
             int callingPid,
             final String callingPackage,
@@ -221,36 +389,148 @@
             BackgroundStartPrivileges backgroundStartPrivileges,
             Intent intent,
             ActivityOptions checkedOptions) {
+
+        if (checkedOptions == null) {
+            // replace null with a constant to simplify evaluation
+            checkedOptions = ACTIVITY_OPTIONS_SYSTEM_DEFINED;
+        }
+
+        BalState state = new BalState(callingUid, callingPid, callingPackage,
+                realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
+                backgroundStartPrivileges, intent, checkedOptions);
+
+        // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
+        // visible window.
+        if (Process.isSdkSandboxUid(state.mRealCallingUid)) {
+            int realCallingSdkSandboxUidToAppUid =
+                    Process.getAppUidForSdkSandboxUid(state.mRealCallingUid);
+            // realCallingSdkSandboxUidToAppUid should probably just be used instead (or in addition
+            // to realCallingUid when calculating resultForRealCaller below.
+            if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
+                BalVerdict balVerdict = new BalVerdict(BAL_ALLOW_SDK_SANDBOX, /*background*/ false,
+                        "uid in SDK sandbox has visible (non-toast) window");
+                return statsLog(balVerdict, state);
+            }
+        }
+
+        BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
+        BalVerdict resultForRealCaller = callingUid == realCallingUid
+                ? resultForCaller // no need to calculate again
+                : checkBackgroundActivityStartAllowedBySender(state, checkedOptions);
+
+        if (resultForCaller.allows()
+                && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
+            if (DEBUG_ACTIVITY_STARTS) {
+                Slog.d(TAG, "Activity start explicitly allowed by PI creator. "
+                        + state.dump(resultForCaller, resultForRealCaller));
+            }
+            return statsLog(resultForCaller, state);
+        }
+        if (resultForRealCaller.allows()
+                && checkedOptions.getPendingIntentBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
+            if (DEBUG_ACTIVITY_STARTS) {
+                Slog.d(TAG, "Activity start explicitly allowed by PI sender. "
+                        + state.dump(resultForCaller, resultForRealCaller));
+            }
+            return statsLog(resultForRealCaller, state);
+        }
+        if (resultForCaller.allows() && resultForRealCaller.allows()
+                && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED
+                && checkedOptions.getPendingIntentBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+            // Both caller and real caller allow with system defined behavior
+            Slog.wtf(TAG,
+                    "With Android 15 BAL hardening this activity start would be blocked"
+                            + " (missing opt in by PI creator)! "
+                            + state.dump(resultForCaller, resultForRealCaller));
+            // return the realCaller result for backwards compatibility
+            return statsLog(resultForRealCaller, state);
+        }
+        if (resultForCaller.allows()
+                && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+            // Allowed before V by creator
+            Slog.wtf(TAG,
+                    "With Android 15 BAL hardening this activity start would be blocked"
+                            + " (missing opt in by PI creator)! "
+                            + state.dump(resultForCaller, resultForRealCaller));
+            return statsLog(resultForCaller, state);
+        }
+        if (resultForRealCaller.allows()
+                && checkedOptions.getPendingIntentBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+            // Allowed before U by sender
+            if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
+                Slog.wtf(TAG,
+                        "With Android 14 BAL hardening this activity start would be blocked"
+                                + " (missing opt in by PI sender)! "
+                                + state.dump(resultForCaller, resultForRealCaller));
+                return statsLog(resultForRealCaller, state);
+            }
+            Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
+                    + " (missing opt in by PI sender)! "
+                    + state.dump(resultForCaller, resultForRealCaller));
+            // fall through
+        }
+        // anything that has fallen through would currently be aborted
+        Slog.w(TAG, "Background activity launch blocked"
+                + state.dump(resultForCaller, resultForRealCaller));
+        // log aborted activity start to TRON
+        if (mService.isActivityStartsLoggingEnabled()) {
+            mSupervisor
+                    .getActivityMetricsLogger()
+                    .logAbortedBgActivityStart(
+                            intent,
+                            callerApp,
+                            callingUid,
+                            callingPackage,
+                            state.mCallingUidProcState,
+                            state.mCallingUidHasAnyVisibleWindow,
+                            realCallingUid,
+                            state.mRealCallingUidProcState,
+                            state.mRealCallingUidHasAnyVisibleWindow,
+                            (originatingPendingIntent != null));
+        }
+        return statsLog(BalVerdict.BLOCK, state);
+    }
+
+    /**
+     * @return A code denoting which BAL rule allows an activity to be started,
+     * or {@link #BAL_BLOCK} if the launch should be blocked
+     */
+    BalVerdict checkBackgroundActivityStartAllowedByCaller(BalState state) {
+        int callingUid = state.mCallingUid;
+        int callingPid = state.mCallingPid;
+        final String callingPackage = state.mCallingPackage;
+        WindowProcessController callerApp = state.mCallerApp;
+
         // don't abort for the most important UIDs
         final int callingAppId = UserHandle.getAppId(callingUid);
-        final boolean useCallingUidState =
-                originatingPendingIntent == null
-                        || checkedOptions == null
-                        || checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
-                                != ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
-        if (useCallingUidState) {
-            if (callingUid == Process.ROOT_UID
-                    || callingAppId == Process.SYSTEM_UID
-                    || callingAppId == Process.NFC_UID) {
-                return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_UID, /*background*/ false,
-                        callingUid, realCallingUid, intent, "Important callingUid");
-            }
+        if (callingUid == Process.ROOT_UID
+                || callingAppId == Process.SYSTEM_UID
+                || callingAppId == Process.NFC_UID) {
+            return new BalVerdict(
+                    BAL_ALLOW_ALLOWLISTED_UID, /*background*/ false,
+                     "Important callingUid");
+        }
 
-            // Always allow home application to start activities.
-            if (isHomeApp(callingUid, callingPackage)) {
-                return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
-                        /*background*/ false, callingUid, realCallingUid, intent,
-                        "Home app");
-            }
+        // Always allow home application to start activities.
+        if (isHomeApp(callingUid, callingPackage)) {
+            return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+                    /*background*/ false,
+                    "Home app");
+        }
 
-            // IME should always be allowed to start activity, like IME settings.
-            final WindowState imeWindow =
-                    mService.mRootWindowContainer.getCurrentInputMethodWindow();
-            if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
-                return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
-                        /*background*/ false, callingUid, realCallingUid, intent,
-                        "Active ime");
-            }
+        // IME should always be allowed to start activity, like IME settings.
+        final WindowState imeWindow =
+                mService.mRootWindowContainer.getCurrentInputMethodWindow();
+        if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
+            return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+                    /*background*/ false,
+                    "Active ime");
         }
 
         // This is used to block background activity launch even if the app is still
@@ -269,331 +549,169 @@
                 appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
         final boolean allowCallingUidStartActivity =
                 ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
-                                && callingUidHasAnyVisibleWindow)
+                        && callingUidHasAnyVisibleWindow)
                         || isCallingUidPersistentSystemProcess;
-        if (useCallingUidState && allowCallingUidStartActivity) {
-            return logStartAllowedAndReturnCode(BAL_ALLOW_VISIBLE_WINDOW,
-                    /*background*/ false, callingUid, realCallingUid, intent,
+        if (allowCallingUidStartActivity) {
+            return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+                    /*background*/ false,
                     "callingUidHasAnyVisibleWindow = "
                             + callingUid
                             + ", isCallingUidPersistentSystemProcess = "
                             + isCallingUidPersistentSystemProcess);
         }
-        // take realCallingUid into consideration
-        final int realCallingUidProcState =
-                (callingUid == realCallingUid)
-                        ? callingUidProcState
-                        : mService.mActiveUids.getUidState(realCallingUid);
-        final boolean realCallingUidHasAnyVisibleWindow =
-                (callingUid == realCallingUid)
-                        ? callingUidHasAnyVisibleWindow
-                        : mService.hasActiveVisibleWindow(realCallingUid);
-        final int realCallingAppId = UserHandle.getAppId(realCallingUid);
-        final boolean isRealCallingUidPersistentSystemProcess =
-                (callingUid == realCallingUid)
-                        ? isCallingUidPersistentSystemProcess
-                        : (realCallingAppId == Process.SYSTEM_UID)
-                                || realCallingUidProcState
-                                        <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
 
-        // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
-        // visible window.
-        if (Process.isSdkSandboxUid(realCallingUid)) {
-            int realCallingSdkSandboxUidToAppUid =
-                    Process.getAppUidForSdkSandboxUid(realCallingUid);
-
-            if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
-                return logStartAllowedAndReturnCode(BAL_ALLOW_SDK_SANDBOX,
-                        /*background*/ false, callingUid, realCallingUid, intent,
-                        "uid in SDK sandbox has visible (non-toast) window");
-            }
-        }
-
-        String realCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
-
-        // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
-        // The options here are the options passed by the sender and not those on the intent.
-        final BackgroundStartPrivileges balAllowedByPiSender =
-                PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
-                        checkedOptions, realCallingUid, realCallingPackage);
-
-        final boolean logVerdictChangeByPiDefaultChange = checkedOptions == null
-                || checkedOptions.getPendingIntentBackgroundActivityStartMode()
-                        == ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
-        final boolean considerPiRules = logVerdictChangeByPiDefaultChange
-                || balAllowedByPiSender.allowsBackgroundActivityStarts();
-        final String verdictLogForPiSender =
-                balAllowedByPiSender.allowsBackgroundActivityStarts() ? VERDICT_ALLOWED
-                        : VERDICT_WOULD_BE_ALLOWED_IF_SENDER_GRANTS_BAL;
-
-        @BalCode int resultIfPiSenderAllowsBal = BAL_BLOCK;
-        if (realCallingUid != callingUid && considerPiRules) {
-            resultIfPiSenderAllowsBal = checkPiBackgroundActivityStart(callingUid, realCallingUid,
-                backgroundStartPrivileges, intent, checkedOptions,
-                realCallingUidHasAnyVisibleWindow, isRealCallingUidPersistentSystemProcess,
-                verdictLogForPiSender);
-        }
-        if (resultIfPiSenderAllowsBal != BAL_BLOCK
-                && balAllowedByPiSender.allowsBackgroundActivityStarts()
-                && !logVerdictChangeByPiDefaultChange) {
-            // The result is to allow (because the sender allows BAL) and we are not interested in
-            // logging differences, so just return.
-            return resultIfPiSenderAllowsBal;
-        }
-        if (useCallingUidState) {
-            // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
-            if (ActivityTaskManagerService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND,
-                    callingPid, callingUid) == PERMISSION_GRANTED) {
-                return logStartAllowedAndReturnCode(BAL_ALLOW_PERMISSION,
-                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                    /*background*/ true, callingUid, realCallingUid, intent,
+        // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
+        if (ActivityTaskManagerService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND,
+                callingPid, callingUid) == PERMISSION_GRANTED) {
+            return new BalVerdict(BAL_ALLOW_PERMISSION,
+                    /*background*/ true,
                     "START_ACTIVITIES_FROM_BACKGROUND permission granted");
-            }
-            // don't abort if the caller has the same uid as the recents component
-            if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
-                return logStartAllowedAndReturnCode(
-                    BAL_ALLOW_ALLOWLISTED_COMPONENT,
-                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                    /*background*/ true, callingUid, realCallingUid,
-                    intent, "Recents Component");
-            }
-            // don't abort if the callingUid is the device owner
-            if (mService.isDeviceOwner(callingUid)) {
-                return logStartAllowedAndReturnCode(
-                    BAL_ALLOW_ALLOWLISTED_COMPONENT,
-                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                    /*background*/ true, callingUid, realCallingUid,
-                    intent, "Device Owner");
-            }
-            // don't abort if the callingUid is a affiliated profile owner
-            if (mService.isAffiliatedProfileOwner(callingUid)) {
-                return logStartAllowedAndReturnCode(
-                    BAL_ALLOW_ALLOWLISTED_COMPONENT,
-                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                    /*background*/ true, callingUid, realCallingUid,
-                    intent, "Affiliated Profile Owner");
-            }
-            // don't abort if the callingUid has companion device
-            final int callingUserId = UserHandle.getUserId(callingUid);
-            if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
-                return logStartAllowedAndReturnCode(
-                    BAL_ALLOW_ALLOWLISTED_COMPONENT,
-                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                    /*background*/ true, callingUid, realCallingUid,
-                    intent, "Companion App");
-            }
-            // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
-            if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
-                Slog.w(
-                        TAG,
-                        "Background activity start for "
-                                + callingPackage
-                                + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
-                return logStartAllowedAndReturnCode(
-                    BAL_ALLOW_SAW_PERMISSION,
-                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                    /*background*/ true, callingUid, realCallingUid,
-                    intent, "SYSTEM_ALERT_WINDOW permission is granted");
-            }
-            // don't abort if the callingUid and callingPackage have the
-            // OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop
-            if (isSystemExemptFlagEnabled() && mService.getAppOpsManager().checkOpNoThrow(
-                        AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
-                        callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED) {
-                return logStartAllowedAndReturnCode(BAL_ALLOW_PERMISSION,
-                        resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                        /*background*/ true, callingUid, realCallingUid, intent,
-                        "OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
-            }
         }
+        // don't abort if the caller has the same uid as the recents component
+        if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+            return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+                    /*background*/ true, "Recents Component");
+        }
+        // don't abort if the callingUid is the device owner
+        if (mService.isDeviceOwner(callingUid)) {
+            return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+                    /*background*/ true, "Device Owner");
+        }
+        // don't abort if the callingUid is a affiliated profile owner
+        if (mService.isAffiliatedProfileOwner(callingUid)) {
+            return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+                    /*background*/ true, "Affiliated Profile Owner");
+        }
+        // don't abort if the callingUid has companion device
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
+            return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+                    /*background*/ true, "Companion App");
+        }
+        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+            Slog.w(
+                    TAG,
+                    "Background activity start for "
+                            + callingPackage
+                            + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+            return new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
+                    /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted");
+        }
+        // don't abort if the callingUid and callingPackage have the
+        // OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop
+        if (isSystemExemptFlagEnabled() && mService.getAppOpsManager().checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
+                callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED) {
+            return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+                    "OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
+        }
+
         // If we don't have callerApp at this point, no caller was provided to startActivity().
         // That's the case for PendingIntent-based starts, since the creator's process might not be
-        // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
-        // caller if caller allows, so that we can make the decision based on its state.
-        int callerAppUid = callingUid;
-        boolean callerAppBasedOnPiSender = callerApp == null && considerPiRules
-                && resultIfPiSenderAllowsBal == BAL_BLOCK;
-        if (callerAppBasedOnPiSender) {
-            callerApp = mService.getProcessController(realCallingPid, realCallingUid);
-            callerAppUid = realCallingUid;
-        }
-        // don't abort if the callerApp or other processes of that uid are allowed in any way
-        if (callerApp != null && (useCallingUidState || callerAppBasedOnPiSender)) {
-            // first check the original calling process
-            final @BalCode int balAllowedForCaller = callerApp
-                    .areBackgroundActivityStartsAllowed(appSwitchState);
-            if (balAllowedForCaller != BAL_BLOCK) {
-                if (callerAppBasedOnPiSender) {
-                    resultIfPiSenderAllowsBal = logStartAllowedAndReturnCode(balAllowedForCaller,
-                        /*background*/ true, callingUid, realCallingUid, intent,
-                        "callerApp process (pid = " + callerApp.getPid()
-                            + ", uid = " + callerAppUid + ") is allowed", verdictLogForPiSender);
-                } else {
-                    return logStartAllowedAndReturnCode(balAllowedForCaller,
-                        resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                        /*background*/ true, callingUid, realCallingUid, intent,
-                        "callerApp process (pid = " + callerApp.getPid()
-                            + ", uid = " + callerAppUid + ") is allowed");
-                }
-            } else {
-                // only if that one wasn't allowed, check the other ones
-                final ArraySet<WindowProcessController> uidProcesses =
-                    mService.mProcessMap.getProcesses(callerAppUid);
-                if (uidProcesses != null) {
-                    for (int i = uidProcesses.size() - 1; i >= 0; i--) {
-                        final WindowProcessController proc = uidProcesses.valueAt(i);
-                        int balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
-                                appSwitchState);
-                        if (proc != callerApp && balAllowedForUid != BAL_BLOCK) {
-                            if (callerAppBasedOnPiSender) {
-                                resultIfPiSenderAllowsBal = logStartAllowedAndReturnCode(
-                                    balAllowedForUid,
-                                    /*background*/ true, callingUid, realCallingUid, intent,
-                                    "process" + proc.getPid() + " from uid " + callerAppUid
-                                        + " is allowed", verdictLogForPiSender);
-                                break;
-                            } else {
-                                return logStartAllowedAndReturnCode(balAllowedForUid,
-                                    resultIfPiSenderAllowsBal, balAllowedByPiSender,
-                                    /*background*/ true, callingUid, realCallingUid, intent,
-                                    "process" + proc.getPid() + " from uid " + callerAppUid
-                                        + " is allowed");
-                            }
-                        }
-                    }
-                }
-            }
-            if (callerAppBasedOnPiSender) {
-                // If caller app was based on PI sender, this result is part of
-                // resultIfPiSenderAllowsBal
-                if (resultIfPiSenderAllowsBal != BAL_BLOCK
-                        && balAllowedByPiSender.allowsBackgroundActivityStarts()
-                        && !logVerdictChangeByPiDefaultChange) {
-                    // The result is to allow (because the sender allows BAL) and we are not
-                    // interested in logging differences, so just return.
-                    return resultIfPiSenderAllowsBal;
-                }
-            } else {
-                // If caller app was NOT based on PI sender and we found a allow reason we should
-                // have returned already
-                checkState(balAllowedForCaller == BAL_BLOCK,
-                        "balAllowedForCaller = " + balAllowedForCaller + " (should have returned)");
-            }
-        }
-        // If we are here, it means all exemptions not based on PI sender failed, so we'll block
-        // unless resultIfPiSenderAllowsBal is an allow and the PI sender allows BAL
-
-        if (realCallingPackage == null) {
-            realCallingPackage = (callingUid == realCallingUid ? callingPackage :
-                    mService.mContext.getPackageManager().getNameForUid(realCallingUid))
-                    + "[debugOnly]";
+        // up and alive.
+        // Don't abort if the callerApp or other processes of that uid are allowed in any way.
+        BalVerdict callerAppAllowsBal = checkProcessAllowsBal(callerApp, state);
+        if (callerAppAllowsBal.allows()) {
+            return callerAppAllowsBal;
         }
 
-        String stateDumpLog = " [callingPackage: " + callingPackage
-                + "; callingUid: " + callingUid
-                + "; appSwitchState: " + appSwitchState
-                + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
-                + "; callingUidProcState: " + DebugUtils.valueToString(
-                        ActivityManager.class, "PROCESS_STATE_", callingUidProcState)
-                + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
-                + "; balAllowedByPiSender: " + balAllowedByPiSender
-                + "; realCallingPackage: " + realCallingPackage
-                + "; realCallingUid: " + realCallingUid
-                + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow
-                + "; realCallingUidProcState: " + DebugUtils.valueToString(
-                        ActivityManager.class, "PROCESS_STATE_", realCallingUidProcState)
-                + "; isRealCallingUidPersistentSystemProcess: "
-                        + isRealCallingUidPersistentSystemProcess
-                + "; originatingPendingIntent: " + originatingPendingIntent
-                + "; backgroundStartPrivileges: " + backgroundStartPrivileges
-                + "; intent: " + intent
-                + "; callerApp: " + callerApp
-                + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask())
-                + "; resultIfPiSenderAllowsBal: " + balCodeToString(resultIfPiSenderAllowsBal)
-                + "]";
-        if (resultIfPiSenderAllowsBal != BAL_BLOCK) {
-            // We should have returned before if !logVerdictChangeByPiDefaultChange
-            checkState(logVerdictChangeByPiDefaultChange,
-                    "resultIfPiSenderAllowsBal = " + balCodeToString(resultIfPiSenderAllowsBal)
-                        + " at the end but logVerdictChangeByPiDefaultChange = false");
-            if (balAllowedByPiSender.allowsBackgroundActivityStarts()) {
-                // The verdict changed from block to allow, PI sender default change is off and
-                // we'd block if it were on
-                Slog.wtf(TAG, "With BAL hardening this activity start would be blocked!"
-                        + stateDumpLog);
-                return resultIfPiSenderAllowsBal;
-            } else {
-                // The verdict changed from allow (resultIfPiSenderAllowsBal) to block, PI sender
-                // default change is on (otherwise we would have fallen into if above) and we'd
-                // allow if it were off
-                Slog.wtf(TAG, "Without BAL hardening this activity start would be allowed!"
-                        + stateDumpLog);
-            }
-        }
-        // anything that has fallen through would currently be aborted
-        Slog.w(TAG, "Background activity launch blocked" + stateDumpLog);
-        // log aborted activity start to TRON
-        if (mService.isActivityStartsLoggingEnabled()) {
-            mSupervisor
-                    .getActivityMetricsLogger()
-                    .logAbortedBgActivityStart(
-                            intent,
-                            callerApp,
-                            callingUid,
-                            callingPackage,
-                            callingUidProcState,
-                            callingUidHasAnyVisibleWindow,
-                            realCallingUid,
-                            realCallingUidProcState,
-                            realCallingUidHasAnyVisibleWindow,
-                            (originatingPendingIntent != null));
-        }
-        return BAL_BLOCK;
+        // If we are here, it means all exemptions based on the creator failed
+        return BalVerdict.BLOCK;
     }
 
-    private @BalCode int checkPiBackgroundActivityStart(int callingUid, int realCallingUid,
-            BackgroundStartPrivileges backgroundStartPrivileges, Intent intent,
-            ActivityOptions checkedOptions, boolean realCallingUidHasAnyVisibleWindow,
-            boolean isRealCallingUidPersistentSystemProcess, String verdictLog) {
-        final boolean useCallerPermission =
-                PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
-        if (useCallerPermission
+    /**
+     * @return A code denoting which BAL rule allows an activity to be started,
+     * or {@link #BAL_BLOCK} if the launch should be blocked
+     */
+    BalVerdict checkBackgroundActivityStartAllowedBySender(
+            BalState state,
+            ActivityOptions checkedOptions) {
+        int realCallingUid = state.mRealCallingUid;
+        BackgroundStartPrivileges backgroundStartPrivileges = state.mBackgroundStartPrivileges;
+
+        if (PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions)
                 && ActivityManager.checkComponentPermission(
-                                android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+                android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
                 realCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
-            return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
-                /*background*/ false, callingUid, realCallingUid, intent,
-                "realCallingUid has BAL permission. realCallingUid: " + realCallingUid,
-                verdictLog);
+            return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                    /*background*/ false,
+                    "realCallingUid has BAL permission. realCallingUid: " + realCallingUid);
         }
 
         // don't abort if the realCallingUid has a visible window
         // TODO(b/171459802): We should check appSwitchAllowed also
-        if (realCallingUidHasAnyVisibleWindow) {
-            return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
-                    /*background*/ false, callingUid, realCallingUid, intent,
+        if (state.mRealCallingUidHasAnyVisibleWindow) {
+            return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                    /*background*/ false,
                     "realCallingUid has visible (non-toast) window. realCallingUid: "
-                            + realCallingUid, verdictLog);
+                            + realCallingUid);
         }
         // if the realCallingUid is a persistent system process, abort if the IntentSender
         // wasn't allowed to start an activity
-        if (isRealCallingUidPersistentSystemProcess
+        if (state.mIsRealCallingUidPersistentSystemProcess
                 && backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
-            return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
-                    /*background*/ false, callingUid, realCallingUid, intent,
+            return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                    /*background*/ false,
                     "realCallingUid is persistent system process AND intent "
                             + "sender allowed (allowBackgroundActivityStart = true). "
-                            + "realCallingUid: " + realCallingUid, verdictLog);
+                            + "realCallingUid: " + realCallingUid);
         }
         // don't abort if the realCallingUid is an associated companion app
         if (mService.isAssociatedCompanionApp(
                 UserHandle.getUserId(realCallingUid), realCallingUid)) {
-            return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
-                    /*background*/ false, callingUid, realCallingUid, intent,
+            return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                    /*background*/ false,
                     "realCallingUid is a companion app. "
-                            + "realCallingUid: " + realCallingUid, verdictLog);
+                            + "realCallingUid: " + realCallingUid);
         }
-        return BAL_BLOCK;
+
+        // don't abort if the callerApp or other processes of that uid are allowed in any way
+        BalVerdict realCallerAppAllowsBal =
+                checkProcessAllowsBal(state.mRealCallerApp, state);
+        if (realCallerAppAllowsBal.allows()) {
+            return realCallerAppAllowsBal;
+        }
+
+        // If we are here, it means all exemptions based on PI sender failed
+        return BalVerdict.BLOCK;
+    }
+
+    /**
+     * Check if the app allows BAL.
+     *
+     * See {@link BackgroundLaunchProcessController#areBackgroundActivityStartsAllowed(int, int,
+     * String, int, boolean, boolean, boolean, long, long, long)} for details on the
+     * exceptions.
+     */
+    private BalVerdict checkProcessAllowsBal(WindowProcessController app,
+            BalState state) {
+        if (app == null) {
+            return BalVerdict.BLOCK;
+        }
+        // first check the original calling process
+        final BalVerdict balAllowedForCaller = app
+                .areBackgroundActivityStartsAllowed(state.mAppSwitchState);
+        if (balAllowedForCaller.allows()) {
+            return balAllowedForCaller.withProcessInfo("callerApp process", app);
+        } else {
+            // only if that one wasn't allowed, check the other ones
+            final ArraySet<WindowProcessController> uidProcesses =
+                    mService.mProcessMap.getProcesses(app.mUid);
+            if (uidProcesses != null) {
+                for (int i = uidProcesses.size() - 1; i >= 0; i--) {
+                    final WindowProcessController proc = uidProcesses.valueAt(i);
+                    if (proc != app) {
+                        BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
+                                state.mAppSwitchState);
+                        if (balAllowedForUid.allows()) {
+                            return balAllowedForCaller.withProcessInfo("process", proc);
+                        }
+                    }
+                }
+            }
+        }
+        return BalVerdict.BLOCK;
     }
 
     /**
@@ -1091,55 +1209,6 @@
         return joiner.toString();
     }
 
-    static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background,
-            int callingUid, int realCallingUid, Intent intent, int pid, String msg) {
-        return logStartAllowedAndReturnCode(code, background, callingUid, realCallingUid, intent,
-                DEBUG_ACTIVITY_STARTS ?  ("[Process(" + pid + ")]" + msg) : "");
-    }
-
-    static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background,
-            int callingUid, int realCallingUid, Intent intent, String msg) {
-        return logStartAllowedAndReturnCode(code, background, callingUid, realCallingUid, intent,
-            msg, VERDICT_ALLOWED);
-    }
-
-    /**
-     * Logs the start and returns one of the provided codes depending on if the PI sender allows
-     * using its BAL privileges.
-     */
-    static @BalCode int logStartAllowedAndReturnCode(@BalCode int result,
-            @BalCode int resultIfPiSenderAllowsBal, BackgroundStartPrivileges balAllowedByPiSender,
-            boolean background, int callingUid, int realCallingUid, Intent intent, String msg) {
-        if (resultIfPiSenderAllowsBal != BAL_BLOCK
-                && balAllowedByPiSender.allowsBackgroundActivityStarts()) {
-            // resultIfPiSenderAllowsBal was already logged, so just return
-            return resultIfPiSenderAllowsBal;
-        }
-        return logStartAllowedAndReturnCode(result, background, callingUid, realCallingUid,
-            intent, msg, VERDICT_ALLOWED);
-    }
-
-
-    static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background,
-            int callingUid, int realCallingUid, Intent intent, String msg, String verdict) {
-        statsLogBalAllowed(code, callingUid, realCallingUid, intent);
-        if (DEBUG_ACTIVITY_STARTS) {
-            StringBuilder builder = new StringBuilder();
-            if (background) {
-                builder.append("Background ");
-            }
-            builder.append(verdict + ": " + msg + ". callingUid: " + callingUid + ". ");
-            builder.append("BAL Code: ");
-            builder.append(balCodeToString(code));
-            if (verdict.equals(VERDICT_ALLOWED)) {
-                Slog.i(TAG, builder.toString());
-            } else {
-                Slog.d(TAG, builder.toString());
-            }
-        }
-        return code;
-    }
-
     private static boolean isSystemExemptFlagEnabled() {
         return DeviceConfig.getBoolean(
                 NAMESPACE_WINDOW_MANAGER,
@@ -1147,8 +1216,12 @@
                 /* defaultValue= */ true);
     }
 
-    private static void statsLogBalAllowed(
-            @BalCode int code, int callingUid, int realCallingUid, Intent intent) {
+    private static BalVerdict statsLog(BalVerdict finalVerdict, BalState state) {
+        @BalCode int code = finalVerdict.getCode();
+        int callingUid = state.mCallingUid;
+        int realCallingUid = state.mRealCallingUid;
+        Intent intent = state.mIntent;
+
         if (code == BAL_ALLOW_PENDING_INTENT
                 && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
             String activityName =
@@ -1168,6 +1241,7 @@
                     callingUid,
                     realCallingUid);
         }
+        return finalVerdict;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 527edc1..e849589 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -27,7 +27,6 @@
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
-import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
 
 import static java.util.Objects.requireNonNull;
 
@@ -49,6 +48,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -96,37 +96,33 @@
         mBackgroundActivityStartCallback = callback;
     }
 
-    @BackgroundActivityStartController.BalCode
-    int areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
+    BalVerdict areBackgroundActivityStartsAllowed(
+            int pid, int uid, String packageName,
             int appSwitchState, boolean isCheckingForFgsStart,
             boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
             long lastStopAppSwitchesTime, long lastActivityLaunchTime,
             long lastActivityFinishTime) {
         // Allow if the proc is instrumenting with background activity starts privs.
         if (hasBackgroundActivityStartPrivileges) {
-            return BackgroundActivityStartController.logStartAllowedAndReturnCode(
-                    BAL_ALLOW_PERMISSION, /*background*/ true, uid, uid, /*intent*/ null,
-                    pid, "Activity start allowed: process instrumenting with background "
-                        + "activity starts privileges");
+            return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+                    "Activity start allowed: process instrumenting with background "
+                            + "activity starts privileges");
         }
         // Allow if the flag was explicitly set.
         if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
-            return BackgroundActivityStartController.logStartAllowedAndReturnCode(
-                    BAL_ALLOW_PERMISSION, /*background*/ true, uid, uid, /*intent*/ null,
-                    pid, "Activity start allowed: process allowed by token");
+            return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+                    "Activity start allowed: process allowed by token");
         }
         // Allow if the caller is bound by a UID that's currently foreground.
         if (isBoundByForegroundUid()) {
-            return BackgroundActivityStartController.logStartAllowedAndReturnCode(
-                    BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, uid, uid, /*intent*/ null,
-                    pid, "Activity start allowed: process bound by foreground uid");
+            return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
+                    "Activity start allowed: process bound by foreground uid");
         }
         // Allow if the caller has an activity in any foreground task.
         if (hasActivityInVisibleTask
                 && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
-            return BackgroundActivityStartController.logStartAllowedAndReturnCode(
-                    BAL_ALLOW_FOREGROUND, /*background*/ false, uid, uid, /*intent*/ null,
-                    pid, "Activity start allowed: process has activity in foreground task");
+            return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
+                    "Activity start allowed: process has activity in foreground task");
         }
 
         // If app switching is not allowed, we ignore all the start activity grace period
@@ -141,9 +137,8 @@
                 // let app to be able to start background activity even it's in grace period.
                 if (lastActivityLaunchTime > lastStopAppSwitchesTime
                         || lastActivityFinishTime > lastStopAppSwitchesTime) {
-                    return BackgroundActivityStartController.logStartAllowedAndReturnCode(
-                            BAL_ALLOW_GRACE_PERIOD, /*background*/ true, uid, uid, /*intent*/ null,
-                            pid, "Activity start allowed: within "
+                    return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
+                            "Activity start allowed: within "
                                     + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
                 }
                 if (DEBUG_ACTIVITY_STARTS) {
@@ -154,7 +149,7 @@
 
             }
         }
-        return BAL_BLOCK;
+        return BalVerdict.BLOCK;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4fa6e29..f314900 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1622,7 +1622,17 @@
             return;
         }
 
+        final Transition.ReadyCondition displayConfig = mTransitionController.isCollecting()
+                ? new Transition.ReadyCondition("displayConfig", this) : null;
+        if (displayConfig != null) {
+            mTransitionController.waitFor(displayConfig);
+        } else if (mTransitionController.isShellTransitionsEnabled()) {
+            Slog.e(TAG, "Display reconfigured outside of a transition: " + this);
+        }
         final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
+        if (displayConfig != null) {
+            displayConfig.meet();
+        }
         if (configUpdated) {
             return;
         }
@@ -4706,6 +4716,17 @@
             scheduleAnimation();
 
             mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
+        } else if (mImeControlTarget != null && mImeControlTarget == mImeLayeringTarget) {
+            // Even if the IME surface parent is not changed, the layer target belonging to the
+            // parent may have changes. Then attempt to reassign if the IME control target is
+            // possible to be the relative layer.
+            final SurfaceControl lastRelativeLayer = mImeWindowsContainer.getLastRelativeLayer();
+            if (lastRelativeLayer != mImeLayeringTarget.mSurfaceControl) {
+                assignRelativeLayerForIme(getSyncTransaction(), false /* forceUpdate */);
+                if (lastRelativeLayer != mImeWindowsContainer.getLastRelativeLayer()) {
+                    scheduleAnimation();
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 17bfeb4..d69c5ef 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1034,20 +1034,15 @@
                 }
                 break;
             case TYPE_NAVIGATION_BAR:
-                mContext.enforcePermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
-                        "DisplayPolicy");
+                mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+                        callingPid, callingUid, "DisplayPolicy");
                 if (mNavigationBar != null && mNavigationBar.isAlive()) {
                     return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
                 }
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
-                // Check for permission if the caller is not the recents component.
-                if (!mService.mAtmService.isCallerRecents(callingUid)) {
-                    mContext.enforcePermission(
-                            android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
-                            "DisplayPolicy");
-                }
+                mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+                        callingPid, callingUid, "DisplayPolicy");
                 break;
             case TYPE_STATUS_BAR_ADDITIONAL:
             case TYPE_STATUS_BAR_SUB_PANEL:
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 1a319ad..fa2c94a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -87,6 +87,7 @@
     private RootWindowContainer mRootWindowContainer;
     private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
     private boolean mWaitingForWakeTransition;
+    private Transition.ReadyCondition mWaitAodHide = null;
 
     KeyguardController(ActivityTaskManagerService service,
             ActivityTaskSupervisor taskSupervisor) {
@@ -565,8 +566,14 @@
 
     // Defer transition until AOD dismissed.
     void updateDeferTransitionForAod(boolean waiting) {
-        if (waiting == mWaitingForWakeTransition) {
-            return;
+        if (mService.getTransitionController().useFullReadyTracking()) {
+            if (waiting == (mWaitAodHide != null)) {
+                return;
+            }
+        } else {
+            if (waiting == mWaitingForWakeTransition) {
+                return;
+            }
         }
         if (!mService.getTransitionController().isCollecting()) {
             return;
@@ -575,12 +582,17 @@
         if (waiting && isAodShowing(DEFAULT_DISPLAY)) {
             mWaitingForWakeTransition = true;
             mWindowManager.mAtmService.getTransitionController().deferTransitionReady();
+            mWaitAodHide = new Transition.ReadyCondition("AOD hidden");
+            mWindowManager.mAtmService.getTransitionController().waitFor(mWaitAodHide);
             mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS);
         } else if (!waiting) {
             // dismiss the deferring if the AOD state change or cancel awake.
             mWaitingForWakeTransition = false;
             mWindowManager.mAtmService.getTransitionController().continueTransitionReady();
             mWindowManager.mH.removeCallbacks(mResetWaitTransition);
+            final Transition.ReadyCondition waitAodHide = mWaitAodHide;
+            mWaitAodHide = null;
+            waitAodHide.meet();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2281395..c81105a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2051,6 +2051,8 @@
         }
 
         transitionController.deferTransitionReady();
+        Transition.ReadyCondition pipChangesApplied = new Transition.ReadyCondition("movedToPip");
+        transitionController.waitFor(pipChangesApplied);
         mService.deferWindowLayout();
         try {
             // This will change the root pinned task's windowing mode to its original mode, ensuring
@@ -2235,6 +2237,7 @@
                 }
             } finally {
                 transitionController.continueTransitionReady();
+                pipChangesApplied.meet();
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6c31e3e..6cad16c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -486,8 +486,6 @@
 
     private boolean mForceShowForAllUsers;
 
-    private boolean mForceTranslucent = false;
-
     // The display category name for this task.
     String mRequiredDisplayCategory;
 
@@ -3643,7 +3641,7 @@
      */
     TaskFragmentParentInfo getTaskFragmentParentInfo() {
         return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
-                shouldBeVisible(null /* starting */));
+                shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity());
     }
 
     @Override
@@ -4502,10 +4500,6 @@
         return true;
     }
 
-    void setForceTranslucent(boolean set) {
-        mForceTranslucent = set;
-    }
-
     @Override
     public boolean isAlwaysOnTop() {
         return !isForceHidden() && super.isAlwaysOnTop();
@@ -4523,11 +4517,6 @@
     }
 
     @Override
-    protected boolean isForceTranslucent() {
-        return mForceTranslucent;
-    }
-
-    @Override
     long getProtoFieldId() {
         return TASK;
     }
@@ -4715,7 +4704,7 @@
         }
         if (isAttached()) {
             setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            moveTaskToBackInner(this);
+            moveTaskToBackInner(this, null /* transition */);
         }
         if (top.isAttached()) {
             top.setWindowingMode(WINDOWING_MODE_UNDEFINED);
@@ -5718,24 +5707,27 @@
                         mTransitionController.requestStartTransition(transition, tr,
                                 null /* remoteTransition */, null /* displayChange */);
                         mTransitionController.collect(tr);
-                        moveTaskToBackInner(tr);
+                        moveTaskToBackInner(tr, transition);
                     });
         } else {
             // Skip the transition for pinned task.
             if (!inPinnedWindowingMode()) {
                 mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
             }
-            moveTaskToBackInner(tr);
+            moveTaskToBackInner(tr, null /* transition */);
         }
         return true;
     }
 
-    private boolean moveTaskToBackInner(@NonNull Task task) {
-        if (mTransitionController.isShellTransitionsEnabled()) {
+    private boolean moveTaskToBackInner(@NonNull Task task, @Nullable Transition transition) {
+        final Transition.ReadyCondition movedToBack =
+                new Transition.ReadyCondition("moved-to-back", task);
+        if (transition != null) {
             // Preventing from update surface position for WindowState if configuration changed,
             // because the position is depends on WindowFrame, so update the position before
             // relayout will only update it to "old" position.
             mAtmService.deferWindowLayout();
+            transition.mReadyTracker.add(movedToBack);
         }
         try {
             moveToBack("moveTaskToBackInner", task);
@@ -5752,6 +5744,9 @@
             if (mTransitionController.isShellTransitionsEnabled()) {
                 mAtmService.continueWindowLayout();
             }
+            if (transition != null) {
+                movedToBack.meet();
+            }
         }
         ActivityRecord topActivity = getDisplayArea().topRunningActivity();
         Task topRootTask = topActivity == null ? null : topActivity.getRootTask();
@@ -6056,6 +6051,11 @@
             // Non-root task position changed.
             mRootWindowContainer.invalidateTaskLayers();
         }
+
+        if (child.asActivityRecord() != null) {
+            // Send for TaskFragmentParentInfo#hasDirectActivity change.
+            sendTaskFragmentParentInfoChangedIfNeeded();
+        }
     }
 
     void reparent(TaskDisplayArea newParent, boolean onTop) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 82d3424..00f2b89 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -374,6 +374,8 @@
     @interface FlagForceHidden {}
     protected int mForceHiddenFlags = 0;
 
+    private boolean mForceTranslucent = false;
+
     final Point mLastSurfaceSize = new Point();
 
     private final Rect mTmpBounds = new Rect();
@@ -843,8 +845,12 @@
         return true;
     }
 
-    protected boolean isForceTranslucent() {
-        return false;
+    boolean isForceTranslucent() {
+        return mForceTranslucent;
+    }
+
+    void setForceTranslucent(boolean set) {
+        mForceTranslucent = set;
     }
 
     boolean isLeafTaskFragment() {
@@ -1049,6 +1055,10 @@
         return getActivity(ActivityRecord::canBeTopRunning);
     }
 
+    /**
+     * Reports non-finishing activity count including this TaskFragment's child embedded
+     * TaskFragments' children activities.
+     */
     int getNonFinishingActivityCount() {
         final int[] runningActivityCount = new int[1];
         forAllActivities(a -> {
@@ -1059,6 +1069,20 @@
         return runningActivityCount[0];
     }
 
+    /**
+     * Returns {@code true} if there's any non-finishing direct children activity, which is not
+     * embedded in TaskFragments
+     */
+    boolean hasNonFinishingDirectActivity() {
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityRecord activity = getChildAt(i).asActivityRecord();
+            if (activity != null && !activity.finishing) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     boolean isTopActivityFocusable() {
         final ActivityRecord r = topRunningActivity();
         return r != null ? r.isFocusable()
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index ff766be..34ae370 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -157,6 +157,15 @@
          */
         private final ArrayMap<IBinder, Integer> mDeferredTransitions = new ArrayMap<>();
 
+        /**
+         * Map from {@link TaskFragmentTransaction#getTransactionToken()} to a
+         * {@link Transition.ReadyCondition} that is waiting for the {@link TaskFragmentTransaction}
+         * to complete.
+         * @see #onTransactionHandled
+         */
+        private final ArrayMap<IBinder, Transition.ReadyCondition> mInFlightTransactions =
+                new ArrayMap<>();
+
         TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
                 boolean isSystemOrganizer) {
             mOrganizer = organizer;
@@ -173,7 +182,7 @@
         @Override
         public void binderDied() {
             synchronized (mGlobalLock) {
-                removeOrganizer(mOrganizer);
+                removeOrganizer(mOrganizer, "client died");
             }
         }
 
@@ -195,7 +204,7 @@
             mOrganizedTaskFragments.remove(taskFragment);
         }
 
-        void dispose() {
+        void dispose(@NonNull String reason) {
             boolean wasVisible = false;
             for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
                 final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
@@ -236,6 +245,10 @@
                 // Cleanup any running transaction to unblock the current transition.
                 onTransactionFinished(mDeferredTransitions.keyAt(i));
             }
+            for (int i = mInFlightTransactions.size() - 1; i >= 0; i--) {
+                // Cleanup any in-flight transactions to unblock the transition.
+                mInFlightTransactions.valueAt(i).meetAlternate("disposed(" + reason + ")");
+            }
             mOrganizer.asBinder().unlinkToDeath(this, 0 /* flags */);
         }
 
@@ -398,11 +411,6 @@
                 Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
                 return;
             }
-            onTransactionStarted(transaction.getTransactionToken());
-        }
-
-        /** Called when the transaction is sent to the organizer. */
-        void onTransactionStarted(@NonNull IBinder transactionToken) {
             if (!mWindowOrganizerController.getTransitionController().isCollecting()) {
                 return;
             }
@@ -410,9 +418,13 @@
                     .getCollectingTransitionId();
             ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                     "Defer transition id=%d for TaskFragmentTransaction=%s", transitionId,
-                    transactionToken);
-            mDeferredTransitions.put(transactionToken, transitionId);
+                    transaction.getTransactionToken());
+            mDeferredTransitions.put(transaction.getTransactionToken(), transitionId);
             mWindowOrganizerController.getTransitionController().deferTransitionReady();
+            final Transition.ReadyCondition transactionApplied = new Transition.ReadyCondition(
+                    "task-fragment transaction", transaction);
+            mWindowOrganizerController.getTransitionController().waitFor(transactionApplied);
+            mInFlightTransactions.put(transaction.getTransactionToken(), transactionApplied);
         }
 
         /** Called when the transaction is finished. */
@@ -496,7 +508,7 @@
                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                         "Unregister task fragment organizer=%s uid=%d pid=%d",
                         organizer.asBinder(), uid, pid);
-                removeOrganizer(organizer);
+                removeOrganizer(organizer, "unregistered");
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -564,6 +576,11 @@
                     : null;
             if (state != null) {
                 state.onTransactionFinished(transactionToken);
+                final Transition.ReadyCondition condition =
+                        state.mInFlightTransactions.remove(transactionToken);
+                if (condition != null) {
+                    condition.meet();
+                }
             }
         }
     }
@@ -777,7 +794,8 @@
         return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
     }
 
-    private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+    private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull String reason) {
         final TaskFragmentOrganizerState state = mTaskFragmentOrganizerState.get(
                 organizer.asBinder());
         if (state == null) {
@@ -788,7 +806,7 @@
         // event dispatch as result of surface placement.
         mPendingTaskFragmentEvents.remove(organizer.asBinder());
         // remove all of the children of the organized TaskFragment
-        state.dispose();
+        state.dispose(reason);
         mTaskFragmentOrganizerState.remove(organizer.asBinder());
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7d65c61..f6b4972 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -236,7 +236,8 @@
     private IRemoteCallback mClientAnimationFinishCallback = null;
 
     private @TransitionState int mState = STATE_PENDING;
-    private final ReadyTracker mReadyTracker = new ReadyTracker();
+    private final ReadyTrackerOld mReadyTrackerOld = new ReadyTrackerOld();
+    final ReadyTracker mReadyTracker = new ReadyTracker(this);
 
     private int mRecentsDisplayId = INVALID_DISPLAY;
 
@@ -656,7 +657,7 @@
             updateTransientFlags(info);
             mChanges.put(curr, info);
             if (isReadyGroup(curr)) {
-                mReadyTracker.addGroup(curr);
+                mReadyTrackerOld.addGroup(curr);
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
                                 + " Transition %d with root=%s", mSyncId, curr);
             }
@@ -887,13 +888,18 @@
      */
     void setReady(WindowContainer wc, boolean ready) {
         if (!isCollecting() || mSyncId < 0) return;
-        mReadyTracker.setReadyFrom(wc, ready);
+        mReadyTrackerOld.setReadyFrom(wc, ready);
         applyReady();
     }
 
     private void applyReady() {
         if (mState < STATE_STARTED) return;
-        final boolean ready = mReadyTracker.allReady();
+        final boolean ready;
+        if (mController.useFullReadyTracking()) {
+            ready = mReadyTracker.isReady();
+        } else {
+            ready = mReadyTrackerOld.allReady();
+        }
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                 "Set transition ready=%b %d", ready, mSyncId);
         boolean changed = mSyncEngine.setReady(mSyncId, ready);
@@ -909,22 +915,22 @@
 
     /**
      * Sets all possible ready groups to ready.
-     * @see ReadyTracker#setAllReady.
+     * @see ReadyTrackerOld#setAllReady
      */
     void setAllReady() {
         if (!isCollecting() || mSyncId < 0) return;
-        mReadyTracker.setAllReady();
+        mReadyTrackerOld.setAllReady();
         applyReady();
     }
 
     @VisibleForTesting
     boolean allReady() {
-        return mReadyTracker.allReady();
+        return mReadyTrackerOld.allReady();
     }
 
     /** This transition has all of its expected participants. */
     boolean isPopulated() {
-        return mState >= STATE_STARTED && mReadyTracker.allReady();
+        return mState >= STATE_STARTED && mReadyTrackerOld.allReady();
     }
 
     /**
@@ -1438,6 +1444,13 @@
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Force Playing Transition: %d",
                 mSyncId);
         mForcePlaying = true;
+        // backwards since conditions are removed.
+        for (int i = mReadyTracker.mConditions.size() - 1; i >= 0; --i) {
+            mReadyTracker.mConditions.get(i).meetAlternate("play-now");
+        }
+        final ReadyCondition forcePlay = new ReadyCondition("force-play-now");
+        mReadyTracker.add(forcePlay);
+        forcePlay.meet();
         setAllReady();
         if (mState == STATE_COLLECTING) {
             start();
@@ -1483,6 +1496,21 @@
             return;
         }
 
+        if (mController.useFullReadyTracking()) {
+            if (mReadyTracker.mMet.isEmpty()) {
+                Slog.e(TAG, "#" + mSyncId + ": No conditions provided");
+            } else {
+                for (int i = 0; i < mReadyTracker.mConditions.size(); ++i) {
+                    Slog.e(TAG, "#" + mSyncId + ": unmet condition at ready: "
+                            + mReadyTracker.mConditions.get(i));
+                }
+            }
+            for (int i = 0; i < mReadyTracker.mMet.size(); ++i) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "#%d: Met condition: %s",
+                        mSyncId, mReadyTracker.mMet.get(i));
+            }
+        }
+
         // Commit the visibility of visible activities before calculateTransitionInfo(), so the
         // TaskInfo can be visible. Also it needs to be done before moveToPlaying(), otherwise
         // ActivityRecord#canShowWindows() may reject to show its window. The visibility also
@@ -2797,7 +2825,7 @@
             // if an activity is pausing, it will call setReady(ar, false) and wait for the next
             // resumed activity. Then do not set to ready because the transition only contains
             // partial participants. Otherwise the transition may only handle HIDE and miss OPEN.
-            if (!mReadyTracker.mUsed) {
+            if (!mReadyTrackerOld.mUsed) {
                 setReady(dc, true);
             }
         }
@@ -3059,19 +3087,160 @@
      * {@link #continueTransitionReady}
      */
     void deferTransitionReady() {
-        ++mReadyTracker.mDeferReadyDepth;
+        ++mReadyTrackerOld.mDeferReadyDepth;
         // Make sure it wait until #continueTransitionReady() is called.
         mSyncEngine.setReady(mSyncId, false);
     }
 
     /** This undoes one call to {@link #deferTransitionReady}. */
     void continueTransitionReady() {
-        --mReadyTracker.mDeferReadyDepth;
+        --mReadyTrackerOld.mDeferReadyDepth;
         // Apply ready in case it is waiting for the previous defer call.
         applyReady();
     }
 
     /**
+     * Represents a condition that must be met before an associated transition can be considered
+     * ready.
+     *
+     * Expected usage is that a ReadyCondition is created and then attached to a transition's
+     * ReadyTracker via {@link ReadyTracker#add}. After that, it is expected to monitor the state
+     * of the system and when the condition it represents is met, it will call
+     * {@link ReadyTracker#meet}.
+     *
+     * This base class is a simple explicit, named condition. A caller will create/attach the
+     * condition and then explicitly call {@link #meet} on it (which internally calls
+     * {@link ReadyTracker#meet}.
+     *
+     * Example:
+     * <pre>
+     *     ReadyCondition myCondition = new ReadyCondition("my condition");
+     *     transitionController.waitFor(myCondition);
+     *     ... Some operations ...
+     *     myCondition.meet();
+     * </pre>
+     */
+    static class ReadyCondition {
+        final String mName;
+
+        /** Just used for debugging */
+        final Object mDebugTarget;
+        ReadyTracker mTracker;
+        boolean mMet = false;
+
+        /** If set (non-null), then this is met by another reason besides state (eg. timeout). */
+        String mAlternate = null;
+
+        ReadyCondition(@NonNull String name) {
+            mName = name;
+            mDebugTarget = null;
+        }
+
+        ReadyCondition(@NonNull String name, @Nullable Object debugTarget) {
+            mName = name;
+            mDebugTarget = debugTarget;
+        }
+
+        protected String getDebugRep() {
+            if (mDebugTarget != null) {
+                return mName + ":" + mDebugTarget;
+            }
+            return mName;
+        }
+
+        @Override
+        public String toString() {
+            return "{" + getDebugRep() + (mAlternate != null ? " (" + mAlternate + ")" : "") + "}";
+        }
+
+        /**
+         * Instructs this condition to start tracking system state to detect when this is met.
+         * Don't call this directly; it is called when this object is attached to a transition's
+         * ready-tracker.
+         */
+        void startTracking() {
+        }
+
+        /**
+         * Immediately consider this condition met by an alternative reason (one which doesn't
+         * match the normal intent of this condition -- eg. a timeout).
+         */
+        void meetAlternate(@NonNull String reason) {
+            if (mMet) return;
+            mAlternate = reason;
+            meet();
+        }
+
+        /** Immediately consider this condition met. */
+        void meet() {
+            if (mMet) return;
+            if (mTracker == null) {
+                throw new IllegalStateException("Can't meet a condition before it is waited on");
+            }
+            mTracker.meet(this);
+        }
+    }
+
+    static class ReadyTracker {
+        /**
+         * Used as a place-holder in situations where the transition system isn't active (such as
+         * early-boot, mid shell crash/recovery, or when using legacy).
+         */
+        static final ReadyTracker NULL_TRACKER = new ReadyTracker(null);
+
+        private final Transition mTransition;
+
+        /** List of conditions that are still being waited on. */
+        final ArrayList<ReadyCondition> mConditions = new ArrayList<>();
+
+        /** List of already-met conditions. Fully-qualified for debugging. */
+        final ArrayList<ReadyCondition> mMet = new ArrayList<>();
+
+        ReadyTracker(Transition transition) {
+            mTransition = transition;
+        }
+
+        void add(@NonNull ReadyCondition condition) {
+            if (mTransition == null || !mTransition.mController.useFullReadyTracking()) {
+                condition.mTracker = NULL_TRACKER;
+                return;
+            }
+            mConditions.add(condition);
+            condition.mTracker = this;
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Add condition %s for #%d",
+                    condition, mTransition.mSyncId);
+            condition.startTracking();
+        }
+
+        void meet(@NonNull ReadyCondition condition) {
+            if (mTransition == null || !mTransition.mController.useFullReadyTracking()) return;
+            if (mTransition.mState >= STATE_PLAYING) {
+                Slog.w(TAG, "#%d: Condition met too late, already in state=" + mTransition.mState
+                        + ": " + condition);
+                return;
+            }
+            if (!mConditions.remove(condition)) {
+                if (mMet.contains(condition)) {
+                    throw new IllegalStateException("Can't meet the same condition more than once: "
+                            + condition + " #" + mTransition.mSyncId);
+                } else {
+                    throw new IllegalArgumentException("Can't meet a condition that isn't being "
+                            + "waited on: " + condition + " in #" + mTransition.mSyncId);
+                }
+            }
+            condition.mMet = true;
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Met condition %s for #%d (%d"
+                    + " left)", condition, mTransition.mSyncId, mConditions.size());
+            mMet.add(condition);
+            mTransition.applyReady();
+        }
+
+        boolean isReady() {
+            return mConditions.isEmpty() && !mMet.isEmpty();
+        }
+    }
+
+    /**
      * The transition sync mechanism has 2 parts:
      *   1. Whether all WM operations for a particular transition are "ready" (eg. did the app
      *      launch or stop or get a new configuration?).
@@ -3084,7 +3253,7 @@
      * of readiness across the multiple groups. Currently, we assume that each display is a group
      * since that is how it has been until now.
      */
-    private static class ReadyTracker {
+    private static class ReadyTrackerOld {
         private final ArrayMap<WindowContainer, Boolean> mReadyGroups = new ArrayMap<>();
 
         /**
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 8ac21e4..28c24c8 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -62,6 +62,7 @@
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.FgThread;
+import com.android.window.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.function.Consumer;
@@ -131,6 +132,7 @@
     TransitionTracer mTransitionTracer;
 
     private SystemPerformanceHinter mSystemPerformanceHinter;
+    private boolean mFullReadyTracking = false;
 
     private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
             new ArrayList<>();
@@ -281,6 +283,7 @@
         registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
         setSyncEngine(wms.mSyncEngine);
         setSystemPerformanceHinter(wms.mSystemPerformanceHinter);
+        mFullReadyTracking = Flags.transitReadyTracking();
     }
 
     @VisibleForTesting
@@ -397,6 +400,14 @@
         return isShellTransitionsEnabled() && SHELL_TRANSITIONS_ROTATION;
     }
 
+    boolean useFullReadyTracking() {
+        return mFullReadyTracking;
+    }
+
+    void setFullReadyTrackingForTest(boolean enabled) {
+        mFullReadyTracking = enabled;
+    }
+
     /**
      * @return {@code true} if transition is actively collecting changes. This is {@code false}
      * once a transition is playing
@@ -1475,6 +1486,19 @@
         applySync.accept(false /* deferred */);
     }
 
+    /**
+     * Instructs the collecting transition to wait until `condition` is met before it is
+     * considered ready.
+     */
+    void waitFor(@NonNull Transition.ReadyCondition condition) {
+        if (mCollectingTransition == null) {
+            Slog.e(TAG, "No collecting transition available to wait for " + condition);
+            condition.mTracker = Transition.ReadyTracker.NULL_TRACKER;
+            return;
+        }
+        mCollectingTransition.mReadyTracker.add(condition);
+    }
+
     interface OnStartCollect {
         void onCollectStarted(boolean deferred);
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4a0f44b..8f884d2f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2706,6 +2706,10 @@
         return mLastLayer;
     }
 
+    SurfaceControl getLastRelativeLayer() {
+        return mLastRelativeToLayer;
+    }
+
     protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
         if (mSurfaceFreezer.hasLeash()) {
             // When the freezer has created animation leash parent for the window, set the layer
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5ed6caf..a8b9417 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -37,6 +37,7 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
 import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE;
+import static android.window.WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT;
 import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN;
 import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER;
@@ -305,9 +306,12 @@
                     // This is a direct call from shell, so the entire transition lifecycle is
                     // contained in the provided transaction if provided. Thus, we can setReady
                     // immediately after apply.
+                    final Transition.ReadyCondition wctApplied =
+                            new Transition.ReadyCondition("start WCT applied");
                     final boolean needsSetReady = t != null;
                     final Transition nextTransition = new Transition(type, 0 /* flags */,
                             mTransitionController, mService.mWindowManager.mSyncEngine);
+                    nextTransition.mReadyTracker.add(wctApplied);
                     nextTransition.calcParallelCollectType(wct);
                     mTransitionController.startCollectOrQueue(nextTransition,
                             (deferred) -> {
@@ -315,6 +319,7 @@
                                 nextTransition.mLogger.mStartWCT = wct;
                                 applyTransaction(wct, -1 /* syncId */, nextTransition, caller,
                                         deferred);
+                                wctApplied.meet();
                                 if (needsSetReady) {
                                     // TODO(b/294925498): Remove this once we have accurate ready
                                     //                    tracking.
@@ -329,6 +334,15 @@
                             });
                     return nextTransition.getToken();
                 }
+                // Currently, application of wct can span multiple looper loops (ie.
+                // waitAsyncStart), so add a condition to ensure that it finishes applying.
+                final Transition.ReadyCondition wctApplied;
+                if (t != null) {
+                    wctApplied = new Transition.ReadyCondition("start WCT applied");
+                    transition.mReadyTracker.add(wctApplied);
+                } else {
+                    wctApplied = null;
+                }
                 // The transition already started collecting before sending a request to shell,
                 // so just start here.
                 if (!transition.isCollecting() && !transition.isForcePlaying()) {
@@ -336,6 +350,9 @@
                             + " means Shell took too long to respond to a request. WM State may be"
                             + " incorrect now, please file a bug");
                     applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
+                    if (wctApplied != null) {
+                        wctApplied.meet();
+                    }
                     return transition.getToken();
                 }
                 transition.mLogger.mStartWCT = wct;
@@ -344,11 +361,17 @@
                         synchronized (mService.mGlobalLock) {
                             transition.start();
                             applyTransaction(wct, -1 /* syncId */, transition, caller);
+                            if (wctApplied != null) {
+                                wctApplied.meet();
+                            }
                         }
                     });
                 } else {
                     transition.start();
                     applyTransaction(wct, -1 /* syncId */, transition, caller);
+                    if (wctApplied != null) {
+                        wctApplied.meet();
+                    }
                 }
                 // Since the transition is already provided, it means WMCore is determining the
                 // "readiness lifecycle" outside the provided transaction, so don't set ready here.
@@ -746,8 +769,7 @@
             }
         }
 
-        if ((c.getChangeMask()
-                & WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT) != 0) {
+        if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) {
             tr.setForceTranslucent(c.getForceTranslucent());
             effects = TRANSACT_EFFECTS_LIFECYCLE;
         }
@@ -855,6 +877,11 @@
                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
             }
         }
+        if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) {
+            taskFragment.setForceTranslucent(c.getForceTranslucent());
+            effects = TRANSACT_EFFECTS_LIFECYCLE;
+        }
+
         effects |= applyChanges(taskFragment, c);
 
         if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
@@ -1159,9 +1186,13 @@
             }
             case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: {
                 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
-                if (container == null || container.asDisplayArea() == null
-                        || !container.isAttached()) {
-                    Slog.e(TAG, "Attempt to operate on unknown or detached display area: "
+                if (container == null || !container.isAttached()) {
+                    Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+                            + container);
+                    break;
+                }
+                if (container.asTask() == null && container.asDisplayArea() == null) {
+                    Slog.e(TAG, "Cannot set always-on-top on non-task or non-display area: "
                             + container);
                     break;
                 }
@@ -1996,9 +2027,11 @@
 
         if (mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
             // System organizer is allowed to update the hidden and focusable state.
-            // We unset the CHANGE_HIDDEN and CHANGE_FOCUSABLE bits because they are checked here.
+            // We unset the CHANGE_HIDDEN, CHANGE_FOCUSABLE, and CHANGE_FORCE_TRANSLUCENT bits
+            // because they are checked here.
             changeMaskToBeChecked &= ~CHANGE_HIDDEN;
             changeMaskToBeChecked &= ~CHANGE_FOCUSABLE;
+            changeMaskToBeChecked &= ~CHANGE_FORCE_TRANSLUCENT;
         }
 
         // setRelativeBounds is allowed.
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index e769a27..a74a707 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -42,7 +42,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
 import static com.android.server.wm.WindowManagerService.MY_PID;
 
 import static java.util.Objects.requireNonNull;
@@ -631,22 +630,23 @@
      */
     @HotPath(caller = HotPath.START_SERVICE)
     public boolean areBackgroundFgsStartsAllowed() {
-        return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(),
-                true /* isCheckingForFgsStart */) != BAL_BLOCK;
+        return areBackgroundActivityStartsAllowed(
+                mAtm.getBalAppSwitchesState(),
+                true /* isCheckingForFgsStart */).allows();
     }
 
-    @BackgroundActivityStartController.BalCode
-    int areBackgroundActivityStartsAllowed(int appSwitchState) {
-        return areBackgroundActivityStartsAllowed(appSwitchState,
+    BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
+            int appSwitchState) {
+        return areBackgroundActivityStartsAllowed(
+                appSwitchState,
                 false /* isCheckingForFgsStart */);
     }
 
-    @BackgroundActivityStartController.BalCode
-    private int areBackgroundActivityStartsAllowed(int appSwitchState,
-            boolean isCheckingForFgsStart) {
-        return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
-                appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
-                mInstrumentingWithBackgroundActivityStartPrivileges,
+    private BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
+            int appSwitchState, boolean isCheckingForFgsStart) {
+        return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid,
+                mInfo.packageName, appSwitchState, isCheckingForFgsStart,
+                hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges,
                 mAtm.getLastStopAppSwitchesTime(),
                 mLastActivityLaunchTime, mLastActivityFinishTime);
     }
diff --git a/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp b/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
index a6084ea..cac13eb 100644
--- a/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
+++ b/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
@@ -34,9 +34,12 @@
 
 static constexpr uint64_t NSEC_PER_MSEC = 1000000;
 
-static int extractUidStats(JNIEnv *env, std::vector<std::vector<uint64_t>> &times,
-                           ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
-                           jlongArray tempForUidStats);
+static int flatten(JNIEnv *env, const std::vector<std::vector<uint64_t>> &times,
+                   jlongArray outArray);
+
+static int combineByBracket(JNIEnv *env, const std::vector<std::vector<uint64_t>> &times,
+                            ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
+                            jlongArray outBrackets);
 
 static bool initialized = false;
 static jclass class_KernelCpuStatsCallback;
@@ -62,25 +65,43 @@
     return OK;
 }
 
+static jboolean nativeIsSupportedFeature(JNIEnv *env) {
+    if (!android::bpf::startTrackingUidTimes()) {
+        return false;
+    }
+    auto totalByScalingStep = android::bpf::getTotalCpuFreqTimes();
+    return totalByScalingStep.has_value();
+}
+
 static jlong nativeReadCpuStats(JNIEnv *env, [[maybe_unused]] jobject zis, jobject callback,
                                 jintArray scalingStepToPowerBracketMap,
-                                jlong lastUpdateTimestampNanos, jlongArray tempForUidStats) {
+                                jlong lastUpdateTimestampNanos, jlongArray cpuTimeByScalingStep,
+                                jlongArray tempForUidStats) {
+    ScopedIntArrayRO scopedScalingStepToPowerBracketMap(env, scalingStepToPowerBracketMap);
+
     if (!initialized) {
         if (init(env) == EXCEPTION) {
             return 0L;
         }
     }
 
+    auto totalByScalingStep = android::bpf::getTotalCpuFreqTimes();
+    if (!totalByScalingStep) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Unsupported kernel feature");
+        return EXCEPTION;
+    }
+
+    if (flatten(env, *totalByScalingStep, cpuTimeByScalingStep) == EXCEPTION) {
+        return 0L;
+    }
+
     uint64_t newLastUpdate = lastUpdateTimestampNanos;
     auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
     if (!data.has_value()) return lastUpdateTimestampNanos;
 
-    ScopedIntArrayRO scopedScalingStepToPowerBracketMap(env, scalingStepToPowerBracketMap);
-
     for (auto &[uid, times] : *data) {
-        int status =
-                extractUidStats(env, times, scopedScalingStepToPowerBracketMap, tempForUidStats);
-        if (status == EXCEPTION) {
+        if (combineByBracket(env, times, scopedScalingStepToPowerBracketMap, tempForUidStats) ==
+            EXCEPTION) {
             return 0L;
         }
         env->CallVoidMethod(callback, method_KernelCpuStatsCallback_processUidStats, (jint)uid,
@@ -89,13 +110,34 @@
     return newLastUpdate;
 }
 
-static int extractUidStats(JNIEnv *env, std::vector<std::vector<uint64_t>> &times,
-                           ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
-                           jlongArray tempForUidStats) {
-    ScopedLongArrayRW scopedTempForStats(env, tempForUidStats);
-    uint64_t *arrayForStats = reinterpret_cast<uint64_t *>(scopedTempForStats.get());
-    const uint8_t statsSize = scopedTempForStats.size();
-    memset(arrayForStats, 0, statsSize * sizeof(uint64_t));
+static int flatten(JNIEnv *env, const std::vector<std::vector<uint64_t>> &times,
+                   jlongArray outArray) {
+    ScopedLongArrayRW scopedOutArray(env, outArray);
+    const uint8_t scalingStepCount = scopedOutArray.size();
+    uint64_t *out = reinterpret_cast<uint64_t *>(scopedOutArray.get());
+    uint32_t scalingStep = 0;
+    for (const auto &subVec : times) {
+        for (uint32_t i = 0; i < subVec.size(); ++i) {
+            if (scalingStep >= scalingStepCount) {
+                jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+                                     "Array is too short, size=%u, scalingStep=%u",
+                                     scalingStepCount, scalingStep);
+                return EXCEPTION;
+            }
+            out[scalingStep] = subVec[i] / NSEC_PER_MSEC;
+            scalingStep++;
+        }
+    }
+    return OK;
+}
+
+static int combineByBracket(JNIEnv *env, const std::vector<std::vector<uint64_t>> &times,
+                            ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
+                            jlongArray outBrackets) {
+    ScopedLongArrayRW scopedOutBrackets(env, outBrackets);
+    uint64_t *brackets = reinterpret_cast<uint64_t *>(scopedOutBrackets.get());
+    const uint8_t statsSize = scopedOutBrackets.size();
+    memset(brackets, 0, statsSize * sizeof(uint64_t));
     const uint8_t scalingStepCount = scopedScalingStepToPowerBracketMap.size();
 
     uint32_t scalingStep = 0;
@@ -108,14 +150,14 @@
                                      scalingStepCount, scalingStep);
                 return EXCEPTION;
             }
-            uint32_t bucket = scopedScalingStepToPowerBracketMap[scalingStep];
-            if (bucket >= statsSize) {
+            uint32_t bracket = scopedScalingStepToPowerBracketMap[scalingStep];
+            if (bracket >= statsSize) {
                 jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
-                                     "UidStats array is too short, length=%u, bucket[%u]=%u",
-                                     statsSize, scalingStep, bucket);
+                                     "Bracket array is too short, length=%u, bracket[%u]=%u",
+                                     statsSize, scalingStep, bracket);
                 return EXCEPTION;
             }
-            arrayForStats[bucket] += subVec[i] / NSEC_PER_MSEC;
+            brackets[bracket] += subVec[i] / NSEC_PER_MSEC;
             scalingStep++;
         }
     }
@@ -123,7 +165,8 @@
 }
 
 static const JNINativeMethod method_table[] = {
-        {"nativeReadCpuStats", "(L" JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK ";[IJ[J)J",
+        {"nativeIsSupportedFeature", "()Z", (void *)nativeIsSupportedFeature},
+        {"nativeReadCpuStats", "(L" JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK ";[IJ[J[J)J",
          (void *)nativeReadCpuStats},
 };
 
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 41706f0..8dfa685 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -23,8 +23,6 @@
         "services.core",
         "app-compat-annotations",
         "service-permission.stubs.system_server",
-    ],
-    static_libs: [
         "device_policy_aconfig_flags_lib",
     ],
 }
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
index ca57f51..8b5d7f0 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -31,6 +31,7 @@
 import android.app.smartspace.SmartspaceConfig;
 import android.app.smartspace.SmartspaceSessionId;
 import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.flags.Flags;
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
@@ -165,7 +166,8 @@
             }
             Context ctx = getContext();
             if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED
-                    || ctx.checkCallingPermission(ACCESS_SMARTSPACE) == PERMISSION_GRANTED
+                    || (Flags.accessSmartspace()
+                    && ctx.checkCallingPermission(ACCESS_SMARTSPACE) == PERMISSION_GRANTED)
                     || mServiceNameResolver.isTemporary(userId)
                     || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index bded9b4..809a0e8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -919,6 +919,7 @@
             assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
             mSetFlagsRule.disableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER);
             when(mService.isBatteryCharging()).thenReturn(false);
+            when(mService.isBatteryNotLow()).thenReturn(false);
 
             when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
                     any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
@@ -938,6 +939,12 @@
             assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
 
             when(mService.isBatteryCharging()).thenReturn(true);
+            assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+            // Only relax restrictions when we at least know the estimated download bytes.
+            assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+            when(mService.isBatteryNotLow()).thenReturn(true);
             assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
             // Only relax restrictions when we at least know the estimated download bytes.
             assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 7a6ac4e..e7f1d16e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -100,6 +101,8 @@
     @Mock
     private LauncherApps mLauncherApps;
     @Mock
+    private ActivityManager mActivityManager;
+    @Mock
     private PackageManager mPackageManager;
     @Mock
     private PackageInstallerService mInstallerService;
@@ -159,6 +162,10 @@
         when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
         when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
                 mLauncherActivityInfos);
+
+        when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
+        when(mActivityManager.getLauncherLargeIconDensity()).thenReturn(100);
+
         doReturn(mComputer).when(mPackageManagerService).snapshotComputer();
         when(mComputer.getPackageUid(eq(CALLER_PACKAGE), eq(0L), eq(mUserId))).thenReturn(
                 Binder.getCallingUid());
@@ -172,7 +179,7 @@
 
         mArchiveManager = spy(new PackageArchiver(mContext, mPackageManagerService));
         doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
-                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+                any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
         doReturn(mIcon).when(mArchiveManager).decodeIcon(
                 any(ArchiveState.ArchiveActivityInfo.class));
     }
@@ -277,7 +284,7 @@
     public void archiveApp_storeIconFails() throws IntentSender.SendIntentException, IOException {
         IOException e = new IOException("IO");
         doThrow(e).when(mArchiveManager).storeIcon(eq(PACKAGE),
-                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+                any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
 
         mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
         rule.mocks().getHandler().flush();
diff --git a/services/tests/powerstatstests/res/xml/power_profile_test.xml b/services/tests/powerstatstests/res/xml/power_profile_test.xml
new file mode 100644
index 0000000..ecd8861
--- /dev/null
+++ b/services/tests/powerstatstests/res/xml/power_profile_test.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<device name="Android">
+    <!-- This is the battery capacity in mAh -->
+    <item name="battery.capacity">3000</item>
+
+    <!-- Power consumption when CPU is suspended -->
+    <item name="cpu.suspend">5</item>
+    <!-- Additional power consumption when CPU is in a kernel idle loop -->
+    <item name="cpu.idle">1.11</item>
+    <!-- Additional power consumption by CPU excluding cluster and core when  running -->
+    <item name="cpu.active">2.55</item>
+
+    <!-- Additional power consumption of CPU policy0 itself when running on related cores -->
+    <item name="cpu.scaling_policy_power.policy0">2.11</item>
+    <!-- Additional power consumption of CPU policy4 itself when running on related cores -->
+    <item name="cpu.scaling_policy_power.policy4">2.22</item>
+
+    <!-- Additional power used by a CPU related to policy3 when running at different
+     speeds. -->
+    <array name="cpu.scaling_step_power.policy0">
+        <value>10</value> <!-- 300 MHz CPU speed -->
+        <value>20</value> <!-- 1000 MHz CPU speed -->
+        <value>30</value> <!-- 1900 MHz CPU speed -->
+    </array>
+    <!-- Additional power used by a CPU related to policy3 when running at different
+         speeds. -->
+    <array name="cpu.scaling_step_power.policy4">
+        <value>25</value> <!-- 300 MHz CPU speed -->
+        <value>35</value> <!-- 1000 MHz CPU speed -->
+        <value>50</value> <!-- 2500 MHz CPU speed -->
+        <value>60</value> <!-- 3000 MHz CPU speed -->
+    </array>
+
+    <!-- Power used by display unit in ambient display mode, including back lighting-->
+    <item name="ambient.on">0.5</item>
+    <!-- Additional power used when screen is turned on at minimum brightness -->
+    <item name="screen.on">100</item>
+    <!-- Additional power used when screen is at maximum brightness, compared to
+         screen at minimum brightness -->
+    <item name="screen.full">800</item>
+
+    <!-- Average power used by the camera flash module when on -->
+    <item name="camera.flashlight">500</item>
+    <!-- Average power use by the camera subsystem for a typical camera
+         application. Intended as a rough estimate for an application running a
+         preview and capturing approximately 10 full-resolution pictures per
+         minute. -->
+    <item name="camera.avg">600</item>
+
+    <!-- Additional power used by the audio hardware, probably due to DSP -->
+    <item name="audio">100.0</item>
+
+    <!-- Additional power used by the video hardware, probably due to DSP -->
+    <item name="video">150.0</item> <!-- ~50mA -->
+
+    <!-- Additional power used when GPS is acquiring a signal -->
+    <item name="gps.on">10</item>
+
+    <!-- Additional power used when cellular radio is transmitting/receiving -->
+    <item name="radio.active">60</item>
+    <!-- Additional power used when cellular radio is paging the tower -->
+    <item name="radio.scanning">3</item>
+    <!-- Additional power used when the cellular radio is on. Multi-value entry,
+         one per signal strength (no signal, weak, moderate, strong) -->
+    <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+        <value>6</value>       <!-- none -->
+        <value>5</value>       <!-- poor -->
+        <value>4</value>       <!-- moderate -->
+        <value>3</value>       <!-- good -->
+        <value>3</value>       <!-- great -->
+    </array>
+
+    <!-- Cellular modem related values. These constants are deprecated, but still supported and
+         need to be tested -->
+    <item name="modem.controller.sleep">123</item>
+    <item name="modem.controller.idle">456</item>
+    <item name="modem.controller.rx">789</item>
+    <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+        <value>10</value>
+        <value>20</value>
+        <value>30</value>
+        <value>40</value>
+        <value>50</value>
+    </array>
+</device>
\ No newline at end of file
diff --git a/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml b/services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml
similarity index 100%
rename from core/tests/coretests/res/xml/power_profile_test_power_brackets.xml
rename to services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
new file mode 100644
index 0000000..48e2dd7
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 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.power.stats;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.MultiStateStats;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AggregatePowerStatsProcessorTest {
+
+    @Test
+    public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
+        AggregatedPowerStatsConfig.PowerComponent config =
+                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
+
+        AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
+                new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+        assertThat(deviceStateEstimatesToStrings(plan))
+                .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
+        assertThat(combinedDeviceStatsToStrings(plan))
+                .containsExactly("[[0, 0]]", "[[0, 1]]", "[[1, 0]]", "[[1, 1]]");
+        assertThat(uidStateEstimatesToStrings(plan, config))
+                .containsExactly(
+                        "[[0, 0]]: [ps]: [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4]]",
+                        "[[0, 1]]: [ps]: [[0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4]]",
+                        "[[1, 0]]: [ps]: [[1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 0, 3], [1, 0, 4]]",
+                        "[[1, 1]]: [ps]: [[1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]");
+    }
+
+    @Test
+    public void createPowerEstimationPlan_combineDeviceStats() {
+        AggregatedPowerStatsConfig.PowerComponent config =
+                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_PROCESS_STATE);
+
+        AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
+                new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+
+        assertThat(deviceStateEstimatesToStrings(plan))
+                .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
+        assertThat(combinedDeviceStatsToStrings(plan))
+                .containsExactly(
+                        "[[0, 0], [0, 1]]",
+                        "[[1, 0], [1, 1]]");
+        assertThat(uidStateEstimatesToStrings(plan, config))
+                .containsExactly(
+                        "[[0, 0], [0, 1]]: [ps]: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]]",
+                        "[[1, 0], [1, 1]]: [ps]: [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]");
+    }
+
+    private static List<String> deviceStateEstimatesToStrings(
+            AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+        return plan.deviceStateEstimations.stream()
+                .map(dse -> dse.stateValues).map(Arrays::toString).toList();
+    }
+
+    private static List<String> combinedDeviceStatsToStrings(
+            AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+        return plan.combinedDeviceStateEstimations.stream()
+                .map(cds -> cds.deviceStateEstimations)
+                .map(dses -> dses.stream()
+                        .map(dse -> dse.stateValues).map(Arrays::toString).toList())
+                .map(Object::toString)
+                .toList();
+    }
+
+    private static List<String> uidStateEstimatesToStrings(
+            AggregatedPowerStatsProcessor.PowerEstimationPlan plan,
+            AggregatedPowerStatsConfig.PowerComponent config) {
+        MultiStateStats.States[] uidStateConfig = config.getUidStateConfig();
+        return plan.uidStateEstimates.stream()
+                .map(use ->
+                        use.combinedDeviceStateEstimate.deviceStateEstimations.stream()
+                                .map(dse -> dse.stateValues).map(Arrays::toString).toList()
+                        + ": "
+                        + Arrays.stream(use.states)
+                                .filter(Objects::nonNull)
+                                .map(MultiStateStats.States::getName).toList()
+                        + ": "
+                        + use.proportionalEstimates.stream()
+                                .map(pe -> trackedStatesToString(uidStateConfig, pe.stateValues))
+                                .toList())
+                .toList();
+    }
+
+    private static Object trackedStatesToString(MultiStateStats.States[] states,
+            int[] stateValues) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        boolean first = true;
+        for (int i = 0; i < states.length; i++) {
+            if (!states[i].isTracked()) {
+                continue;
+            }
+
+            if (!first) {
+                sb.append(", ");
+            }
+            first = false;
+            sb.append(stateValues[i]);
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 3579fce..0b10954 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -142,6 +142,22 @@
         return this;
     }
 
+    /**
+     * Mocks the CPU bracket count
+     */
+    public BatteryUsageStatsRule setCpuPowerBracketCount(int count) {
+        when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(count);
+        return this;
+    }
+
+    /**
+     * Mocks the CPU bracket for the given CPU scaling policy and step
+     */
+    public BatteryUsageStatsRule setCpuPowerBracket(int policy, int step, int bracket) {
+        when(mPowerProfile.getCpuPowerBracketForScalingStep(policy, step)).thenReturn(bracket);
+        return this;
+    }
+
     public BatteryUsageStatsRule setAveragePowerForOrdinal(String group, int ordinal,
             double value) {
         when(mPowerProfile.getAveragePowerForOrdinal(group, ordinal)).thenReturn(value);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
new file mode 100644
index 0000000..79084cc
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2023 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.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+import android.util.LongArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CpuAggregatedPowerStatsProcessorTest {
+    @Rule
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+            .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
+            .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300})
+            .setAveragePowerForCpuScalingPolicy(0, 360)
+            .setAveragePowerForCpuScalingPolicy(2, 480)
+            .setAveragePowerForCpuScalingStep(0, 0, 300)
+            .setAveragePowerForCpuScalingStep(0, 1, 400)
+            .setAveragePowerForCpuScalingStep(2, 0, 500)
+            .setCpuPowerBracketCount(3)
+            .setCpuPowerBracket(0, 0, 0)
+            .setCpuPowerBracket(0, 1, 1)
+            .setCpuPowerBracket(2, 0, 2);
+
+    private AggregatedPowerStatsConfig.PowerComponent mConfig;
+    private CpuAggregatedPowerStatsProcessor mProcessor;
+    private MockPowerComponentAggregatedPowerStats mStats;
+
+    @Before
+    public void setup() {
+        mConfig = new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
+
+        mProcessor = new CpuAggregatedPowerStatsProcessor(
+                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies());
+    }
+
+    @Test
+    public void powerProfileModel() {
+        mStats = new MockPowerComponentAggregatedPowerStats(mConfig, false);
+        mStats.setDeviceStats(
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
+                concat(
+                        values(3500, 4500, 3000),   // scaling steps
+                        values(2000, 1000),         // clusters
+                        values(5000)),              // uptime
+                3.113732);
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON),
+                concat(
+                        values(6000, 6500, 4000),
+                        values(5000, 3000),
+                        values(7000)),
+                4.607245);
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
+                concat(
+                        values(9000, 10000, 7000),
+                        values(8000, 6000),
+                        values(20000)),
+                7.331799);
+        mStats.setUidStats(24,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(400, 1500, 2000),  1.206947);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(900, 1000, 1500), 1.016182);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
+                values(600, 500, 300), 0.385042);
+        mStats.setUidStats(42,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
+                values(1500, 2000, 1000), 1.252578);
+
+        mProcessor.finish(mStats);
+
+        mStats.verifyPowerEstimates();
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        mStats = new MockPowerComponentAggregatedPowerStats(mConfig, true);
+        mStats.setDeviceStats(
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
+                concat(
+                        values(3500, 4500, 3000),           // scaling steps
+                        values(2000, 1000),                 // clusters
+                        values(5000),                       // uptime
+                        values(5_000_000L, 6_000_000L)),    // energy, uC
+                3.055555);
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON),
+                concat(
+                        values(6000, 6500, 4000),
+                        values(5000, 3000),
+                        values(7000),
+                        values(5_000_000L, 6_000_000L)),    // same as above
+                3.055555);                                  // same as above - WAI
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
+                concat(
+                        values(9000, 10000, 7000),
+                        values(8000, 6000),
+                        values(20000),
+                        values(8_000_000L, 18_000_000L)),
+                7.222222);
+        mStats.setUidStats(24,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(400, 1500, 2000),  1.449078);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(900, 1000, 1500), 1.161902);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
+                values(600, 500, 300), 0.355406);
+        mStats.setUidStats(42,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
+                values(1500, 2000, 1000), 0.80773);
+
+        mProcessor.finish(mStats);
+
+        mStats.verifyPowerEstimates();
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private long[] values(long... values) {
+        return values;
+    }
+
+    private long[] concat(long[]... arrays) {
+        LongArray all = new LongArray();
+        for (long[] array : arrays) {
+            for (long value : array) {
+                all.add(value);
+            }
+        }
+        return all.toArray();
+    }
+
+    private static class MockPowerComponentAggregatedPowerStats extends
+            PowerComponentAggregatedPowerStats {
+        private final CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+        private final PowerStats.Descriptor mDescriptor;
+        private HashMap<String, long[]> mDeviceStats = new HashMap<>();
+        private HashMap<String, long[]> mUidStats = new HashMap<>();
+        private HashSet<Integer> mUids = new HashSet<>();
+        private HashMap<String, Double> mExpectedDevicePower = new HashMap<>();
+        private HashMap<String, Double> mExpectedUidPower = new HashMap<>();
+
+        MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
+                boolean useEnergyConsumers) {
+            super(config);
+            mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+            mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
+            mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
+            mStatsLayout.addDeviceSectionUptime();
+            if (useEnergyConsumers) {
+                mStatsLayout.addDeviceSectionEnergyConsumers(2);
+            }
+            mStatsLayout.addDeviceSectionPowerEstimate();
+            mStatsLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0, 1, 2});
+            mStatsLayout.addUidSectionPowerEstimate();
+
+            PersistableBundle extras = new PersistableBundle();
+            mStatsLayout.toExtras(extras);
+            mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+                    mStatsLayout.getDeviceStatsArrayLength(), mStatsLayout.getUidStatsArrayLength(),
+                    extras);
+        }
+
+        @Override
+        public PowerStats.Descriptor getPowerStatsDescriptor() {
+            return mDescriptor;
+        }
+
+        @Override
+        boolean getDeviceStats(long[] outValues, int[] deviceStates) {
+            long[] values = getDeviceStats(deviceStates);
+            System.arraycopy(values, 0, outValues, 0, values.length);
+            return true;
+        }
+
+        private long[] getDeviceStats(int[] deviceStates) {
+            String key = statesToString(getConfig().getDeviceStateConfig(), deviceStates);
+            long[] values = mDeviceStats.get(key);
+            return values == null ? new long[mDescriptor.statsArrayLength] : values;
+        }
+
+        void setDeviceStats(int[] states, long[] values, double expectedPowerEstimate) {
+            setDeviceStats(states, values);
+            mExpectedDevicePower.put(statesToString(getConfig().getDeviceStateConfig(), states),
+                    expectedPowerEstimate);
+        }
+
+        @Override
+        void setDeviceStats(int[] states, long[] values) {
+            String key = statesToString(getConfig().getDeviceStateConfig(), states);
+            mDeviceStats.put(key, Arrays.copyOf(values, mDescriptor.statsArrayLength));
+        }
+
+        @Override
+        boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
+            long[] values = getUidStats(uid, uidStates);
+            assertThat(values).isNotNull();
+            System.arraycopy(values, 0, outValues, 0, values.length);
+            return true;
+        }
+
+        private long[] getUidStats(int uid, int[] uidStates) {
+            String key = uid + " " + statesToString(getConfig().getUidStateConfig(), uidStates);
+            long[] values = mUidStats.get(key);
+            return values == null ? new long[mDescriptor.uidStatsArrayLength] : values;
+        }
+
+        void setUidStats(int uid, int[] states, long[] values, double expectedPowerEstimate) {
+            setUidStats(uid, states, values);
+            mExpectedUidPower.put(
+                    uid + " " + statesToString(getConfig().getUidStateConfig(), states),
+                    expectedPowerEstimate);
+        }
+
+        @Override
+        void setUidStats(int uid, int[] states, long[] values) {
+            mUids.add(uid);
+            String key = uid + " " + statesToString(getConfig().getUidStateConfig(), states);
+            mUidStats.put(key, Arrays.copyOf(values, mDescriptor.uidStatsArrayLength));
+        }
+
+        @Override
+        void collectUids(Collection<Integer> uids) {
+            uids.addAll(mUids);
+        }
+
+        void verifyPowerEstimates() {
+            StringBuilder mismatches = new StringBuilder();
+            for (Map.Entry<String, Double> entry : mExpectedDevicePower.entrySet()) {
+                String key = entry.getKey();
+                double expected = mExpectedDevicePower.get(key);
+                double actual = mStatsLayout.getDevicePowerEstimate(mDeviceStats.get(key));
+                if (Math.abs(expected - actual) > 0.005) {
+                    mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
+                }
+            }
+            for (Map.Entry<String, Double> entry : mExpectedUidPower.entrySet()) {
+                String key = entry.getKey();
+                double expected = mExpectedUidPower.get(key);
+                double actual = mStatsLayout.getUidPowerEstimate(mUidStats.get(key));
+                if (Math.abs(expected - actual) > 0.005) {
+                    mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
+                }
+            }
+            if (!mismatches.isEmpty()) {
+                fail("Unexpected power estimations:\n" + mismatches);
+            }
+        }
+
+        private String statesToString(MultiStateStats.States[] config, int[] states) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < states.length; i++) {
+                sb.append(config[i].getName()).append("=").append(states[i]).append(" ");
+            }
+            return sb.toString();
+        }
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index f2ee6db..bc211df 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -20,16 +20,26 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.power.PowerStatsInternal;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.frameworks.powerstatstests.R;
 import com.android.internal.os.CpuScalingPolicies;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.PowerStats;
@@ -40,85 +50,266 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CpuPowerStatsCollectorTest {
+    private Context mContext;
     private final MockClock mMockClock = new MockClock();
     private final HandlerThread mHandlerThread = new HandlerThread("test");
     private Handler mHandler;
-    private CpuPowerStatsCollector mCollector;
     private PowerStats mCollectedStats;
-    @Mock
     private PowerProfile mPowerProfile;
     @Mock
     private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
+    @Mock
+    private PowerStatsInternal mPowerStatsInternal;
+    private CpuScalingPolicies mCpuScalingPolicies;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getContext();
 
         mHandlerThread.start();
         mHandler = mHandlerThread.getThreadHandler();
-        when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2);
-        when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0);
-        when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1);
-        mCollector = new CpuPowerStatsCollector(new CpuScalingPolicies(
-                new SparseArray<>() {{
-                    put(0, new int[]{0});
-                }},
-                new SparseArray<>() {{
-                    put(0, new int[]{1, 12});
-                }}),
-                mPowerProfile, mHandler, mMockKernelCpuStatsReader, 60_000, mMockClock);
-        mCollector.addConsumer(stats -> mCollectedStats = stats);
-        mCollector.setEnabled(true);
+        when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true);
     }
 
     @Test
-    public void collectStats() {
-        mockKernelCpuStats(new SparseArray<>() {{
-                put(42, new long[]{100, 200});
-                put(99, new long[]{300, 600});
-            }}, 0, 1234);
+    public void powerBrackets_specifiedInPowerProfile() {
+        mPowerProfile = new PowerProfile(mContext);
+        mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
+        mCpuScalingPolicies = new CpuScalingPolicies(
+                new SparseArray<>() {{
+                    put(0, new int[]{0});
+                    put(4, new int[]{4});
+                }},
+                new SparseArray<>() {{
+                    put(0, new int[]{100});
+                    put(4, new int[]{400, 500});
+                }});
+
+        CpuPowerStatsCollector collector = createCollector(8, 0);
+
+        assertThat(getScalingStepToPowerBracketMap(collector))
+                .isEqualTo(new int[]{1, 1, 0});
+    }
+
+    @Test
+    public void powerBrackets_default_noEnergyConsumers() {
+        mPowerProfile = new PowerProfile(mContext);
+        mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+        mockCpuScalingPolicies(2);
+
+        CpuPowerStatsCollector collector = createCollector(3, 0);
+
+        assertThat(new String[]{
+                collector.getCpuPowerBracketDescription(0),
+                collector.getCpuPowerBracketDescription(1),
+                collector.getCpuPowerBracketDescription(2)})
+                .isEqualTo(new String[]{
+                        "0/300(10.0)",
+                        "0/1000(20.0), 0/2000(30.0), 4/300(25.0)",
+                        "4/1000(35.0), 4/2500(50.0), 4/3000(60.0)"});
+        assertThat(getScalingStepToPowerBracketMap(collector))
+                .isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2});
+    }
+
+    @Test
+    public void powerBrackets_moreBracketsThanStates() {
+        mPowerProfile = new PowerProfile(mContext);
+        mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+        mockCpuScalingPolicies(2);
+
+        CpuPowerStatsCollector collector = createCollector(8, 0);
+
+        assertThat(getScalingStepToPowerBracketMap(collector))
+                .isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6});
+    }
+
+    @Test
+    public void powerBrackets_energyConsumers() throws Exception {
+        mPowerProfile = new PowerProfile(mContext);
+        mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+        mockCpuScalingPolicies(2);
+        mockEnergyConsumers();
+
+        CpuPowerStatsCollector collector = createCollector(8, 2);
+
+        assertThat(getScalingStepToPowerBracketMap(collector))
+                .isEqualTo(new int[]{0, 1, 1, 2, 2, 3, 3});
+    }
+
+    @Test
+    public void powerStatsDescriptor() throws Exception {
+        mPowerProfile = new PowerProfile(mContext);
+        mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+        mockCpuScalingPolicies(2);
+        mockEnergyConsumers();
+
+        CpuPowerStatsCollector collector = createCollector(8, 2);
+        PowerStats.Descriptor descriptor = collector.getPowerStatsDescriptor();
+        assertThat(descriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
+        assertThat(descriptor.name).isEqualTo("cpu");
+        assertThat(descriptor.statsArrayLength).isEqualTo(13);
+        assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
+        CpuPowerStatsCollector.StatsArrayLayout layout =
+                new CpuPowerStatsCollector.StatsArrayLayout();
+        layout.fromExtras(descriptor.extras);
+
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        layout.setTimeByScalingStep(deviceStats, 2, 42);
+        layout.setConsumedEnergy(deviceStats, 1, 43);
+        layout.setUptime(deviceStats, 44);
+        layout.setDevicePowerEstimate(deviceStats, 45);
+
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        layout.setUidTimeByPowerBracket(uidStats, 3, 46);
+        layout.setUidPowerEstimate(uidStats, 47);
+
+        assertThat(layout.getCpuScalingStepCount()).isEqualTo(7);
+        assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42);
+
+        assertThat(layout.getCpuClusterEnergyConsumerCount()).isEqualTo(2);
+        assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43);
+
+        assertThat(layout.getUptime(deviceStats)).isEqualTo(44);
+
+        assertThat(layout.getDevicePowerEstimate(deviceStats)).isEqualTo(45);
+
+        assertThat(layout.getScalingStepToPowerBracketMap()).isEqualTo(
+                new int[]{0, 1, 1, 2, 2, 3, 3});
+        assertThat(layout.getCpuPowerBracketCount()).isEqualTo(4);
+
+        assertThat(layout.getUidTimeByPowerBracket(uidStats, 3)).isEqualTo(46);
+        assertThat(layout.getUidPowerEstimate(uidStats)).isEqualTo(47);
+    }
+
+    @Test
+    public void collectStats() throws Exception {
+        mockCpuScalingPolicies(1);
+        mockPowerProfile();
+        mockEnergyConsumers();
+
+        CpuPowerStatsCollector collector = createCollector(8, 0);
+        CpuPowerStatsCollector.StatsArrayLayout layout =
+                new CpuPowerStatsCollector.StatsArrayLayout();
+        layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+
+        mockKernelCpuStats(new long[]{1111, 2222, 3333},
+                new SparseArray<>() {{
+                    put(42, new long[]{100, 200});
+                    put(99, new long[]{300, 600});
+                }}, 0, 1234);
 
         mMockClock.uptime = 1000;
-        mCollector.forceSchedule();
+        collector.forceSchedule();
         waitForIdle();
 
         assertThat(mCollectedStats.durationMs).isEqualTo(1234);
-        assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{100, 200});
-        assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{300, 600});
 
-        mockKernelCpuStats(new SparseArray<>() {{
-                put(42, new long[]{123, 234});
-                put(99, new long[]{345, 678});
-            }}, 1234, 3421);
+        assertThat(layout.getCpuScalingStepCount()).isEqualTo(3);
+        assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 0)).isEqualTo(1111);
+        assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 1)).isEqualTo(2222);
+        assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 2)).isEqualTo(3333);
+
+        assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(0);
+        assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(0);
+
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+                .isEqualTo(100);
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+                .isEqualTo(200);
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+                .isEqualTo(300);
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+                .isEqualTo(600);
+
+        mockKernelCpuStats(new long[]{5555, 4444, 3333},
+                new SparseArray<>() {{
+                    put(42, new long[]{123, 234});
+                    put(99, new long[]{345, 678});
+                }}, 1234, 3421);
 
         mMockClock.uptime = 2000;
-        mCollector.forceSchedule();
+        collector.forceSchedule();
         waitForIdle();
 
         assertThat(mCollectedStats.durationMs).isEqualTo(3421 - 1234);
-        assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{23, 34});
-        assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{45, 78});
+
+        assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 0)).isEqualTo(4444);
+        assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 1)).isEqualTo(2222);
+        assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 2)).isEqualTo(0);
+
+        // 500 * 1000 / 3500
+        assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(143);
+        // 700 * 1000 / 3500
+        assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(200);
+
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+                .isEqualTo(23);
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+                .isEqualTo(34);
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+                .isEqualTo(45);
+        assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+                .isEqualTo(78);
     }
 
-    private void mockKernelCpuStats(SparseArray<long[]> uidToCpuStats,
+    private void mockCpuScalingPolicies(int clusterCount) {
+        SparseArray<int[]> cpus = new SparseArray<>();
+        SparseArray<int[]> freqs = new SparseArray<>();
+        cpus.put(0, new int[]{0, 1, 2, 3});
+        freqs.put(0, new int[]{300000, 1000000, 2000000});
+        if (clusterCount == 2) {
+            cpus.put(4, new int[]{4, 5});
+            freqs.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+        }
+        mCpuScalingPolicies = new CpuScalingPolicies(cpus, freqs);
+    }
+
+    private void mockPowerProfile() {
+        mPowerProfile = mock(PowerProfile.class);
+        when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2);
+        when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0);
+        when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1);
+        when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 2)).thenReturn(1);
+    }
+
+    private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
+            int defaultCpuPowerBracketsPerEnergyConsumer) {
+        CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies,
+                mPowerProfile, mHandler, mMockKernelCpuStatsReader, () -> mPowerStatsInternal,
+                () -> 3500, 60_000, mMockClock, defaultCpuPowerBrackets,
+                defaultCpuPowerBracketsPerEnergyConsumer);
+        collector.addConsumer(stats -> mCollectedStats = stats);
+        collector.setEnabled(true);
+        return collector;
+    }
+
+    private void mockKernelCpuStats(long[] deviceStats, SparseArray<long[]> uidToCpuStats,
             long expectedLastUpdateTimestampMs, long newLastUpdateTimestampMs) {
         when(mMockKernelCpuStatsReader.nativeReadCpuStats(
                 any(CpuPowerStatsCollector.KernelCpuStatsCallback.class),
-                any(int[].class), anyLong(), any(long[].class)))
+                any(int[].class), anyLong(), any(long[].class), any(long[].class)))
                 .thenAnswer(invocation -> {
                     CpuPowerStatsCollector.KernelCpuStatsCallback callback =
                             invocation.getArgument(0);
                     int[] powerBucketIndexes = invocation.getArgument(1);
                     long lastTimestamp = invocation.getArgument(2);
-                    long[] tempStats = invocation.getArgument(3);
+                    long[] cpuTimeByScalingStep = invocation.getArgument(3);
+                    long[] tempStats = invocation.getArgument(4);
 
-                    assertThat(powerBucketIndexes).isEqualTo(new int[]{0, 1});
+                    assertThat(powerBucketIndexes).isEqualTo(new int[]{0, 1, 1});
                     assertThat(lastTimestamp / 1000000L).isEqualTo(expectedLastUpdateTimestampMs);
                     assertThat(tempStats).hasLength(2);
 
+                    System.arraycopy(deviceStats, 0, cpuTimeByScalingStep, 0,
+                            cpuTimeByScalingStep.length);
+
                     for (int i = 0; i < uidToCpuStats.size(); i++) {
                         int uid = uidToCpuStats.keyAt(i);
                         long[] cpuStats = uidToCpuStats.valueAt(i);
@@ -129,6 +320,67 @@
                 });
     }
 
+    @SuppressWarnings("unchecked")
+    private void mockEnergyConsumers() throws Exception {
+        when(mPowerStatsInternal.getEnergyConsumerInfo())
+                .thenReturn(new EnergyConsumer[]{
+                        new EnergyConsumer() {{
+                            id = 1;
+                            type = EnergyConsumerType.CPU_CLUSTER;
+                            ordinal = 0;
+                            name = "CPU0";
+                        }},
+                        new EnergyConsumer() {{
+                            id = 2;
+                            type = EnergyConsumerType.CPU_CLUSTER;
+                            ordinal = 1;
+                            name = "CPU4";
+                        }},
+                        new EnergyConsumer() {{
+                            id = 3;
+                            type = EnergyConsumerType.BLUETOOTH;
+                            name = "BT";
+                        }},
+                });
+
+        CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class);
+        when(future1.get(anyLong(), any(TimeUnit.class)))
+                .thenReturn(new EnergyConsumerResult[]{
+                        new EnergyConsumerResult() {{
+                            id = 1;
+                            energyUWs = 1000;
+                        }},
+                        new EnergyConsumerResult() {{
+                            id = 2;
+                            energyUWs = 2000;
+                        }}
+                });
+
+        CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class);
+        when(future2.get(anyLong(), any(TimeUnit.class)))
+                .thenReturn(new EnergyConsumerResult[]{
+                        new EnergyConsumerResult() {{
+                            id = 1;
+                            energyUWs = 1500;
+                        }},
+                        new EnergyConsumerResult() {{
+                            id = 2;
+                            energyUWs = 2700;
+                        }}
+                });
+
+        when(mPowerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2})))
+                .thenReturn(future1)
+                .thenReturn(future2);
+    }
+
+    private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
+        CpuPowerStatsCollector.StatsArrayLayout layout =
+                new CpuPowerStatsCollector.StatsArrayLayout();
+        layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+        return layout.getScalingStepToPowerBracketMap();
+    }
+
     private void waitForIdle() {
         ConditionVariable done = new ConditionVariable();
         mHandler.post(done::open);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index 30a73181..eb03a6c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -180,7 +180,7 @@
 
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw, true);
-        multiStateStats.dump(pw);
+        multiStateStats.dump(pw, Arrays::toString);
         assertThat(sw.toString()).isEqualTo(
                 "plugged-in fg [25, 50]\n"
                 + "on-battery fg [25, 50]\n"
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 71007f5..52a5d8f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -156,6 +156,7 @@
         mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
         mUserState.setTouchExplorationEnabledLocked(true);
         mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
+        mUserState.setMagnificationTwoFingerTripleTapEnabledLocked(true);
         mUserState.setAutoclickEnabledLocked(true);
         mUserState.setUserNonInteractiveUiTimeoutLocked(30);
         mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -178,6 +179,7 @@
         assertNull(mUserState.getTargetAssignedToAccessibilityButton());
         assertFalse(mUserState.isTouchExplorationEnabledLocked());
         assertFalse(mUserState.isMagnificationSingleFingerTripleTapEnabledLocked());
+        assertFalse(mUserState.isMagnificationTwoFingerTripleTapEnabledLocked());
         assertFalse(mUserState.isAutoclickEnabledLocked());
         assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
         assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
index 3808f30..bfaf4959 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -29,6 +29,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -38,6 +40,7 @@
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.accessibilityservice.MagnificationConfig;
+import android.companion.virtual.IVirtualDeviceListener;
 import android.companion.virtual.IVirtualDeviceManager;
 import android.companion.virtual.VirtualDeviceManager;
 import android.content.ComponentName;
@@ -50,6 +53,7 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArraySet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -74,6 +78,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -94,6 +99,9 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    @Rule
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock private Context mMockContext;
     @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
     @Mock private AccessibilityWindowManager mMockA11yWindowManager;
@@ -114,6 +122,8 @@
 
     @Before
     public void setup() throws RemoteException {
+        mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
+
         MockitoAnnotations.initMocks(this);
         final Resources resources = InstrumentationRegistry.getContext().getResources();
 
@@ -121,6 +131,8 @@
                 resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width);
         mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color);
         when(mMockContext.getResources()).thenReturn(resources);
+        when(mMockContext.getMainExecutor())
+                .thenReturn(InstrumentationRegistry.getTargetContext().getMainExecutor());
 
         when(mMockVirtualDeviceManagerInternal.getDeviceIdsForUid(anyInt())).thenReturn(
                 new ArraySet(Set.of(DEVICE_ID)));
@@ -416,6 +428,101 @@
         assertThat(focusStrokeWidth).isEqualTo(mFocusStrokeWidthDefaultValue);
     }
 
+    @Test
+    public void testRegisterProxy_registersVirtualDeviceListener() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+
+        verify(mMockIVirtualDeviceManager, times(1)).registerVirtualDeviceListener(any());
+    }
+
+    @Test
+    public void testRegisterMultipleProxies_registersOneVirtualDeviceListener()
+            throws RemoteException {
+        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+        registerProxy(DISPLAY_2_ID);
+
+        verify(mMockIVirtualDeviceManager, times(1)).registerVirtualDeviceListener(any());
+    }
+
+    @Test
+    public void testUnregisterProxy_unregistersVirtualDeviceListener() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+
+        mProxyManager.unregisterProxy(DISPLAY_ID);
+
+        verify(mMockIVirtualDeviceManager, times(1)).unregisterVirtualDeviceListener(any());
+    }
+
+    @Test
+    public void testUnregisterProxy_onlyUnregistersVirtualDeviceListenerOnLastProxyRemoval()
+            throws RemoteException {
+        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+        registerProxy(DISPLAY_2_ID);
+
+        mProxyManager.unregisterProxy(DISPLAY_ID);
+        verify(mMockIVirtualDeviceManager, never()).unregisterVirtualDeviceListener(any());
+
+        mProxyManager.unregisterProxy(DISPLAY_2_ID);
+        verify(mMockIVirtualDeviceManager, times(1)).unregisterVirtualDeviceListener(any());
+    }
+
+    @Test
+    public void testRegisteredProxy_virtualDeviceClosed_proxyClosed()
+            throws RemoteException {
+        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+
+        assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isTrue();
+        assertThat(mProxyManager.isProxyedDisplay(DISPLAY_ID)).isTrue();
+
+        ArgumentCaptor<IVirtualDeviceListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(IVirtualDeviceListener.class);
+        verify(mMockIVirtualDeviceManager, times(1))
+                .registerVirtualDeviceListener(listenerArgumentCaptor.capture());
+
+        listenerArgumentCaptor.getValue().onVirtualDeviceClosed(DEVICE_ID);
+
+        verify(mMockProxySystemSupport, timeout(5_000)).removeDeviceIdLocked(DEVICE_ID);
+
+        assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isFalse();
+        assertThat(mProxyManager.isProxyedDisplay(DISPLAY_ID)).isFalse();
+    }
+
+    @Test
+    public void testRegisteredProxy_unrelatedVirtualDeviceClosed_proxyNotClosed()
+            throws RemoteException {
+        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+
+        assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isTrue();
+        assertThat(mProxyManager.isProxyedDisplay(DISPLAY_ID)).isTrue();
+
+        ArgumentCaptor<IVirtualDeviceListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(IVirtualDeviceListener.class);
+        verify(mMockIVirtualDeviceManager, times(1))
+                .registerVirtualDeviceListener(listenerArgumentCaptor.capture());
+
+        listenerArgumentCaptor.getValue().onVirtualDeviceClosed(DEVICE_ID + 1);
+
+        assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isTrue();
+        assertThat(mProxyManager.isProxyedDisplay(DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public void testRegisterProxy_doesNotRegisterVirtualDeviceListener_flagDisabled()
+            throws RemoteException {
+        mSetFlagsRule.disableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS);
+        registerProxy(DISPLAY_ID);
+        mProxyManager.unregisterProxy(DISPLAY_ID);
+
+        verify(mMockIVirtualDeviceManager, never()).registerVirtualDeviceListener(any());
+        verify(mMockIVirtualDeviceManager, never()).unregisterVirtualDeviceListener(any());
+    }
+
     private void registerProxy(int displayId) {
         try {
             mProxyManager.registerProxy(mMockAccessibilityServiceClient, displayId, anyInt(),
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 24a628e..d26d671 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -345,7 +345,7 @@
         assertWithMessage("should not have received intents")
                 .that(getActions(mInjector.mSentIntents)).isEmpty();
         // TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't
-        // because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
+        // because StorageManager.isCeStorageUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
         // properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can
         // mock static methods (but moving this class would involve changing the presubmit tests,
         // and the cascade effect goes on...). In fact, a better approach would to not assert the
@@ -648,7 +648,7 @@
         // checking.
         waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
         verify(mInjector.mStorageManagerMock, times(0))
-                .lockUserKey(anyInt());
+                .lockCeStorage(anyInt());
 
         addForegroundUserAndContinueUserSwitch(TEST_USER_ID2, TEST_USER_ID1,
                 numerOfUserSwitches, true);
@@ -663,7 +663,7 @@
         mUserController.finishUserStopped(ussUser1, /* allowDelayedLocking= */ true);
         waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
         verify(mInjector.mStorageManagerMock, times(1))
-                .lockUserKey(TEST_USER_ID);
+                .lockCeStorage(TEST_USER_ID);
     }
 
     /**
@@ -757,7 +757,7 @@
         mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND);
 
         verify(mInjector.mStorageManagerMock, never())
-                .unlockUserKey(eq(TEST_USER_ID), anyInt(), any());
+                .unlockCeStorage(eq(TEST_USER_ID), anyInt(), any());
     }
 
     @Test
@@ -1035,7 +1035,7 @@
         mUserController.finishUserStopped(ussUser, delayedLocking);
         waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
         verify(mInjector.mStorageManagerMock, times(expectLocking ? 1 : 0))
-                .lockUserKey(userId);
+                .lockCeStorage(userId);
     }
 
     private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index e3e708e..0230d77 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -47,7 +47,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -65,7 +64,6 @@
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptStatusListener;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -1753,45 +1751,6 @@
         verifyNoMoreInteractions(callback);
     }
 
-    @Test
-    public void testRegisterBiometricPromptOnKeyguardCallback_authenticationAlreadyStarted()
-            throws Exception {
-        final IBiometricPromptStatusListener callback =
-                mock(IBiometricPromptStatusListener.class);
-
-        setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
-        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
-                true /* requireConfirmation */, null /* authenticators */);
-        mBiometricService.mImpl.registerBiometricPromptStatusListener(callback);
-
-        verify(callback).onBiometricPromptShowing();
-    }
-
-    @Test
-    public void testRegisterBiometricPromptOnKeyguardCallback_startAuth_dismissDialog()
-            throws Exception {
-        final IBiometricPromptStatusListener listener =
-                mock(IBiometricPromptStatusListener.class);
-        setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
-        mBiometricService.mImpl.registerBiometricPromptStatusListener(listener);
-        waitForIdle();
-
-        verify(listener).onBiometricPromptIdle();
-
-        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
-                true /* requireConfirmation */, null /* authenticators */);
-        waitForIdle();
-
-        verify(listener).onBiometricPromptShowing();
-
-        final byte[] hat = generateRandomHAT();
-        mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
-                BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, hat);
-        waitForIdle();
-
-        verify(listener, times(2)).onBiometricPromptIdle();
-    }
-
     // Helper methods
 
     private int invokeCanAuthenticate(BiometricService service, int authenticators)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index fe2ac17..f5d50d1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -304,17 +304,17 @@
 
         doAnswer(invocation -> {
             Object[] args = invocation.getArguments();
-            mStorageManager.unlockUserKey(/* userId= */ (int) args[0],
+            mStorageManager.unlockCeStorage(/* userId= */ (int) args[0],
                     /* secret= */ (byte[]) args[2]);
             return null;
-        }).when(sm).unlockUserKey(anyInt(), anyInt(), any());
+        }).when(sm).unlockCeStorage(anyInt(), anyInt(), any());
 
         doAnswer(invocation -> {
             Object[] args = invocation.getArguments();
-            mStorageManager.setUserKeyProtection(/* userId= */ (int) args[0],
+            mStorageManager.setCeStorageProtection(/* userId= */ (int) args[0],
                     /* secret= */ (byte[]) args[1]);
             return null;
-        }).when(sm).setUserKeyProtection(anyInt(), any());
+        }).when(sm).setCeStorageProtection(anyInt(), any());
 
         return sm;
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java
index 91f3fed..c08ad13 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java
@@ -24,7 +24,7 @@
 
     private final ArrayMap<Integer, byte[]> mUserSecrets = new ArrayMap<>();
 
-    public void setUserKeyProtection(int userId, byte[] secret) {
+    public void setCeStorageProtection(int userId, byte[] secret) {
         assertThat(mUserSecrets).doesNotContainKey(userId);
         mUserSecrets.put(userId, secret);
     }
@@ -35,7 +35,7 @@
         return secret;
     }
 
-    public void unlockUserKey(int userId, byte[] secret) {
+    public void unlockCeStorage(int userId, byte[] secret) {
         assertThat(mUserSecrets.get(userId)).isEqualTo(secret);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 4e6dd06..ece3dfe 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -674,6 +674,17 @@
     }
 
     @Test
+    public void notifyPermissionRequestCancelled_forwardsToLogger() {
+        int hostUid = 123;
+        mService =
+                new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+        mService.notifyPermissionRequestCancelled(hostUid);
+
+        verify(mMediaProjectionMetricsLogger).logProjectionPermissionRequestCancelled(hostUid);
+    }
+
+    @Test
     public void notifyAppSelectorDisplayed_forwardsToLogger() {
         int hostUid = 456;
         mService =
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index 410604f..ad1cd6e 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
@@ -452,6 +453,63 @@
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
     }
 
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsStateChangedAtomId() {
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifyStateChangedAtomIdLogged();
+    }
+
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsExistingSessionId() {
+        int existingSessionId = 456;
+        when(mSessionIdGenerator.getCurrentSessionId()).thenReturn(existingSessionId);
+
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifySessionIdLogged(existingSessionId);
+    }
+
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsStateCancelled() {
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED);
+    }
+
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsPreviousState() {
+        mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+        verifyPreviousStateLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN);
+
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+        verifyPreviousStateLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
+    }
+
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsHostUid() {
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifyHostUidLogged(TEST_HOST_UID);
+    }
+
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsUnknownTargetUid() {
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifyTargetUidLogged(-2);
+    }
+
+    @Test
+    public void logProjectionPermissionRequestCancelled_logsUnknownCreationSource() {
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifyCreationSourceLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+    }
+
     private void verifyStateChangedAtomIdLogged() {
         verify(mFrameworkStatsLogWrapper)
                 .write(
diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index fc27edc..a4d50f0 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -94,27 +94,31 @@
     public void testBugreportFileManagerFileExists() {
         Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
         mBugreportFileManager.addBugreportFileForCaller(
-                callingInfo, mBugreportFile);
+                callingInfo, mBugreportFile, /* keepOnRetrieval= */ false);
 
         assertThrows(IllegalArgumentException.class, () ->
                 mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
-                        callingInfo, "unknown-file.zip"));
+                        mContext, callingInfo, Process.myUserHandle().getIdentifier(),
+                        "unknown-file.zip"));
 
         // No exception should be thrown.
-        mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(callingInfo, mBugreportFile);
+        mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
+                mContext, callingInfo, mContext.getUserId(), mBugreportFile);
     }
 
     @Test
     public void testBugreportFileManagerMultipleFiles() {
         Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
         mBugreportFileManager.addBugreportFileForCaller(
-                callingInfo, mBugreportFile);
+                callingInfo, mBugreportFile, /* keepOnRetrieval= */ false);
         mBugreportFileManager.addBugreportFileForCaller(
-                callingInfo, mBugreportFile2);
+                callingInfo, mBugreportFile2, /* keepOnRetrieval= */ false);
 
         // No exception should be thrown.
-        mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(callingInfo, mBugreportFile);
-        mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(callingInfo, mBugreportFile2);
+        mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
+                mContext, callingInfo, mContext.getUserId(), mBugreportFile);
+        mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
+                mContext, callingInfo, mContext.getUserId(), mBugreportFile2);
     }
 
     @Test
@@ -122,7 +126,8 @@
         Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
         assertThrows(IllegalArgumentException.class,
                 () -> mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
-                        callingInfo, "test-file.zip"));
+                        mContext, callingInfo, Process.myUserHandle().getIdentifier(),
+                        "test-file.zip"));
     }
 
     @Test
@@ -130,7 +135,8 @@
         CountDownLatch latch = new CountDownLatch(1);
         Listener listener = new Listener(latch);
         mService.retrieveBugreport(Binder.getCallingUid(), mContext.getPackageName(),
-                new FileDescriptor(), mBugreportFile, listener);
+                mContext.getUserId(), new FileDescriptor(), mBugreportFile,
+                /* keepOnRetrieval= */ false, listener);
         assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
         assertThat(listener.getErrorCode()).isEqualTo(
                 BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 9fca513..d09aa89 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -25,8 +25,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -46,6 +44,7 @@
 import android.os.IHintSession;
 import android.os.PerformanceHintManager;
 import android.os.Process;
+import android.util.Log;
 
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -58,7 +57,14 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
 
 /**
  * Tests for {@link com.android.server.power.hint.HintManagerService}.
@@ -67,8 +73,11 @@
  *  atest FrameworksServicesTests:HintManagerServiceTest
  */
 public class HintManagerServiceTest {
+    private static final String TAG = "HintManagerServiceTest";
+
     private static final long DEFAULT_HINT_PREFERRED_RATE = 16666666L;
     private static final long DEFAULT_TARGET_DURATION = 16666666L;
+    private static final long CONCURRENCY_TEST_DURATION_SEC = 10;
     private static final int UID = Process.myUid();
     private static final int TID = Process.myPid();
     private static final int TGID = Process.getThreadGroupLeader(TID);
@@ -103,6 +112,55 @@
         LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
     }
 
+    static class NativeWrapperFake extends NativeWrapper {
+        @Override
+        public void halInit() {
+        }
+
+        @Override
+        public long halGetHintSessionPreferredRate() {
+            return 1;
+        }
+
+        @Override
+        public long halCreateHintSession(int tgid, int uid, int[] tids, long durationNanos) {
+            return 1;
+        }
+
+        @Override
+        public void halPauseHintSession(long halPtr) {
+        }
+
+        @Override
+        public void halResumeHintSession(long halPtr) {
+        }
+
+        @Override
+        public void halCloseHintSession(long halPtr) {
+        }
+
+        @Override
+        public void halUpdateTargetWorkDuration(long halPtr, long targetDurationNanos) {
+        }
+
+        @Override
+        public void halReportActualWorkDuration(
+                long halPtr, long[] actualDurationNanos, long[] timeStampNanos) {
+        }
+
+        @Override
+        public void halSendHint(long halPtr, int hint) {
+        }
+
+        @Override
+        public void halSetThreads(long halPtr, int[] tids) {
+        }
+
+        @Override
+        public void halSetMode(long halPtr, int mode, boolean enabled) {
+        }
+    }
+
     private HintManagerService createService() {
         mService = new HintManagerService(mContext, new Injector() {
             NativeWrapper createNativeWrapper() {
@@ -112,6 +170,15 @@
         return mService;
     }
 
+    private HintManagerService createServiceWithFakeWrapper() {
+        mService = new HintManagerService(mContext, new Injector() {
+            NativeWrapper createNativeWrapper() {
+                return new NativeWrapperFake();
+            }
+        });
+        return mService;
+    }
+
     @Test
     public void testInitializeService() {
         HintManagerService service = createService();
@@ -166,8 +233,7 @@
             latch.countDown();
         });
         latch.await();
-
-        assumeFalse(a.updateHintAllowed());
+        assertFalse(service.mUidObserver.isUidForeground(a.mUid));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(anyLong());
 
         // Set session to foreground and calling updateHintAllowed() would invoke resume();
@@ -181,7 +247,7 @@
         });
         latch2.await();
 
-        assumeTrue(a.updateHintAllowed());
+        assertTrue(service.mUidObserver.isUidForeground(a.mUid));
         verify(mNativeWrapperMock, times(1)).halResumeHintSession(anyLong());
     }
 
@@ -254,7 +320,7 @@
         });
         latch.await();
 
-        assumeFalse(a.updateHintAllowed());
+        assertFalse(service.mUidObserver.isUidForeground(a.mUid));
         a.reportActualWorkDuration(DURATIONS_THREE, TIMESTAMPS_THREE);
         verify(mNativeWrapperMock, never()).halReportActualWorkDuration(anyLong(), any(), any());
     }
@@ -280,7 +346,7 @@
         service.mUidObserver.onUidStateChanged(
                 a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
         FgThread.getHandler().runWithScissors(() -> { }, 500);
-        assertFalse(a.updateHintAllowed());
+        assertFalse(service.mUidObserver.isUidForeground(a.mUid));
         a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
         verify(mNativeWrapperMock, never()).halSendHint(anyLong(), anyInt());
     }
@@ -303,7 +369,7 @@
         });
         latch.await();
 
-        assertFalse(a.updateHintAllowed());
+        assertFalse(service.mUidObserver.isUidForeground(a.mUid));
     }
 
     @Test
@@ -316,7 +382,7 @@
 
         service.mUidObserver.onUidStateChanged(
                 a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        assertTrue(a.updateHintAllowed());
+        assertTrue(service.mUidObserver.isUidForeground(a.mUid));
     }
 
     @Test
@@ -342,7 +408,7 @@
         service.mUidObserver.onUidStateChanged(
                 a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
         FgThread.getHandler().runWithScissors(() -> { }, 500);
-        assertFalse(a.updateHintAllowed());
+        assertFalse(service.mUidObserver.isUidForeground(a.mUid));
         a.setThreads(SESSION_TIDS_A);
         verify(mNativeWrapperMock, never()).halSetThreads(anyLong(), any());
     }
@@ -372,9 +438,159 @@
         service.mUidObserver.onUidStateChanged(
                 a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
         FgThread.getHandler().runWithScissors(() -> { }, 500);
-        assertFalse(a.updateHintAllowed());
+        assertFalse(service.mUidObserver.isUidForeground(a.mUid));
         a.setMode(0, true);
         verify(mNativeWrapperMock, never()).halSetMode(anyLong(), anyInt(), anyBoolean());
     }
 
+    // This test checks that concurrent operations from different threads on IHintService,
+    // IHintSession and UidObserver will not cause data race or deadlock. Ideally we should also
+    // check the output of threads' reportActualDuration performance to detect lock starvation
+    // but the result is not stable, so it's better checked manually.
+    @Test
+    public void testConcurrency() throws Exception {
+        HintManagerService service = createServiceWithFakeWrapper();
+        // initialize session threads to run in parallel
+        final int sessionCount = 10;
+        // the signal that the main thread will send to session threads to check for run or exit
+        AtomicReference<Boolean> shouldRun = new AtomicReference<>(true);
+        // the signal for main test thread to wait for session threads and notifier thread to
+        // finish and exit
+        CountDownLatch latch = new CountDownLatch(sessionCount + 1);
+        // list of exceptions with one per session thread or notifier thread
+        List<AtomicReference<Exception>> execs = new ArrayList<>(sessionCount + 1);
+        List<Thread> threads = new ArrayList<>(sessionCount + 1);
+        for (int i = 0; i < sessionCount; i++) {
+            final AtomicReference<Exception> exec = new AtomicReference<>();
+            execs.add(exec);
+            int j = i;
+            Thread app = new Thread(() -> {
+                try {
+                    while (shouldRun.get()) {
+                        runAppHintSession(service, j, shouldRun);
+                    }
+                } catch (Exception e) {
+                    exec.set(e);
+                } finally {
+                    latch.countDown();
+                }
+            });
+            threads.add(app);
+        }
+
+        // initialize a UID state notifier thread to run in parallel
+        final AtomicReference<Exception> notifierExec = new AtomicReference<>();
+        execs.add(notifierExec);
+        Thread notifier = new Thread(() -> {
+            try {
+                long min = Long.MAX_VALUE;
+                long max = Long.MIN_VALUE;
+                long sum = 0;
+                int count = 0;
+                while (shouldRun.get()) {
+                    long start = System.nanoTime();
+                    service.mUidObserver.onUidStateChanged(UID,
+                            ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+                    long elapsed = System.nanoTime() - start;
+                    sum += elapsed;
+                    count++;
+                    min = Math.min(min, elapsed);
+                    max = Math.max(max, elapsed);
+                    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
+                    service.mUidObserver.onUidStateChanged(UID,
+                            ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+                    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
+                }
+                Log.d(TAG, "notifier thread min " + min + " max " + max + " avg " + sum / count);
+                service.mUidObserver.onUidGone(UID, true);
+            } catch (Exception e) {
+                notifierExec.set(e);
+            } finally {
+                latch.countDown();
+            }
+        });
+        threads.add(notifier);
+
+        // start all the threads
+        for (Thread thread : threads) {
+            thread.start();
+        }
+        // keep the test running for a few seconds
+        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(CONCURRENCY_TEST_DURATION_SEC));
+        // send signal to stop all threads
+        shouldRun.set(false);
+        // wait for all threads to exit
+        latch.await();
+        // check if any thread throws exception
+        for (AtomicReference<Exception> exec : execs) {
+            if (exec.get() != null) {
+                throw exec.get();
+            }
+        }
+    }
+
+    private void runAppHintSession(HintManagerService service, int logId,
+            AtomicReference<Boolean> shouldRun) throws Exception {
+        IBinder token = new Binder();
+        AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
+                .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+        // we will start some threads and get their valid TIDs to update
+        int threadCount = 3;
+        // the list of TIDs
+        int[] tids = new int[threadCount];
+        // atomic index for each thread to set its TID in the list
+        AtomicInteger k = new AtomicInteger(0);
+        // signal for the session main thread to wait for child threads to finish updating TIDs
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        // signal for the session main thread to notify child threads to exit
+        CountDownLatch stopLatch = new CountDownLatch(1);
+        for (int j = 0; j < threadCount; j++) {
+            Thread thread = new Thread(() -> {
+                try {
+                    tids[k.getAndIncrement()] = android.os.Process.myTid();
+                    latch.countDown();
+                    stopLatch.await();
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+            thread.start();
+        }
+        latch.await();
+        a.setThreads(tids);
+        // we don't need the threads to exist after update
+        stopLatch.countDown();
+        a.updateTargetWorkDuration(5);
+        // measure the time it takes in HintManagerService to run reportActualDuration
+        long min = Long.MAX_VALUE;
+        long max = Long.MIN_VALUE;
+        long sum = 0;
+        int count = 0;
+        List<Long> values = new ArrayList<>();
+        long testStart = System.nanoTime();
+        // run report actual for 4-second per cycle
+        while (shouldRun.get() && System.nanoTime() - testStart < TimeUnit.SECONDS.toNanos(
+                Math.min(4, CONCURRENCY_TEST_DURATION_SEC))) {
+            long start = System.nanoTime();
+            a.reportActualWorkDuration(new long[]{5}, new long[]{start});
+            long elapsed = System.nanoTime() - start;
+            values.add(elapsed);
+            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(5));
+            sum += elapsed;
+            count++;
+            min = Math.min(min, elapsed);
+            max = Math.max(max, elapsed);
+        }
+        Collections.sort(values);
+        if (!values.isEmpty()) {
+            Log.d(TAG, "app thread " + logId + " min " + min + " max " + max
+                    + " avg " + sum / count + " count " + count
+                    + " 80th " + values.get((int) (values.size() * 0.8))
+                    + " 90th " + values.get((int) (values.size() * 0.9))
+                    + " 95th " + values.get((int) (values.size() * 0.95)));
+        } else {
+            Log.w(TAG, "No reportActualWorkDuration executed");
+        }
+        a.close();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 51b9c17..47f15b8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -18,6 +18,7 @@
 import static com.android.server.notification.SnoozeHelper.CONCURRENT_SNOOZE_LIMIT;
 import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
 
+import static com.google.common.truth.Truth.assertThat;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -486,6 +487,29 @@
     }
 
     @Test
+    public void testRepostAll() throws Exception {
+        final int profileId = 11;
+        final int otherUserId = 2;
+        IntArray userIds = new IntArray();
+        userIds.add(UserHandle.USER_SYSTEM);
+        userIds.add(profileId);
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+        NotificationRecord r3 = getNotificationRecord("pkg", 3, "three", UserHandle.of(profileId));
+        NotificationRecord r4 = getNotificationRecord("pkg", 4, "four", UserHandle.of(otherUserId));
+        mSnoozeHelper.snooze(r,  1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        mSnoozeHelper.snooze(r3, 1000);
+        mSnoozeHelper.snooze(r4, 1000);
+
+        mSnoozeHelper.repostAll(userIds);
+
+        verify(mCallback, times(3)).repost(anyInt(), any(), anyBoolean());
+        // All notifications were reposted, except the one for otherUserId
+        assertThat(mSnoozeHelper.getSnoozed()).containsExactly(r4);
+    }
+
+    @Test
     public void testGetSnoozedBy() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 6235b3b..c57b051 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -1742,6 +1742,7 @@
     private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
             @TaskFragmentOperation.OperationType int opType) {
         final Task task = createTask(mDisplayContent);
+        doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
         // Create a non-embedded Activity at the bottom.
         final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
                 .setTask(task)
@@ -1934,7 +1935,7 @@
     /** Setups the mock Task as the parent of the given TaskFragment. */
     private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
         doReturn(mockParent).when(taskFragment).getTask();
-        doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true))
+        doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true, true))
                 .when(mockParent).getTaskFragmentParentInfo();
 
         // Task needs to be visible
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0c58069..435a835 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1568,6 +1568,7 @@
 
         final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
         final ActivityRecord embeddedActivity = fragment.getTopMostActivity();
+        doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
         task.moveActivityToFront(activity);
         assertEquals("Activity must be moved to front", activity, task.getTopMostActivity());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index c8546c6..4773023 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2475,6 +2475,46 @@
         verify(session).close();
     }
 
+    @Test
+    public void testReadyTrackerBasics() {
+        final TransitionController controller = new TestTransitionController(
+                mock(ActivityTaskManagerService.class));
+        controller.setFullReadyTrackingForTest(true);
+        Transition transit = createTestTransition(TRANSIT_OPEN, controller);
+        // Not ready if nothing has happened yet
+        assertFalse(transit.mReadyTracker.isReady());
+
+        Transition.ReadyCondition condition1 = new Transition.ReadyCondition("c1");
+        transit.mReadyTracker.add(condition1);
+        assertFalse(transit.mReadyTracker.isReady());
+
+        Transition.ReadyCondition condition2 = new Transition.ReadyCondition("c2");
+        transit.mReadyTracker.add(condition2);
+        assertFalse(transit.mReadyTracker.isReady());
+
+        condition2.meet();
+        assertFalse(transit.mReadyTracker.isReady());
+
+        condition1.meet();
+        assertTrue(transit.mReadyTracker.isReady());
+    }
+
+    @Test
+    public void testReadyTrackerAlternate() {
+        final TransitionController controller = new TestTransitionController(
+                mock(ActivityTaskManagerService.class));
+        controller.setFullReadyTrackingForTest(true);
+        Transition transit = createTestTransition(TRANSIT_OPEN, controller);
+
+        Transition.ReadyCondition condition1 = new Transition.ReadyCondition("c1");
+        transit.mReadyTracker.add(condition1);
+        assertFalse(transit.mReadyTracker.isReady());
+
+        condition1.meetAlternate("reason1");
+        assertTrue(transit.mReadyTracker.isReady());
+        assertEquals("reason1", condition1.mAlternate);
+    }
+
     private static void makeTaskOrganized(Task... tasks) {
         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
         for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 0b77fd8..699580a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -101,6 +101,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.function.BiConsumer;
 
 /**
  * Test class for {@link ITaskOrganizer} and {@link android.window.ITaskOrganizerController}.
@@ -583,7 +584,7 @@
     }
 
     @Test
-    public void testTaskFragmentHiddenAndFocusableChanges() {
+    public void testTaskFragmentHiddenFocusableTranslucentChanges() {
         removeGlobalMinSizeRestriction();
         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
@@ -605,10 +606,12 @@
         assertTrue(taskFragment.shouldBeVisible(null));
         assertTrue(taskFragment.isFocusable());
         assertTrue(taskFragment.isTopActivityFocusable());
+        assertFalse(taskFragment.isForceTranslucent());
 
         // Apply transaction to the TaskFragment hidden and not focusable.
         t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
         t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+        t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), true);
         mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
                 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
                 false /* shouldApplyIndependently */);
@@ -617,10 +620,12 @@
         assertFalse(taskFragment.shouldBeVisible(null));
         assertFalse(taskFragment.isFocusable());
         assertFalse(taskFragment.isTopActivityFocusable());
+        assertTrue(taskFragment.isForceTranslucent());
 
         // Apply transaction to the TaskFragment not hidden and focusable.
         t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false);
         t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+        t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), false);
         mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
                 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
                 false /* shouldApplyIndependently */);
@@ -629,10 +634,32 @@
         assertTrue(taskFragment.shouldBeVisible(null));
         assertTrue(taskFragment.isFocusable());
         assertTrue(taskFragment.isTopActivityFocusable());
+        assertFalse(taskFragment.isForceTranslucent());
     }
 
     @Test
-    public void testTaskFragmentHiddenAndFocusableChanges_throwsWhenNotSystemOrganizer() {
+    public void testTaskFragmentChangeHidden_throwsWhenNotSystemOrganizer() {
+        // Non-system organizers are not allow to update the hidden state.
+        testTaskFragmentChangesWithoutSystemOrganizerThrowException(
+                (t, windowContainerToken) -> t.setHidden(windowContainerToken, true));
+    }
+
+    @Test
+    public void testTaskFragmentChangeFocusable_throwsWhenNotSystemOrganizer() {
+        // Non-system organizers are not allow to update the focusable state.
+        testTaskFragmentChangesWithoutSystemOrganizerThrowException(
+                (t, windowContainerToken) -> t.setFocusable(windowContainerToken, false));
+    }
+
+    @Test
+    public void testTaskFragmentChangeTranslucent_throwsWhenNotSystemOrganizer() {
+        // Non-system organizers are not allow to update the translucent state.
+        testTaskFragmentChangesWithoutSystemOrganizerThrowException(
+                (t, windowContainerToken) -> t.setForceTranslucent(windowContainerToken, true));
+    }
+
+    private void testTaskFragmentChangesWithoutSystemOrganizerThrowException(
+            BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp) {
         removeGlobalMinSizeRestriction();
         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
@@ -641,21 +668,15 @@
         final TaskFragmentOrganizer organizer =
                 createTaskFragmentOrganizer(t, false /* isSystemOrganizer */);
 
-        final IBinder token = new Binder();
         final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                 .setParentTask(rootTask)
-                .setFragmentToken(token)
+                .setFragmentToken(new Binder())
                 .setOrganizer(organizer)
                 .createActivityCount(1)
                 .build();
 
-        assertTrue(rootTask.shouldBeVisible(null));
-        assertTrue(taskFragment.shouldBeVisible(null));
+        addOp.accept(t, taskFragment.mRemoteToken.toWindowContainerToken());
 
-        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
-        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
-
-        // Non-system organizers are not allow to update the hidden and focusable states.
         assertThrows(SecurityException.class, () ->
                 mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
                         t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
@@ -1646,6 +1667,28 @@
         verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
     }
 
+    @Test
+    public void testSetAlwaysOnTop() {
+        final Task rootTask = new TaskBuilder(mSupervisor)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        testSetAlwaysOnTop(rootTask);
+
+        final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
+        displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        testSetAlwaysOnTop(displayArea);
+    }
+
+    private void testSetAlwaysOnTop(WindowContainer wc) {
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+        t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), true);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+        assertTrue(wc.isAlwaysOnTop());
+
+        t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), false);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+        assertFalse(wc.isAlwaysOnTop());
+    }
+
     private ActivityRecord createActivityRecordAndDispatchPendingEvents(Task task) {
         final ActivityRecord record = createActivityRecord(task);
         // Flush EVENT_APPEARED.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 3ec6f42..973ab84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -448,7 +448,6 @@
         mDisplayContent.updateImeParent();
 
         // Ime should on top of the popup IME layering target window.
-        mDisplayContent.assignChildLayers(mTransaction);
         assertWindowHigher(mImeWindow, popupImeTargetWin);
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index 93b5a40..f6c6a64 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
 import static android.Manifest.permission.LOG_COMPAT_CHANGE;
 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
 import static android.Manifest.permission.RECORD_AUDIO;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
@@ -29,6 +30,8 @@
 import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
 import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
 import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_COPY_AUDIO_DATA_FAILURE;
+import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED;
+import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION;
 
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_ERROR;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS;
@@ -75,6 +78,8 @@
 import android.service.voice.HotwordDetectionServiceFailure;
 import android.service.voice.HotwordDetector;
 import android.service.voice.HotwordRejectedResult;
+import android.service.voice.HotwordTrainingData;
+import android.service.voice.HotwordTrainingDataLimitEnforcer;
 import android.service.voice.IDspHotwordDetectionCallback;
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
 import android.service.voice.VisualQueryDetectionServiceFailure;
@@ -127,6 +132,9 @@
     private static final String HOTWORD_DETECTION_OP_MESSAGE =
             "Providing hotword detection result to VoiceInteractionService";
 
+    private static final String HOTWORD_TRAINING_DATA_OP_MESSAGE =
+            "Providing hotword training data to VoiceInteractionService";
+
     // The error codes are used for onHotwordDetectionServiceFailure callback.
     // Define these due to lines longer than 100 characters.
     static final int ONDETECTED_GOT_SECURITY_EXCEPTION =
@@ -512,6 +520,25 @@
                                 }
 
                                 @Override
+                                public void onTrainingData(HotwordTrainingData data)
+                                        throws RemoteException {
+                                    sendTrainingData(new TrainingDataEgressCallback() {
+                                        @Override
+                                        public void onHotwordDetectionServiceFailure(
+                                                HotwordDetectionServiceFailure failure)
+                                                throws RemoteException {
+                                            callback.onHotwordDetectionServiceFailure(failure);
+                                        }
+
+                                        @Override
+                                        public void onTrainingData(HotwordTrainingData data)
+                                                throws RemoteException {
+                                            callback.onTrainingData(data);
+                                        }
+                                    }, data);
+                                }
+
+                                @Override
                                 public void onDetected(HotwordDetectedResult triggerResult)
                                         throws RemoteException {
                                     synchronized (mLock) {
@@ -593,6 +620,82 @@
                 mVoiceInteractionServiceUid);
     }
 
+    /** Used to send training data.
+     *
+     * @hide
+     */
+    interface TrainingDataEgressCallback {
+        /** Called to send training data */
+        void onTrainingData(HotwordTrainingData trainingData) throws RemoteException;
+
+        /** Called to inform failure to send training data. */
+        void onHotwordDetectionServiceFailure(HotwordDetectionServiceFailure failure) throws
+                RemoteException;
+
+    }
+
+    /** Default implementation to send training data from {@link HotwordDetectionService}
+     *  to {@link HotwordDetector}.
+     *
+     * <p> Verifies RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA permission has been
+     * granted and training data egress is within daily limit.
+     *
+     * @param callback used to send training data or inform of failures to send training data.
+     * @param data training data to egress.
+     *
+     * @hide
+     */
+    void sendTrainingData(
+            TrainingDataEgressCallback callback, HotwordTrainingData data) throws RemoteException {
+        Slog.d(TAG, "onTrainingData()");
+
+        // Check training data permission is granted.
+        try {
+            enforcePermissionForTrainingDataDelivery();
+        } catch (SecurityException e) {
+            Slog.w(TAG, "Ignoring training data due to a SecurityException", e);
+            try {
+                callback.onHotwordDetectionServiceFailure(
+                        new HotwordDetectionServiceFailure(
+                                ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION,
+                                "Security exception occurred"
+                                        + "in #onTrainingData method."));
+            } catch (RemoteException e1) {
+                notifyOnDetectorRemoteException();
+                throw e1;
+            }
+            return;
+        }
+
+        // Check whether within daily egress limit.
+        boolean withinEgressLimit = HotwordTrainingDataLimitEnforcer.getInstance(mContext)
+                                                                    .incrementEgressCount();
+        if (!withinEgressLimit) {
+            Slog.d(TAG, "Ignoring training data as exceeded egress limit.");
+            try {
+                callback.onHotwordDetectionServiceFailure(
+                        new HotwordDetectionServiceFailure(
+                                ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED,
+                                "Training data egress limit exceeded."));
+            } catch (RemoteException e) {
+                notifyOnDetectorRemoteException();
+                throw e;
+            }
+            return;
+        }
+
+        try {
+            Slog.i(TAG, "Egressing training data from hotword trusted process.");
+            if (mDebugHotwordLogging) {
+                Slog.d(TAG, "Egressing hotword training data " + data);
+            }
+            callback.onTrainingData(data);
+        } catch (RemoteException e) {
+            notifyOnDetectorRemoteException();
+            throw e;
+        }
+    }
+
     void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
         synchronized (mLock) {
             if (mInitialized || mDestroyed) {
@@ -781,6 +884,27 @@
     }
 
     /**
+     * Enforces permission for training data delivery.
+     *
+     * <p> Throws a {@link SecurityException} if training data egress permission is not granted.
+     */
+    void enforcePermissionForTrainingDataDelivery() {
+        Binder.withCleanCallingIdentity(() -> {
+            synchronized (mLock) {
+                enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
+                        RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+                        HOTWORD_TRAINING_DATA_OP_MESSAGE);
+
+                mAppOpsManager.noteOpNoThrow(
+                        AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+                        mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+                        mVoiceInteractorIdentity.attributionTag,
+                        HOTWORD_TRAINING_DATA_OP_MESSAGE);
+            }
+        });
+    }
+
+    /**
      * Throws a {@link SecurityException} if the given identity has no permission to receive data.
      *
      * @param context    A {@link Context}, used for permission checks.
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
index 9a4fbdc..6418f3e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
@@ -42,6 +42,7 @@
 import android.service.voice.HotwordDetectionServiceFailure;
 import android.service.voice.HotwordDetector;
 import android.service.voice.HotwordRejectedResult;
+import android.service.voice.HotwordTrainingData;
 import android.service.voice.IDspHotwordDetectionCallback;
 import android.util.Slog;
 
@@ -229,6 +230,23 @@
                     }
                 }
             }
+
+            @Override
+            public void onTrainingData(HotwordTrainingData data) throws RemoteException {
+                sendTrainingData(new TrainingDataEgressCallback() {
+                    @Override
+                    public void onHotwordDetectionServiceFailure(
+                            HotwordDetectionServiceFailure failure) throws RemoteException {
+                        externalCallback.onHotwordDetectionServiceFailure(failure);
+                    }
+
+                    @Override
+                    public void onTrainingData(HotwordTrainingData data)
+                            throws RemoteException {
+                        externalCallback.onTrainingData(data);
+                    }
+                }, data);
+            }
         };
 
         mValidatingDspTrigger = true;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 0a70a5f..b098e82 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -214,7 +214,6 @@
                 new ServiceConnectionFactory(visualQueryDetectionServiceIntent,
                         bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_VISUAL_QUERY);
 
-
         mLastRestartInstant = Instant.now();
 
         if (mReStartPeriodSeconds <= 0) {
@@ -918,7 +917,8 @@
             session = new SoftwareTrustedHotwordDetectorSession(
                     mRemoteHotwordDetectionService, mLock, mContext, token, callback,
                     mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
-                    mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener);
+                    mScheduledExecutorService, mDebugHotwordLogging,
+                    mRemoteExceptionListener);
         }
         mHotwordRecognitionCallback = callback;
         mDetectorSessions.put(detectorType, session);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
index f06c997..2e23eff 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
@@ -40,6 +40,7 @@
 import android.service.voice.HotwordDetectionServiceFailure;
 import android.service.voice.HotwordDetector;
 import android.service.voice.HotwordRejectedResult;
+import android.service.voice.HotwordTrainingData;
 import android.service.voice.IDspHotwordDetectionCallback;
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
 import android.service.voice.ISandboxedDetectionService;
@@ -195,6 +196,21 @@
                         mVoiceInteractionServiceUid);
                 // onRejected isn't allowed here, and we are not expecting it.
             }
+
+            public void onTrainingData(HotwordTrainingData data) throws RemoteException {
+                sendTrainingData(new TrainingDataEgressCallback() {
+                    @Override
+                    public void onHotwordDetectionServiceFailure(
+                            HotwordDetectionServiceFailure failure) throws RemoteException {
+                        mSoftwareCallback.onHotwordDetectionServiceFailure(failure);
+                    }
+
+                    @Override
+                    public void onTrainingData(HotwordTrainingData data) throws RemoteException {
+                        mSoftwareCallback.onTrainingData(data);
+                    }
+                }, data);
+            }
         };
 
         mRemoteDetectionService.run(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 1c689d0..a584fc9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1541,7 +1541,31 @@
                 }
             }
         }
-        //----------------- Model management APIs --------------------------------//
+
+        @Override
+        @android.annotation.EnforcePermission(
+                android.Manifest.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT)
+        public void resetHotwordTrainingDataEgressCountForTest() {
+            super.resetHotwordTrainingDataEgressCountForTest_enforcePermission();
+            synchronized (this) {
+                enforceIsCurrentVoiceInteractionService();
+
+                if (mImpl == null) {
+                    Slog.w(TAG, "resetHotwordTrainingDataEgressCountForTest without running"
+                            + " voice interaction service");
+                    return;
+                }
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.resetHotwordTrainingDataEgressCountForTest();
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+
+            }
+        }
+
+      //----------------- Model management APIs --------------------------------//
 
         @Override
         public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 6ba77da..3c4b58f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -60,6 +60,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.service.voice.HotwordDetector;
+import android.service.voice.HotwordTrainingDataLimitEnforcer;
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
 import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
 import android.service.voice.IVoiceInteractionService;
@@ -72,6 +73,7 @@
 import android.util.Slog;
 import android.view.IWindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
 import com.android.internal.app.IVisualQueryDetectionAttentionListener;
 import com.android.internal.app.IVoiceActionCheckCallback;
@@ -991,6 +993,12 @@
         }
     }
 
+    @VisibleForTesting
+    void resetHotwordTrainingDataEgressCountForTest() {
+        HotwordTrainingDataLimitEnforcer.getInstance(mContext.getApplicationContext())
+                        .resetTrainingDataEgressCount();
+    }
+
     void startLocked() {
         Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
         intent.setComponent(mComponent);
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
deleted file mode 100644
index e172090..0000000
--- a/startop/view_compiler/Android.bp
+++ /dev/null
@@ -1,115 +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.
-//
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-cc_defaults {
-    name: "viewcompiler_defaults",
-    header_libs: [
-        "libbase_headers",
-    ],
-    shared_libs: [
-        "libbase",
-        "slicer",
-    ],
-    static_libs: [
-        "libcutils",
-        "libtinyxml2",
-        "liblog",
-        "libutils",
-        "libziparchive",
-        "libz",
-    ],
-    cpp_std: "gnu++2b",
-    target: {
-        android: {
-            shared_libs: [
-                "libandroidfw",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libandroidfw",
-            ],
-        },
-    },
-}
-
-cc_library_static {
-    name: "libviewcompiler",
-    defaults: ["viewcompiler_defaults"],
-    srcs: [
-        "apk_layout_compiler.cc",
-        "dex_builder.cc",
-        "dex_layout_compiler.cc",
-        "java_lang_builder.cc",
-        "tinyxml_layout_parser.cc",
-        "util.cc",
-        "layout_validation.cc",
-    ],
-    host_supported: true,
-}
-
-cc_binary {
-    name: "viewcompiler",
-    defaults: ["viewcompiler_defaults"],
-    srcs: [
-        "main.cc",
-    ],
-    static_libs: [
-        "libgflags",
-        "libviewcompiler",
-    ],
-    host_supported: true,
-}
-
-cc_test_host {
-    name: "view-compiler-tests",
-    defaults: ["viewcompiler_defaults"],
-    srcs: [
-        "layout_validation_test.cc",
-        "util_test.cc",
-    ],
-    static_libs: [
-        "libviewcompiler",
-    ],
-}
-
-cc_binary_host {
-    name: "dex_testcase_generator",
-    defaults: ["viewcompiler_defaults"],
-    srcs: ["dex_testcase_generator.cc"],
-    static_libs: [
-        "libviewcompiler",
-    ],
-}
-
-genrule {
-    name: "generate_dex_testcases",
-    tools: [":dex_testcase_generator"],
-    cmd: "$(location :dex_testcase_generator) $(genDir)",
-    out: [
-        "simple.dex",
-        "trivial.dex",
-    ],
-}
diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS
deleted file mode 100644
index e5aead9..0000000
--- a/startop/view_compiler/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-eholk@google.com
-mathieuc@google.com
diff --git a/startop/view_compiler/README.md b/startop/view_compiler/README.md
deleted file mode 100644
index f8da02b..0000000
--- a/startop/view_compiler/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# View Compiler
-
-This directory contains an experimental compiler for layout files.
-
-It will take a layout XML file and produce a CompiledLayout.java file with a
-specialized layout inflation function.
-
-To use it, let's assume you had a layout in `my_layout.xml` and your app was in
-the Java language package `com.example.myapp`. Run the following command:
-
-    viewcompiler my_layout.xml --package com.example.myapp --out CompiledView.java
-
-This will produce a `CompiledView.java`, which can then be compiled into your
-Android app. Then to use it, in places where you would have inflated
-`R.layouts.my_layout`, instead call `CompiledView.inflate`.
-
-Precompiling views like this generally improves the time needed to inflate them.
-
-This tool is still in its early stages and has a number of limitations.
-* Currently only one layout can be compiled at a time.
-* `merge` and `include` nodes are not supported.
-* View compilation is a manual process that requires code changes in the
-  application.
-* This only works for apps that do not use a custom layout inflater.
-* Other limitations yet to be discovered.
-
-## DexBuilder Tests
-
-The DexBuilder has several low-level end to end tests to verify generated DEX
-code validates, runs, and has the correct behavior. There are, unfortunately, a
-number of pieces that must be added to generate new tests. Here are the
-components:
-
-* `dex_testcase_generator` - Written in C++ using `DexBuilder`. This runs as a
-  build step produce the DEX files that will be tested on device. See the
-  `genrule` named `generate_dex_testcases` in `Android.bp`. These files are then
-  copied over to the device by TradeFed when running tests.
-* `DexBuilderTest` - This is a Java Language test harness that loads the
-  generated DEX files and exercises methods in the file.
-
-To add a new DEX file test, follow these steps:
-1. Modify `dex_testcase_generator` to produce the DEX file.
-2. Add the filename to the `out` list of the `generate_dex_testcases` rule in
-   `Android.bp`.
-3. Add a new `push` option to `AndroidTest.xml` to copy the DEX file to the
-   device.
-4. Modify `DexBuilderTest.java` to load and exercise the new test.
-
-In each case, you should be able to cargo-cult the existing test cases.
-
-In general, you can probably get by without adding a new generated DEX file, and
-instead add more methods to the files that are already generated. In this case,
-you can skip all of steps 2 and 3 above, and simplify steps 1 and 4.
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
deleted file mode 100644
index 5f5652c..0000000
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ /dev/null
@@ -1,179 +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.
- */
-
-#include "apk_layout_compiler.h"
-#include "dex_layout_compiler.h"
-#include "java_lang_builder.h"
-#include "layout_validation.h"
-#include "util.h"
-
-#include "androidfw/ApkAssets.h"
-#include "androidfw/AssetManager2.h"
-#include "androidfw/ResourceTypes.h"
-
-#include <iostream>
-#include <locale>
-
-#include "android-base/stringprintf.h"
-
-namespace startop {
-
-using android::ResXMLParser;
-using android::base::StringPrintf;
-
-class ResXmlVisitorAdapter {
- public:
-  ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {}
-
-  template <typename Visitor>
-  void Accept(Visitor* visitor) {
-    size_t depth{0};
-    do {
-      switch (parser_->next()) {
-        case ResXMLParser::START_DOCUMENT:
-          depth++;
-          visitor->VisitStartDocument();
-          break;
-        case ResXMLParser::END_DOCUMENT:
-          depth--;
-          visitor->VisitEndDocument();
-          break;
-        case ResXMLParser::START_TAG: {
-          depth++;
-          size_t name_length = 0;
-          const char16_t* name = parser_->getElementName(&name_length);
-          visitor->VisitStartTag(std::u16string{name, name_length});
-          break;
-        }
-        case ResXMLParser::END_TAG:
-          depth--;
-          visitor->VisitEndTag();
-          break;
-        default:;
-      }
-    } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE);
-  }
-
- private:
-  ResXMLParser* parser_;
-};
-
-bool CanCompileLayout(ResXMLParser* parser) {
-  ResXmlVisitorAdapter adapter{parser};
-  LayoutValidationVisitor visitor;
-  adapter.Accept(&visitor);
-
-  return visitor.can_compile();
-}
-
-namespace {
-void CompileApkAssetsLayouts(const android::ApkAssetsPtr& assets, CompilationTarget target,
-                             std::ostream& target_out) {
-  android::AssetManager2 resources;
-  resources.SetApkAssets({assets});
-
-  std::string package_name;
-
-  // TODO: handle multiple packages better
-  bool first = true;
-  for (const auto& package : assets->GetLoadedArsc()->GetPackages()) {
-    CHECK(first);
-    package_name = package->GetPackageName();
-    first = false;
-  }
-
-  dex::DexBuilder dex_file;
-  dex::ClassBuilder compiled_view{
-      dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))};
-  std::vector<dex::MethodBuilder> methods;
-
-  assets->GetAssetsProvider()->ForEachFile("res/", [&](android::StringPiece s, android::FileType) {
-      if (s == "layout") {
-          auto path = StringPrintf("res/%.*s/", (int)s.size(), s.data());
-          assets->GetAssetsProvider()
-                  ->ForEachFile(path, [&](android::StringPiece layout_file, android::FileType) {
-                      auto layout_path = StringPrintf("%s%.*s", path.c_str(),
-                                                      (int)layout_file.size(), layout_file.data());
-                      android::ApkAssetsCookie cookie = android::kInvalidCookie;
-                      auto asset = resources.OpenNonAsset(layout_path,
-                                                          android::Asset::ACCESS_RANDOM, &cookie);
-                      CHECK(asset);
-                      CHECK(android::kInvalidCookie != cookie);
-                      const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie);
-                      CHECK(nullptr != dynamic_ref_table);
-                      android::ResXMLTree xml_tree{dynamic_ref_table};
-                      xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true), asset->getLength(),
-                                     /*copy_data=*/true);
-                      android::ResXMLParser parser{xml_tree};
-                      parser.restart();
-                      if (CanCompileLayout(&parser)) {
-                          parser.restart();
-                          const std::string layout_name =
-                                  startop::util::FindLayoutNameFromFilename(layout_path);
-                          ResXmlVisitorAdapter adapter{&parser};
-                          switch (target) {
-                              case CompilationTarget::kDex: {
-                                  methods.push_back(compiled_view.CreateMethod(
-                                          layout_name,
-                                          dex::Prototype{dex::TypeDescriptor::FromClassname(
-                                                                 "android.view.View"),
-                                                         dex::TypeDescriptor::FromClassname(
-                                                                 "android.content.Context"),
-                                                         dex::TypeDescriptor::Int()}));
-                                  DexViewBuilder builder(&methods.back());
-                                  builder.Start();
-                                  LayoutCompilerVisitor visitor{&builder};
-                                  adapter.Accept(&visitor);
-                                  builder.Finish();
-                                  methods.back().Encode();
-                                  break;
-                              }
-                              case CompilationTarget::kJavaLanguage: {
-                                  JavaLangViewBuilder builder{package_name, layout_name,
-                                                              target_out};
-                                  builder.Start();
-                                  LayoutCompilerVisitor visitor{&builder};
-                                  adapter.Accept(&visitor);
-                                  builder.Finish();
-                                  break;
-                              }
-                          }
-                      }
-                  });
-      }
-  });
-
-  if (target == CompilationTarget::kDex) {
-    slicer::MemView image{dex_file.CreateImage()};
-    target_out.write(image.ptr<const char>(), image.size());
-  }
-}
-}  // namespace
-
-void CompileApkLayouts(const std::string& filename, CompilationTarget target,
-                       std::ostream& target_out) {
-  auto assets = android::ApkAssets::Load(filename);
-  CompileApkAssetsLayouts(assets, target, target_out);
-}
-
-void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
-                         std::ostream& target_out) {
-  constexpr const char* friendly_name{"viewcompiler assets"};
-  auto assets = android::ApkAssets::LoadFromFd(std::move(fd), friendly_name);
-  CompileApkAssetsLayouts(assets, target, target_out);
-}
-
-}  // namespace startop
diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h
deleted file mode 100644
index 03bd545..0000000
--- a/startop/view_compiler/apk_layout_compiler.h
+++ /dev/null
@@ -1,35 +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.
- */
-
-#ifndef APK_LAYOUT_COMPILER_H_
-#define APK_LAYOUT_COMPILER_H_
-
-#include <string>
-
-#include "android-base/unique_fd.h"
-
-namespace startop {
-
-enum class CompilationTarget { kJavaLanguage, kDex };
-
-void CompileApkLayouts(const std::string& filename, CompilationTarget target,
-                       std::ostream& target_out);
-void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
-                         std::ostream& target_out);
-
-}  // namespace startop
-
-#endif  // APK_LAYOUT_COMPILER_H_
\ No newline at end of file
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
deleted file mode 100644
index 50cf5a50..0000000
--- a/startop/view_compiler/dex_builder.cc
+++ /dev/null
@@ -1,704 +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.
- */
-
-#include "dex_builder.h"
-
-#include <fstream>
-#include <memory>
-
-namespace startop {
-namespace dex {
-
-using std::shared_ptr;
-using std::string;
-
-using ::dex::kAccPublic;
-using Op = Instruction::Op;
-
-const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; };
-const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; };
-
-namespace {
-// From https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
-constexpr uint8_t kDexFileMagic[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00};
-
-// Strings lengths can be 32 bits long, but encoded as LEB128 this can take up to five bytes.
-constexpr size_t kMaxEncodedStringLength{5};
-
-// Converts invoke-* to invoke-*/range
-constexpr ::dex::Opcode InvokeToInvokeRange(::dex::Opcode opcode) {
-  switch (opcode) {
-    case ::dex::Opcode::OP_INVOKE_VIRTUAL:
-      return ::dex::Opcode::OP_INVOKE_VIRTUAL_RANGE;
-    case ::dex::Opcode::OP_INVOKE_DIRECT:
-      return ::dex::Opcode::OP_INVOKE_DIRECT_RANGE;
-    case ::dex::Opcode::OP_INVOKE_STATIC:
-      return ::dex::Opcode::OP_INVOKE_STATIC_RANGE;
-    case ::dex::Opcode::OP_INVOKE_INTERFACE:
-      return ::dex::Opcode::OP_INVOKE_INTERFACE_RANGE;
-    default:
-      LOG(FATAL) << opcode << " is not a recognized invoke opcode.";
-      __builtin_unreachable();
-  }
-}
-
-std::string DotToDescriptor(const char* class_name) {
-  std::string descriptor(class_name);
-  std::replace(descriptor.begin(), descriptor.end(), '.', '/');
-  if (descriptor.length() > 0 && descriptor[0] != '[') {
-    descriptor = "L" + descriptor + ";";
-  }
-  return descriptor;
-}
-
-}  // namespace
-
-std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
-  switch (opcode) {
-    case Instruction::Op::kReturn:
-      out << "kReturn";
-      return out;
-    case Instruction::Op::kReturnObject:
-      out << "kReturnObject";
-      return out;
-    case Instruction::Op::kMove:
-      out << "kMove";
-      return out;
-    case Instruction::Op::kMoveObject:
-      out << "kMoveObject";
-      return out;
-    case Instruction::Op::kInvokeVirtual:
-      out << "kInvokeVirtual";
-      return out;
-    case Instruction::Op::kInvokeDirect:
-      out << "kInvokeDirect";
-      return out;
-    case Instruction::Op::kInvokeStatic:
-      out << "kInvokeStatic";
-      return out;
-    case Instruction::Op::kInvokeInterface:
-      out << "kInvokeInterface";
-      return out;
-    case Instruction::Op::kBindLabel:
-      out << "kBindLabel";
-      return out;
-    case Instruction::Op::kBranchEqz:
-      out << "kBranchEqz";
-      return out;
-    case Instruction::Op::kBranchNEqz:
-      out << "kBranchNEqz";
-      return out;
-    case Instruction::Op::kNew:
-      out << "kNew";
-      return out;
-    case Instruction::Op::kCheckCast:
-      out << "kCheckCast";
-      return out;
-    case Instruction::Op::kGetStaticField:
-      out << "kGetStaticField";
-      return out;
-    case Instruction::Op::kSetStaticField:
-      out << "kSetStaticField";
-      return out;
-    case Instruction::Op::kGetInstanceField:
-      out << "kGetInstanceField";
-      return out;
-    case Instruction::Op::kSetInstanceField:
-      out << "kSetInstanceField";
-      return out;
-  }
-}
-
-std::ostream& operator<<(std::ostream& out, const Value& value) {
-  if (value.is_register()) {
-    out << "Register(" << value.value() << ")";
-  } else if (value.is_parameter()) {
-    out << "Parameter(" << value.value() << ")";
-  } else if (value.is_immediate()) {
-    out << "Immediate(" << value.value() << ")";
-  } else if (value.is_string()) {
-    out << "String(" << value.value() << ")";
-  } else if (value.is_label()) {
-    out << "Label(" << value.value() << ")";
-  } else if (value.is_type()) {
-    out << "Type(" << value.value() << ")";
-  } else {
-    out << "UnknownValue";
-  }
-  return out;
-}
-
-void* TrackingAllocator::Allocate(size_t size) {
-  std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
-  void* raw_buffer = buffer.get();
-  allocations_[raw_buffer] = std::move(buffer);
-  return raw_buffer;
-}
-
-void TrackingAllocator::Free(void* ptr) { allocations_.erase(allocations_.find(ptr)); }
-
-// Write out a DEX file that is basically:
-//
-// package dextest;
-// public class DexTest {
-//     public static int foo(String s) { return s.length(); }
-// }
-void WriteTestDexFile(const string& filename) {
-  DexBuilder dex_file;
-
-  ClassBuilder cbuilder{dex_file.MakeClass("dextest.DexTest")};
-  cbuilder.set_source_file("dextest.java");
-
-  TypeDescriptor string_type = TypeDescriptor::FromClassname("java.lang.String");
-
-  MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
-
-  LiveRegister result = method.AllocRegister();
-
-  MethodDeclData string_length =
-      dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
-
-  method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
-  method.BuildReturn(result);
-
-  method.Encode();
-
-  slicer::MemView image{dex_file.CreateImage()};
-
-  std::ofstream out_file(filename);
-  out_file.write(image.ptr<const char>(), image.size());
-}
-
-TypeDescriptor TypeDescriptor::FromClassname(const std::string& name) {
-  return TypeDescriptor{DotToDescriptor(name.c_str())};
-}
-
-DexBuilder::DexBuilder() : dex_file_{std::make_shared<ir::DexFile>()} {
-  dex_file_->magic = slicer::MemView{kDexFileMagic, sizeof(kDexFileMagic)};
-}
-
-slicer::MemView DexBuilder::CreateImage() {
-  ::dex::Writer writer(dex_file_);
-  size_t image_size{0};
-  ::dex::u1* image = writer.CreateImage(&allocator_, &image_size);
-  return slicer::MemView{image, image_size};
-}
-
-ir::String* DexBuilder::GetOrAddString(const std::string& string) {
-  ir::String*& entry = strings_[string];
-
-  if (entry == nullptr) {
-    // Need to encode the length and then write out the bytes, including 1 byte for null terminator
-    auto buffer = std::make_unique<uint8_t[]>(string.size() + kMaxEncodedStringLength + 1);
-    uint8_t* string_data_start = ::dex::WriteULeb128(buffer.get(), string.size());
-
-    size_t header_length =
-        reinterpret_cast<uintptr_t>(string_data_start) - reinterpret_cast<uintptr_t>(buffer.get());
-
-    auto end = std::copy(string.begin(), string.end(), string_data_start);
-    *end = '\0';
-
-    entry = Alloc<ir::String>();
-    // +1 for null terminator
-    entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
-    ::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex();
-    dex_file_->strings_map[new_index] = entry;
-    entry->orig_index = new_index;
-    string_data_.push_back(std::move(buffer));
-  }
-  return entry;
-}
-
-ClassBuilder DexBuilder::MakeClass(const std::string& name) {
-  auto* class_def = Alloc<ir::Class>();
-  ir::Type* type_def = GetOrAddType(DotToDescriptor(name.c_str()));
-  type_def->class_def = class_def;
-
-  class_def->type = type_def;
-  class_def->super_class = GetOrAddType(DotToDescriptor("java.lang.Object"));
-  class_def->access_flags = kAccPublic;
-  return ClassBuilder{this, name, class_def};
-}
-
-ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
-  if (types_by_descriptor_.find(descriptor) != types_by_descriptor_.end()) {
-    return types_by_descriptor_[descriptor];
-  }
-
-  ir::Type* type = Alloc<ir::Type>();
-  type->descriptor = GetOrAddString(descriptor);
-  types_by_descriptor_[descriptor] = type;
-  type->orig_index = dex_file_->types_indexes.AllocateIndex();
-  dex_file_->types_map[type->orig_index] = type;
-  return type;
-}
-
-ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name,
-                                         TypeDescriptor type) {
-  const auto key = std::make_tuple(parent, name);
-  if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) {
-    return field_decls_by_key_[key];
-  }
-
-  ir::FieldDecl* field = Alloc<ir::FieldDecl>();
-  field->parent = GetOrAddType(parent);
-  field->name = GetOrAddString(name);
-  field->type = GetOrAddType(type);
-  field->orig_index = dex_file_->fields_indexes.AllocateIndex();
-  dex_file_->fields_map[field->orig_index] = field;
-  field_decls_by_key_[key] = field;
-  return field;
-}
-
-ir::Proto* Prototype::Encode(DexBuilder* dex) const {
-  auto* proto = dex->Alloc<ir::Proto>();
-  proto->shorty = dex->GetOrAddString(Shorty());
-  proto->return_type = dex->GetOrAddType(return_type_.descriptor());
-  if (param_types_.size() > 0) {
-    proto->param_types = dex->Alloc<ir::TypeList>();
-    for (const auto& param_type : param_types_) {
-      proto->param_types->types.push_back(dex->GetOrAddType(param_type.descriptor()));
-    }
-  } else {
-    proto->param_types = nullptr;
-  }
-  return proto;
-}
-
-std::string Prototype::Shorty() const {
-  std::string shorty;
-  shorty.append(return_type_.short_descriptor());
-  for (const auto& type_descriptor : param_types_) {
-    shorty.append(type_descriptor.short_descriptor());
-  }
-  return shorty;
-}
-
-const TypeDescriptor& Prototype::ArgType(size_t index) const {
-  CHECK_LT(index, param_types_.size());
-  return param_types_[index];
-}
-
-ClassBuilder::ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def)
-    : parent_(parent), type_descriptor_{TypeDescriptor::FromClassname(name)}, class_(class_def) {}
-
-MethodBuilder ClassBuilder::CreateMethod(const std::string& name, Prototype prototype) {
-  ir::MethodDecl* decl = parent_->GetOrDeclareMethod(type_descriptor_, name, prototype).decl;
-
-  return MethodBuilder{parent_, class_, decl};
-}
-
-void ClassBuilder::set_source_file(const string& source) {
-  class_->source_file = parent_->GetOrAddString(source);
-}
-
-MethodBuilder::MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl)
-    : dex_{dex}, class_{class_def}, decl_{decl} {}
-
-ir::EncodedMethod* MethodBuilder::Encode() {
-  auto* method = dex_->Alloc<ir::EncodedMethod>();
-  method->decl = decl_;
-
-  // TODO: make access flags configurable
-  method->access_flags = kAccPublic | ::dex::kAccStatic;
-
-  auto* code = dex_->Alloc<ir::Code>();
-  CHECK(decl_->prototype != nullptr);
-  size_t const num_args =
-      decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
-  code->registers = NumRegisters() + num_args + kMaxScratchRegisters;
-  code->ins_count = num_args;
-  EncodeInstructions();
-  code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
-  size_t const return_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
-  code->outs_count = std::max(return_count, max_args_);
-  method->code = code;
-
-  class_->direct_methods.push_back(method);
-
-  return method;
-}
-
-LiveRegister MethodBuilder::AllocRegister() {
-  // Find a free register
-  for (size_t i = 0; i < register_liveness_.size(); ++i) {
-    if (!register_liveness_[i]) {
-      register_liveness_[i] = true;
-      return LiveRegister{&register_liveness_, i};
-    }
-  }
-
-  // If we get here, all the registers are in use, so we have to allocate a new
-  // one.
-  register_liveness_.push_back(true);
-  return LiveRegister{&register_liveness_, register_liveness_.size() - 1};
-}
-
-Value MethodBuilder::MakeLabel() {
-  labels_.push_back({});
-  return Value::Label(labels_.size() - 1);
-}
-
-void MethodBuilder::AddInstruction(Instruction instruction) {
-  instructions_.push_back(instruction);
-}
-
-void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
-
-void MethodBuilder::BuildReturn(Value src, bool is_object) {
-  AddInstruction(Instruction::OpWithArgs(
-      is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src));
-}
-
-void MethodBuilder::BuildConst4(Value target, int value) {
-  CHECK_LT(value, 16);
-  AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
-}
-
-void MethodBuilder::BuildConstString(Value target, const std::string& value) {
-  const ir::String* const dex_string = dex_->GetOrAddString(value);
-  AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index)));
-}
-
-void MethodBuilder::EncodeInstructions() {
-  buffer_.clear();
-  for (const auto& instruction : instructions_) {
-    EncodeInstruction(instruction);
-  }
-}
-
-void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
-  switch (instruction.opcode()) {
-    case Instruction::Op::kReturn:
-      return EncodeReturn(instruction, ::dex::Opcode::OP_RETURN);
-    case Instruction::Op::kReturnObject:
-      return EncodeReturn(instruction, ::dex::Opcode::OP_RETURN_OBJECT);
-    case Instruction::Op::kMove:
-    case Instruction::Op::kMoveObject:
-      return EncodeMove(instruction);
-    case Instruction::Op::kInvokeVirtual:
-      return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_VIRTUAL);
-    case Instruction::Op::kInvokeDirect:
-      return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_DIRECT);
-    case Instruction::Op::kInvokeStatic:
-      return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_STATIC);
-    case Instruction::Op::kInvokeInterface:
-      return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_INTERFACE);
-    case Instruction::Op::kBindLabel:
-      return BindLabel(instruction.args()[0]);
-    case Instruction::Op::kBranchEqz:
-      return EncodeBranch(::dex::Opcode::OP_IF_EQZ, instruction);
-    case Instruction::Op::kBranchNEqz:
-      return EncodeBranch(::dex::Opcode::OP_IF_NEZ, instruction);
-    case Instruction::Op::kNew:
-      return EncodeNew(instruction);
-    case Instruction::Op::kCheckCast:
-      return EncodeCast(instruction);
-    case Instruction::Op::kGetStaticField:
-    case Instruction::Op::kSetStaticField:
-    case Instruction::Op::kGetInstanceField:
-    case Instruction::Op::kSetInstanceField:
-      return EncodeFieldOp(instruction);
-  }
-}
-
-void MethodBuilder::EncodeReturn(const Instruction& instruction, ::dex::Opcode opcode) {
-  CHECK(!instruction.dest().has_value());
-  if (instruction.args().size() == 0) {
-    Encode10x(::dex::Opcode::OP_RETURN_VOID);
-  } else {
-    CHECK_EQ(1, instruction.args().size());
-    size_t source = RegisterValue(instruction.args()[0]);
-    Encode11x(opcode, source);
-  }
-}
-
-void MethodBuilder::EncodeMove(const Instruction& instruction) {
-  CHECK(Instruction::Op::kMove == instruction.opcode() ||
-        Instruction::Op::kMoveObject == instruction.opcode());
-  CHECK(instruction.dest().has_value());
-  CHECK(instruction.dest()->is_variable());
-  CHECK_EQ(1, instruction.args().size());
-
-  const Value& source = instruction.args()[0];
-
-  if (source.is_immediate()) {
-    // TODO: support more registers
-    CHECK_LT(RegisterValue(*instruction.dest()), 16);
-    Encode11n(::dex::Opcode::OP_CONST_4, RegisterValue(*instruction.dest()), source.value());
-  } else if (source.is_string()) {
-    constexpr size_t kMaxRegisters = 256;
-    CHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
-    CHECK_LT(source.value(), 65536);  // make sure we don't need a jumbo string
-    Encode21c(::dex::Opcode::OP_CONST_STRING, RegisterValue(*instruction.dest()), source.value());
-  } else if (source.is_variable()) {
-    // For the moment, we only use this when we need to reshuffle registers for
-    // an invoke instruction, meaning we are too big for the 4-bit version.
-    // We'll err on the side of caution and always generate the 16-bit form of
-    // the instruction.
-    auto opcode = instruction.opcode() == Instruction::Op::kMove
-                        ? ::dex::Opcode::OP_MOVE_16
-                        : ::dex::Opcode::OP_MOVE_OBJECT_16;
-    Encode32x(opcode, RegisterValue(*instruction.dest()), RegisterValue(source));
-  } else {
-    UNIMPLEMENTED(FATAL);
-  }
-}
-
-void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::dex::Opcode opcode) {
-  constexpr size_t kMaxArgs = 5;
-
-  // Currently, we only support up to 5 arguments.
-  CHECK_LE(instruction.args().size(), kMaxArgs);
-
-  uint8_t arguments[kMaxArgs]{};
-  bool has_long_args = false;
-  for (size_t i = 0; i < instruction.args().size(); ++i) {
-    CHECK(instruction.args()[i].is_variable());
-    arguments[i] = RegisterValue(instruction.args()[i]);
-    if (!IsShortRegister(arguments[i])) {
-      has_long_args = true;
-    }
-  }
-
-  if (has_long_args) {
-    // Some of the registers don't fit in the four bit short form of the invoke
-    // instruction, so we need to do an invoke/range. To do this, we need to
-    // first move all the arguments into contiguous temporary registers.
-    std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
-
-    const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument());
-    CHECK(prototype.has_value());
-
-    for (size_t i = 0; i < instruction.args().size(); ++i) {
-      Instruction::Op move_op;
-      if (opcode == ::dex::Opcode::OP_INVOKE_VIRTUAL ||
-          opcode == ::dex::Opcode::OP_INVOKE_DIRECT) {
-        // In this case, there is an implicit `this` argument, which is always an object.
-        if (i == 0) {
-          move_op = Instruction::Op::kMoveObject;
-        } else {
-          move_op = prototype->ArgType(i - 1).is_object() ? Instruction::Op::kMoveObject
-                                                          : Instruction::Op::kMove;
-        }
-      } else {
-        move_op = prototype->ArgType(i).is_object() ? Instruction::Op::kMoveObject
-                                                    : Instruction::Op::kMove;
-      }
-
-      EncodeMove(Instruction::OpWithArgs(move_op, scratch[i], instruction.args()[i]));
-    }
-
-    Encode3rc(InvokeToInvokeRange(opcode),
-              instruction.args().size(),
-              instruction.index_argument(),
-              RegisterValue(scratch[0]));
-  } else {
-    Encode35c(opcode,
-              instruction.args().size(),
-              instruction.index_argument(),
-              arguments[0],
-              arguments[1],
-              arguments[2],
-              arguments[3],
-              arguments[4]);
-  }
-
-  // If there is a return value, add a move-result instruction
-  if (instruction.dest().has_value()) {
-    Encode11x(instruction.result_is_object() ? ::dex::Opcode::OP_MOVE_RESULT_OBJECT
-                                             : ::dex::Opcode::OP_MOVE_RESULT,
-              RegisterValue(*instruction.dest()));
-  }
-
-  max_args_ = std::max(max_args_, instruction.args().size());
-}
-
-// Encodes a conditional branch that tests a single argument.
-void MethodBuilder::EncodeBranch(::dex::Opcode op, const Instruction& instruction) {
-  const auto& args = instruction.args();
-  const auto& test_value = args[0];
-  const auto& branch_target = args[1];
-  CHECK_EQ(2, args.size());
-  CHECK(test_value.is_variable());
-  CHECK(branch_target.is_label());
-
-  size_t instruction_offset = buffer_.size();
-  size_t field_offset = buffer_.size() + 1;
-  Encode21c(
-      op, RegisterValue(test_value), LabelValue(branch_target, instruction_offset, field_offset));
-}
-
-void MethodBuilder::EncodeNew(const Instruction& instruction) {
-  CHECK_EQ(Instruction::Op::kNew, instruction.opcode());
-  CHECK(instruction.dest().has_value());
-  CHECK(instruction.dest()->is_variable());
-  CHECK_EQ(1, instruction.args().size());
-
-  const Value& type = instruction.args()[0];
-  CHECK_LT(RegisterValue(*instruction.dest()), 256);
-  CHECK(type.is_type());
-  Encode21c(::dex::Opcode::OP_NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
-}
-
-void MethodBuilder::EncodeCast(const Instruction& instruction) {
-  CHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
-  CHECK(instruction.dest().has_value());
-  CHECK(instruction.dest()->is_variable());
-  CHECK_EQ(1, instruction.args().size());
-
-  const Value& type = instruction.args()[0];
-  CHECK_LT(RegisterValue(*instruction.dest()), 256);
-  CHECK(type.is_type());
-  Encode21c(::dex::Opcode::OP_CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
-}
-
-void MethodBuilder::EncodeFieldOp(const Instruction& instruction) {
-  const auto& args = instruction.args();
-  switch (instruction.opcode()) {
-    case Instruction::Op::kGetStaticField: {
-      CHECK(instruction.dest().has_value());
-      CHECK(instruction.dest()->is_variable());
-      CHECK_EQ(0, instruction.args().size());
-
-      Encode21c(::dex::Opcode::OP_SGET,
-                RegisterValue(*instruction.dest()),
-                instruction.index_argument());
-      break;
-    }
-    case Instruction::Op::kSetStaticField: {
-      CHECK(!instruction.dest().has_value());
-      CHECK_EQ(1, args.size());
-      CHECK(args[0].is_variable());
-
-      Encode21c(::dex::Opcode::OP_SPUT, RegisterValue(args[0]), instruction.index_argument());
-      break;
-    }
-    case Instruction::Op::kGetInstanceField: {
-      CHECK(instruction.dest().has_value());
-      CHECK(instruction.dest()->is_variable());
-      CHECK_EQ(1, instruction.args().size());
-
-      Encode22c(::dex::Opcode::OP_IGET,
-                RegisterValue(*instruction.dest()),
-                RegisterValue(args[0]),
-                instruction.index_argument());
-      break;
-    }
-    case Instruction::Op::kSetInstanceField: {
-      CHECK(!instruction.dest().has_value());
-      CHECK_EQ(2, args.size());
-      CHECK(args[0].is_variable());
-      CHECK(args[1].is_variable());
-
-      Encode22c(::dex::Opcode::OP_IPUT,
-                RegisterValue(args[1]),
-                RegisterValue(args[0]),
-                instruction.index_argument());
-      break;
-    }
-    default: { LOG(FATAL) << "Unsupported field operation"; }
-  }
-}
-
-size_t MethodBuilder::RegisterValue(const Value& value) const {
-  if (value.is_register()) {
-    return value.value();
-  } else if (value.is_parameter()) {
-    return value.value() + NumRegisters() + kMaxScratchRegisters;
-  }
-  CHECK(false && "Must be either a parameter or a register");
-  return 0;
-}
-
-void MethodBuilder::BindLabel(const Value& label_id) {
-  CHECK(label_id.is_label());
-
-  LabelData& label = labels_[label_id.value()];
-  CHECK(!label.bound_address.has_value());
-
-  label.bound_address = buffer_.size();
-
-  // patch any forward references to this label.
-  for (const auto& ref : label.references) {
-    buffer_[ref.field_offset] = *label.bound_address - ref.instruction_offset;
-  }
-  // No point keeping these around anymore.
-  label.references.clear();
-}
-
-::dex::u2 MethodBuilder::LabelValue(const Value& label_id, size_t instruction_offset,
-                                    size_t field_offset) {
-  CHECK(label_id.is_label());
-  LabelData& label = labels_[label_id.value()];
-
-  // Short-circuit if the label is already bound.
-  if (label.bound_address.has_value()) {
-    return *label.bound_address - instruction_offset;
-  }
-
-  // Otherwise, save a reference to where we need to back-patch later.
-  label.references.push_front(LabelReference{instruction_offset, field_offset});
-  return 0;
-}
-
-const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
-                                                     Prototype prototype) {
-  MethodDeclData& entry = method_id_map_[{type, name, prototype}];
-
-  if (entry.decl == nullptr) {
-    // This method has not already been declared, so declare it.
-    ir::MethodDecl* decl = dex_file_->Alloc<ir::MethodDecl>();
-    // The method id is the last added method.
-    size_t id = dex_file_->methods.size() - 1;
-
-    ir::String* dex_name{GetOrAddString(name)};
-    decl->name = dex_name;
-    decl->parent = GetOrAddType(type.descriptor());
-    decl->prototype = GetOrEncodeProto(prototype);
-
-    // update the index -> ir node map (see tools/dexter/slicer/dex_ir_builder.cc)
-    auto new_index = dex_file_->methods_indexes.AllocateIndex();
-    auto& ir_node = dex_file_->methods_map[new_index];
-    CHECK(ir_node == nullptr);
-    ir_node = decl;
-    decl->orig_index = decl->index = new_index;
-
-    entry = {id, decl};
-  }
-
-  return entry;
-}
-
-std::optional<const Prototype> DexBuilder::GetPrototypeByMethodId(size_t method_id) const {
-  for (const auto& entry : method_id_map_) {
-    if (entry.second.id == method_id) {
-      return entry.first.prototype;
-    }
-  }
-  return {};
-}
-
-ir::Proto* DexBuilder::GetOrEncodeProto(Prototype prototype) {
-  ir::Proto*& ir_proto = proto_map_[prototype];
-  if (ir_proto == nullptr) {
-    ir_proto = prototype.Encode(this);
-  }
-  return ir_proto;
-}
-
-}  // namespace dex
-}  // namespace startop
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
deleted file mode 100644
index eb2dc88..0000000
--- a/startop/view_compiler/dex_builder.h
+++ /dev/null
@@ -1,626 +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.
- */
-#ifndef DEX_BUILDER_H_
-#define DEX_BUILDER_H_
-
-#include <array>
-#include <forward_list>
-#include <map>
-#include <optional>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "android-base/logging.h"
-
-#include "slicer/dex_bytecode.h"
-#include "slicer/dex_ir.h"
-#include "slicer/writer.h"
-
-namespace startop {
-namespace dex {
-
-// TODO: remove this once the dex generation code is complete.
-void WriteTestDexFile(const std::string& filename);
-
-//////////////////////////
-// Forward declarations //
-//////////////////////////
-class DexBuilder;
-
-// Our custom allocator for dex::Writer
-//
-// This keeps track of all allocations and ensures they are freed when
-// TrackingAllocator is destroyed. Pointers to memory allocated by this
-// allocator must not outlive the allocator.
-class TrackingAllocator : public ::dex::Writer::Allocator {
- public:
-  virtual void* Allocate(size_t size);
-  virtual void Free(void* ptr);
-
- private:
-  std::unordered_map<void*, std::unique_ptr<uint8_t[]>> allocations_;
-};
-
-// Represents a DEX type descriptor.
-//
-// TODO: add a way to create a descriptor for a reference of a class type.
-class TypeDescriptor {
- public:
-  // Named constructors for base type descriptors.
-  static const TypeDescriptor Int();
-  static const TypeDescriptor Void();
-
-  // Creates a type descriptor from a fully-qualified class name. For example, it turns the class
-  // name java.lang.Object into the descriptor Ljava/lang/Object.
-  static TypeDescriptor FromClassname(const std::string& name);
-
-  // Return the full descriptor, such as I or Ljava/lang/Object
-  const std::string& descriptor() const { return descriptor_; }
-  // Return the shorty descriptor, such as I or L
-  std::string short_descriptor() const { return descriptor().substr(0, 1); }
-
-  bool is_object() const { return short_descriptor() == "L"; }
-
-  bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; }
-
- private:
-  explicit TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
-
-  const std::string descriptor_;
-};
-
-// Defines a function signature. For example, Prototype{TypeDescriptor::VOID, TypeDescriptor::Int}
-// represents the function type (Int) -> Void.
-class Prototype {
- public:
-  template <typename... TypeDescriptors>
-  explicit Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
-      : return_type_{return_type}, param_types_{param_types...} {}
-
-  // Encode this prototype into the dex file.
-  ir::Proto* Encode(DexBuilder* dex) const;
-
-  // Get the shorty descriptor, such as VII for (Int, Int) -> Void
-  std::string Shorty() const;
-
-  const TypeDescriptor& ArgType(size_t index) const;
-
-  bool operator<(const Prototype& rhs) const {
-    return std::make_tuple(return_type_, param_types_) <
-           std::make_tuple(rhs.return_type_, rhs.param_types_);
-  }
-
- private:
-  const TypeDescriptor return_type_;
-  const std::vector<TypeDescriptor> param_types_;
-};
-
-// Represents a DEX register or constant. We separate regular registers and parameters
-// because we will not know the real parameter id until after all instructions
-// have been generated.
-class Value {
- public:
-  static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
-  static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
-  static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
-  static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
-  static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
-  static constexpr Value Type(size_t id) { return Value{id, Kind::kType}; }
-
-  bool is_register() const { return kind_ == Kind::kLocalRegister; }
-  bool is_parameter() const { return kind_ == Kind::kParameter; }
-  bool is_variable() const { return is_register() || is_parameter(); }
-  bool is_immediate() const { return kind_ == Kind::kImmediate; }
-  bool is_string() const { return kind_ == Kind::kString; }
-  bool is_label() const { return kind_ == Kind::kLabel; }
-  bool is_type() const { return kind_ == Kind::kType; }
-
-  size_t value() const { return value_; }
-
-  constexpr Value() : value_{0}, kind_{Kind::kInvalid} {}
-
- private:
-  enum class Kind { kInvalid, kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
-
-  size_t value_;
-  Kind kind_;
-
-  constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
-};
-
-// Represents an allocated register returned by MethodBuilder::AllocRegister
-class LiveRegister {
-  friend class MethodBuilder;
-
- public:
-  LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} {
-    other.index_ = {};
-  };
-  ~LiveRegister() {
-    if (index_.has_value()) {
-      (*liveness_)[*index_] = false;
-    }
-  };
-
-  operator const Value() const { return Value::Local(*index_); }
-
- private:
-  LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {}
-
-  std::vector<bool>* const liveness_;
-  std::optional<size_t> index_;
-};
-
-// A virtual instruction. We convert these to real instructions in MethodBuilder::Encode.
-// Virtual instructions are needed to keep track of information that is not known until all of the
-// code is generated. This information includes things like how many local registers are created and
-// branch target locations.
-class Instruction {
- public:
-  // The operation performed by this instruction. These are virtual instructions that do not
-  // correspond exactly to DEX instructions.
-  enum class Op {
-    kBindLabel,
-    kBranchEqz,
-    kBranchNEqz,
-    kCheckCast,
-    kGetInstanceField,
-    kGetStaticField,
-    kInvokeDirect,
-    kInvokeInterface,
-    kInvokeStatic,
-    kInvokeVirtual,
-    kMove,
-    kMoveObject,
-    kNew,
-    kReturn,
-    kReturnObject,
-    kSetInstanceField,
-    kSetStaticField
-  };
-
-  ////////////////////////
-  // Named Constructors //
-  ////////////////////////
-
-  // For instructions with no return value and no arguments.
-  static inline Instruction OpNoArgs(Op opcode) {
-    return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}};
-  }
-  // For most instructions, which take some number of arguments and have an optional return value.
-  template <typename... T>
-  static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest,
-                                       const T&... args) {
-    return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
-  }
-
-  // A cast instruction. Basically, `(type)val`
-  static inline Instruction Cast(Value val, Value type) {
-    CHECK(type.is_type());
-    return OpWithArgs(Op::kCheckCast, val, type);
-  }
-
-  // For method calls.
-  template <typename... T>
-  static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest,
-                                          Value this_arg, T... args) {
-    return Instruction{
-        Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
-  }
-  // Returns an object
-  template <typename... T>
-  static inline Instruction InvokeVirtualObject(size_t index_argument,
-                                                std::optional<const Value> dest, Value this_arg,
-                                                const T&... args) {
-    return Instruction{
-        Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
-  }
-  // For direct calls (basically, constructors).
-  template <typename... T>
-  static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
-                                         Value this_arg, const T&... args) {
-    return Instruction{
-        Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
-  }
-  // Returns an object
-  template <typename... T>
-  static inline Instruction InvokeDirectObject(size_t index_argument,
-                                               std::optional<const Value> dest, Value this_arg,
-                                               T... args) {
-    return Instruction{
-        Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
-  }
-  // For static calls.
-  template <typename... T>
-  static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
-                                         T... args) {
-    return Instruction{
-        Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
-  }
-  // Returns an object
-  template <typename... T>
-  static inline Instruction InvokeStaticObject(size_t index_argument,
-                                               std::optional<const Value> dest, T... args) {
-    return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
-  }
-  // For static calls.
-  template <typename... T>
-  static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
-                                            const T&... args) {
-    return Instruction{
-        Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
-  }
-
-  static inline Instruction GetStaticField(size_t field_id, Value dest) {
-    return Instruction{Op::kGetStaticField, field_id, dest};
-  }
-
-  static inline Instruction SetStaticField(size_t field_id, Value value) {
-    return Instruction{
-        Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
-  }
-
-  static inline Instruction GetField(size_t field_id, Value dest, Value object) {
-    return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object};
-  }
-
-  static inline Instruction SetField(size_t field_id, Value object, Value value) {
-    return Instruction{
-        Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value};
-  }
-
-  ///////////////
-  // Accessors //
-  ///////////////
-
-  Op opcode() const { return opcode_; }
-  size_t index_argument() const { return index_argument_; }
-  bool result_is_object() const { return result_is_object_; }
-  const std::optional<const Value>& dest() const { return dest_; }
-  const std::vector<const Value>& args() const { return args_; }
-
- private:
-  inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
-      : opcode_{opcode},
-        index_argument_{index_argument},
-        result_is_object_{false},
-        dest_{dest},
-        args_{} {}
-
-  template <typename... T>
-  inline Instruction(Op opcode, size_t index_argument, bool result_is_object,
-                     std::optional<const Value> dest, const T&... args)
-      : opcode_{opcode},
-        index_argument_{index_argument},
-        result_is_object_{result_is_object},
-        dest_{dest},
-        args_{args...} {}
-
-  const Op opcode_;
-  // The index of the method to invoke, for kInvokeVirtual and similar opcodes.
-  const size_t index_argument_{0};
-  const bool result_is_object_;
-  const std::optional<const Value> dest_;
-  const std::vector<const Value> args_;
-};
-
-// Needed for CHECK_EQ, DCHECK_EQ, etc.
-std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode);
-
-// Keeps track of information needed to manipulate or call a method.
-struct MethodDeclData {
-  size_t id;
-  ir::MethodDecl* decl;
-};
-
-// Tools to help build methods and their bodies.
-class MethodBuilder {
- public:
-  MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl);
-
-  // Encode the method into DEX format.
-  ir::EncodedMethod* Encode();
-
-  // Create a new register to be used to storing values.
-  LiveRegister AllocRegister();
-
-  Value MakeLabel();
-
-  /////////////////////////////////
-  // Instruction builder methods //
-  /////////////////////////////////
-
-  void AddInstruction(Instruction instruction);
-
-  // return-void
-  void BuildReturn();
-  void BuildReturn(Value src, bool is_object = false);
-  // const/4
-  void BuildConst4(Value target, int value);
-  void BuildConstString(Value target, const std::string& value);
-  template <typename... T>
-  void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args);
-
-  // TODO: add builders for more instructions
-
-  DexBuilder* dex_file() const { return dex_; }
-
- private:
-  void EncodeInstructions();
-  void EncodeInstruction(const Instruction& instruction);
-
-  // Encodes a return instruction. For instructions with no return value, the opcode field is
-  // ignored. Otherwise, this specifies which return instruction will be used (return,
-  // return-object, etc.)
-  void EncodeReturn(const Instruction& instruction, ::dex::Opcode opcode);
-
-  void EncodeMove(const Instruction& instruction);
-  void EncodeInvoke(const Instruction& instruction, ::dex::Opcode opcode);
-  void EncodeBranch(::dex::Opcode op, const Instruction& instruction);
-  void EncodeNew(const Instruction& instruction);
-  void EncodeCast(const Instruction& instruction);
-  void EncodeFieldOp(const Instruction& instruction);
-
-  // Low-level instruction format encoding. See
-  // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
-  // formats.
-
-  inline uint8_t ToBits(::dex::Opcode opcode) {
-    static_assert(sizeof(uint8_t) == sizeof(::dex::Opcode));
-    return static_cast<uint8_t>(opcode);
-  }
-
-  inline void Encode10x(::dex::Opcode opcode) {
-    // 00|op
-    static_assert(sizeof(uint8_t) == sizeof(::dex::Opcode));
-    buffer_.push_back(ToBits(opcode));
-  }
-
-  inline void Encode11x(::dex::Opcode opcode, uint8_t a) {
-    // aa|op
-    buffer_.push_back((a << 8) | ToBits(opcode));
-  }
-
-  inline void Encode11n(::dex::Opcode opcode, uint8_t a, int8_t b) {
-    // b|a|op
-
-    // Make sure the fields are in bounds (4 bits for a, 4 bits for b).
-    CHECK_LT(a, 16);
-    CHECK_LE(-8, b);
-    CHECK_LT(b, 8);
-
-    buffer_.push_back(((b & 0xf) << 12) | (a << 8) | ToBits(opcode));
-  }
-
-  inline void Encode21c(::dex::Opcode opcode, uint8_t a, uint16_t b) {
-    // aa|op|bbbb
-    buffer_.push_back((a << 8) | ToBits(opcode));
-    buffer_.push_back(b);
-  }
-
-  inline void Encode22c(::dex::Opcode opcode, uint8_t a, uint8_t b, uint16_t c) {
-    // b|a|op|bbbb
-    CHECK(IsShortRegister(a));
-    CHECK(IsShortRegister(b));
-    buffer_.push_back((b << 12) | (a << 8) | ToBits(opcode));
-    buffer_.push_back(c);
-  }
-
-  inline void Encode32x(::dex::Opcode opcode, uint16_t a, uint16_t b) {
-    buffer_.push_back(ToBits(opcode));
-    buffer_.push_back(a);
-    buffer_.push_back(b);
-  }
-
-  inline void Encode35c(::dex::Opcode opcode, size_t a, uint16_t b, uint8_t c, uint8_t d,
-                        uint8_t e, uint8_t f, uint8_t g) {
-    // a|g|op|bbbb|f|e|d|c
-
-    CHECK_LE(a, 5);
-    CHECK(IsShortRegister(c));
-    CHECK(IsShortRegister(d));
-    CHECK(IsShortRegister(e));
-    CHECK(IsShortRegister(f));
-    CHECK(IsShortRegister(g));
-    buffer_.push_back((a << 12) | (g << 8) | ToBits(opcode));
-    buffer_.push_back(b);
-    buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c);
-  }
-
-  inline void Encode3rc(::dex::Opcode opcode, size_t a, uint16_t b, uint16_t c) {
-    CHECK_LE(a, 255);
-    buffer_.push_back((a << 8) | ToBits(opcode));
-    buffer_.push_back(b);
-    buffer_.push_back(c);
-  }
-
-  static constexpr bool IsShortRegister(size_t register_value) { return register_value < 16; }
-
-  // Returns an array of num_regs scratch registers. These are guaranteed to be
-  // contiguous, so they are suitable for the invoke-*/range instructions.
-  template <int num_regs>
-  std::array<Value, num_regs> GetScratchRegisters() const {
-    static_assert(num_regs <= kMaxScratchRegisters);
-    std::array<Value, num_regs> regs;
-    for (size_t i = 0; i < num_regs; ++i) {
-      regs[i] = std::move(Value::Local(NumRegisters() + i));
-    }
-    return regs;
-  }
-
-  // Converts a register or parameter to its DEX register number.
-  size_t RegisterValue(const Value& value) const;
-
-  // Sets a label's address to the current position in the instruction buffer. If there are any
-  // forward references to the label, this function will back-patch them.
-  void BindLabel(const Value& label);
-
-  // Returns the offset of the label relative to the given instruction offset. If the label is not
-  // bound, a reference will be saved and it will automatically be patched when the label is bound.
-  ::dex::u2 LabelValue(const Value& label, size_t instruction_offset, size_t field_offset);
-
-  DexBuilder* dex_;
-  ir::Class* class_;
-  ir::MethodDecl* decl_;
-
-  // A list of the instructions we will eventually encode.
-  std::vector<Instruction> instructions_;
-
-  // A buffer to hold instructions that have been encoded.
-  std::vector<::dex::u2> buffer_;
-
-  // We create some scratch registers for when we have to shuffle registers
-  // around to make legal DEX code.
-  static constexpr size_t kMaxScratchRegisters = 5;
-
-  size_t NumRegisters() const {
-    return register_liveness_.size();
-  }
-
-  // Stores information needed to back-patch a label once it is bound. We need to know the start of
-  // the instruction that refers to the label, and the offset to where the actual label value should
-  // go.
-  struct LabelReference {
-    size_t instruction_offset;
-    size_t field_offset;
-  };
-
-  struct LabelData {
-    std::optional<size_t> bound_address;
-    std::forward_list<LabelReference> references;
-  };
-
-  std::vector<LabelData> labels_;
-
-  // During encoding, keep track of the largest number of arguments needed, so we can use it for our
-  // outs count
-  size_t max_args_{0};
-
-  std::vector<bool> register_liveness_;
-};
-
-// A helper to build class definitions.
-class ClassBuilder {
- public:
-  ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def);
-
-  void set_source_file(const std::string& source);
-
-  // Create a method with the given name and prototype. The returned MethodBuilder can be used to
-  // fill in the method body.
-  MethodBuilder CreateMethod(const std::string& name, Prototype prototype);
-
- private:
-  DexBuilder* const parent_;
-  const TypeDescriptor type_descriptor_;
-  ir::Class* const class_;
-};
-
-// Builds Dex files from scratch.
-class DexBuilder {
- public:
-  DexBuilder();
-
-  // Create an in-memory image of the DEX file that can either be loaded directly or written to a
-  // file.
-  slicer::MemView CreateImage();
-
-  template <typename T>
-  T* Alloc() {
-    return dex_file_->Alloc<T>();
-  }
-
-  // Find the ir::String that matches the given string, creating it if it does not exist.
-  ir::String* GetOrAddString(const std::string& string);
-  // Create a new class of the given name.
-  ClassBuilder MakeClass(const std::string& name);
-
-  // Add a type for the given descriptor, or return the existing one if it already exists.
-  // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
-  // imported classes.
-  ir::Type* GetOrAddType(const std::string& descriptor);
-  inline ir::Type* GetOrAddType(TypeDescriptor descriptor) {
-    return GetOrAddType(descriptor.descriptor());
-  }
-
-  ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type);
-
-  // Returns the method id for the method, creating it if it has not been created yet.
-  const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
-                                           Prototype prototype);
-
-  std::optional<const Prototype> GetPrototypeByMethodId(size_t method_id) const;
-
- private:
-  // Looks up the ir::Proto* corresponding to this given prototype, or creates one if it does not
-  // exist.
-  ir::Proto* GetOrEncodeProto(Prototype prototype);
-
-  std::shared_ptr<ir::DexFile> dex_file_;
-
-  // allocator_ is needed to be able to encode the image.
-  TrackingAllocator allocator_;
-
-  // We'll need to allocate buffers for all of the encoded strings we create. This is where we store
-  // all of them.
-  std::vector<std::unique_ptr<uint8_t[]>> string_data_;
-
-  // Keep track of what types we've defined so we can look them up later.
-  std::unordered_map<std::string, ir::Type*> types_by_descriptor_;
-
-  struct MethodDescriptor {
-    TypeDescriptor type;
-    std::string name;
-    Prototype prototype;
-
-    inline bool operator<(const MethodDescriptor& rhs) const {
-      return std::make_tuple(type, name, prototype) <
-             std::make_tuple(rhs.type, rhs.name, rhs.prototype);
-    }
-  };
-
-  // Maps method declarations to their method index. This is needed to encode references to them.
-  // When we go to actually write the DEX file, slicer will re-assign these after correctly sorting
-  // the methods list.
-  std::map<MethodDescriptor, MethodDeclData> method_id_map_;
-
-  // Keep track of what strings we've defined so we can look them up later.
-  std::unordered_map<std::string, ir::String*> strings_;
-
-  // Keep track of already-encoded protos.
-  std::map<Prototype, ir::Proto*> proto_map_;
-
-  // Keep track of fields that have been declared
-  std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_;
-};
-
-template <typename... T>
-void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor,
-                             const T&... args) {
-  MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
-  // allocate the object
-  ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
-  AddInstruction(
-      Instruction::OpWithArgs(Instruction::Op::kNew, target, Value::Type(type_def->orig_index)));
-  // call the constructor
-  AddInstruction(Instruction::InvokeDirect(constructor_data.id, /*dest=*/{}, target, args...));
-};
-
-}  // namespace dex
-}  // namespace startop
-
-#endif  // DEX_BUILDER_H_
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
deleted file mode 100644
index bcba2fe..0000000
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ /dev/null
@@ -1,68 +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.
-//
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-genrule {
-    name: "generate_compiled_layout1",
-    tools: [":viewcompiler"],
-    cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
-    srcs: ["res/layout/layout1.xml"],
-    out: [
-        "layout1.dex",
-    ],
-}
-
-genrule {
-    name: "generate_compiled_layout2",
-    tools: [":viewcompiler"],
-    cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
-    srcs: ["res/layout/layout2.xml"],
-    out: [
-        "layout2.dex",
-    ],
-}
-
-android_test {
-    name: "dex-builder-test",
-    srcs: [
-        "src/android/startop/test/DexBuilderTest.java",
-        "src/android/startop/test/LayoutCompilerTest.java",
-        "src/android/startop/test/TestClass.java",
-    ],
-    sdk_version: "current",
-    data: [
-        ":generate_dex_testcases",
-        ":generate_compiled_layout1",
-        ":generate_compiled_layout2",
-    ],
-    static_libs: [
-        "androidx.test.core",
-        "androidx.test.runner",
-        "junit",
-    ],
-    manifest: "AndroidManifest.xml",
-    resource_dirs: ["res"],
-    test_config: "AndroidTest.xml",
-    test_suites: ["general-tests"],
-}
diff --git a/startop/view_compiler/dex_builder_test/AndroidManifest.xml b/startop/view_compiler/dex_builder_test/AndroidManifest.xml
deleted file mode 100644
index b335663..0000000
--- a/startop/view_compiler/dex_builder_test/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.startop.test" >
-
-    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="android.startop.test"
-                     android:label="DexBuilder Tests"/>
-
-</manifest>
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
deleted file mode 100644
index 59093c7..0000000
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Runs DexBuilder Tests.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-instrumentation" />
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="dex-builder-test.apk" />
-    </target_preparer>
-
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
-        <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
-        <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" />
-        <option name="push" value="layout2.dex->/data/local/tmp/dex-builder-test/layout2.dex" />
-    </target_preparer>
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.startop.test" />
-        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
-    </test>
-</configuration>
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
deleted file mode 100644
index 0f9375c..0000000
--- a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-   android:layout_width="match_parent"
-   android:layout_height="match_parent"
-   android:paddingLeft="16dp"
-   android:paddingRight="16dp"
-   android:orientation="vertical"
-   android:gravity="center">
-
-    <Button
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-    <Button
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
- </LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout2.xml b/startop/view_compiler/dex_builder_test/res/layout/layout2.xml
deleted file mode 100644
index b092e1c..0000000
--- a/startop/view_compiler/dex_builder_test/res/layout/layout2.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <TableRow
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" >
-
-        <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Button" />
-
-        <TableRow
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" >
-
-            <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button" />
-            <TableRow
-                android:layout_width="match_parent"
-                android:layout_height="match_parent" >
-
-                <Button
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="Button" />
-
-                <TableRow
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent" >
-
-                    <Button
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:text="Button" />
-                </TableRow>
-
-            </TableRow>
-        </TableRow>
-    </TableRow>
-</LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
deleted file mode 100644
index 6af01f6f..0000000
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ /dev/null
@@ -1,213 +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.
- */
-
-package android.startop.test;
-
-import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import dalvik.system.PathClassLoader;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-// Adding tests here requires changes in several other places. See README.md in
-// the view_compiler directory for more information.
-public final class DexBuilderTest {
-  static ClassLoader loadDexFile(String filename) throws Exception {
-    return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
-        DexBuilderTest.class.getClassLoader());
-  }
-
-  public void hello() {}
-
-  @Test
-  public void loadTrivialDex() throws Exception {
-    ClassLoader loader = loadDexFile("trivial.dex");
-    loader.loadClass("android.startop.test.testcases.Trivial");
-  }
-
-  @Test
-  public void return5() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("return5");
-    Assert.assertEquals(5, method.invoke(null));
-  }
-
-  @Test
-  public void returnInteger5() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnInteger5");
-    Assert.assertEquals(5, method.invoke(null));
-  }
-
-  @Test
-  public void returnParam() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnParam", int.class);
-    Assert.assertEquals(5, method.invoke(null, 5));
-    Assert.assertEquals(42, method.invoke(null, 42));
-  }
-
-  @Test
-  public void returnStringLength() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnStringLength", String.class);
-    Assert.assertEquals(13, method.invoke(null, "Hello, World!"));
-  }
-
-  @Test
-  public void returnIfZero() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnIfZero", int.class);
-    Assert.assertEquals(5, method.invoke(null, 0));
-    Assert.assertEquals(3, method.invoke(null, 17));
-  }
-
-  @Test
-  public void returnIfNotZero() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnIfNotZero", int.class);
-    Assert.assertEquals(3, method.invoke(null, 0));
-    Assert.assertEquals(5, method.invoke(null, 17));
-  }
-
-  @Test
-  public void backwardsBranch() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("backwardsBranch");
-    Assert.assertEquals(2, method.invoke(null));
-  }
-
-  @Test
-  public void returnNull() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnNull");
-    Assert.assertEquals(null, method.invoke(null));
-  }
-
-  @Test
-  public void makeString() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("makeString");
-    Assert.assertEquals("Hello, World!", method.invoke(null));
-  }
-
-  @Test
-  public void returnStringIfZeroAB() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnStringIfZeroAB", int.class);
-    Assert.assertEquals("a", method.invoke(null, 0));
-    Assert.assertEquals("b", method.invoke(null, 1));
-  }
-
-  @Test
-  public void returnStringIfZeroBA() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("returnStringIfZeroBA", int.class);
-    Assert.assertEquals("b", method.invoke(null, 0));
-    Assert.assertEquals("a", method.invoke(null, 1));
-  }
-
-  @Test
-  public void invokeStaticReturnObject() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("invokeStaticReturnObject", int.class, int.class);
-    Assert.assertEquals("10", method.invoke(null, 10, 10));
-    Assert.assertEquals("a", method.invoke(null, 10, 16));
-    Assert.assertEquals("5", method.invoke(null, 5, 16));
-  }
-
-  @Test
-  public void invokeVirtualReturnObject() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class);
-    Assert.assertEquals("bc", method.invoke(null, "abc", 1));
-  }
-
-  @Test
-  public void castObjectToString() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("castObjectToString", Object.class);
-    Assert.assertEquals("abc", method.invoke(null, "abc"));
-    boolean castFailed = false;
-    try {
-      method.invoke(null, 5);
-    } catch (InvocationTargetException e) {
-      if (e.getCause() instanceof ClassCastException) {
-        castFailed = true;
-      } else {
-        throw e;
-      }
-    }
-    Assert.assertTrue(castFailed);
-  }
-
-  @Test
-  public void readStaticField() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("readStaticField");
-    TestClass.staticInteger = 5;
-    Assert.assertEquals(5, method.invoke(null));
-  }
-
-  @Test
-  public void setStaticField() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("setStaticField");
-    TestClass.staticInteger = 5;
-    method.invoke(null);
-    Assert.assertEquals(7, TestClass.staticInteger);
-  }
-
-  @Test
-  public void readInstanceField() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("readInstanceField", TestClass.class);
-    TestClass obj = new TestClass();
-    obj.instanceField = 5;
-    Assert.assertEquals(5, method.invoke(null, obj));
-  }
-
-  @Test
-  public void setInstanceField() throws Exception {
-    ClassLoader loader = loadDexFile("simple.dex");
-    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
-    Method method = clazz.getMethod("setInstanceField", TestClass.class);
-    TestClass obj = new TestClass();
-    obj.instanceField = 5;
-    method.invoke(null, obj);
-    Assert.assertEquals(7, obj.instanceField);
-  }
-}
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
deleted file mode 100644
index b0cf91d..0000000
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
+++ /dev/null
@@ -1,52 +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.
- */
-
-package android.startop.test;
-
-import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import android.view.View;
-import dalvik.system.PathClassLoader;
-import java.lang.reflect.Method;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.lang.reflect.Method;
-
-// Adding tests here requires changes in several other places. See README.md in
-// the view_compiler directory for more information.
-public class LayoutCompilerTest {
-    static ClassLoader loadDexFile(String filename) throws Exception {
-        return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
-                ClassLoader.getSystemClassLoader());
-    }
-
-    @Test
-    public void loadAndInflateLayout1() throws Exception {
-        ClassLoader dex_file = loadDexFile("layout1.dex");
-        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
-        Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
-        Context context = InstrumentationRegistry.getTargetContext();
-        layout1.invoke(null, context, R.layout.layout1);
-    }
-
-    @Test
-    public void loadAndInflateLayout2() throws Exception {
-        ClassLoader dex_file = loadDexFile("layout2.dex");
-        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
-        Method layout1 = compiled_view.getMethod("layout2", Context.class, int.class);
-        Context context = InstrumentationRegistry.getTargetContext();
-        layout1.invoke(null, context, R.layout.layout1);
-    }
-}
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
deleted file mode 100644
index dd77923..0000000
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
+++ /dev/null
@@ -1,23 +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.
- */
-package android.startop.test;
-
- /**
- * A simple class to help test DexBuilder.
- */
-public final class TestClass {
-    public static int staticInteger;
-
-    public int instanceField;
-}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
deleted file mode 100644
index bddb8aa..0000000
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ /dev/null
@@ -1,208 +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.
- */
-
-#include "dex_layout_compiler.h"
-#include "layout_validation.h"
-
-#include "android-base/stringprintf.h"
-
-namespace startop {
-
-using android::base::StringPrintf;
-using dex::Instruction;
-using dex::LiveRegister;
-using dex::Prototype;
-using dex::TypeDescriptor;
-using dex::Value;
-
-namespace {
-// TODO: these are a bunch of static initializers, which we should avoid. See if
-// we can make them constexpr.
-const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet");
-const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context");
-const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater");
-const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources");
-const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String");
-const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View");
-const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup");
-const TypeDescriptor kXmlResourceParser =
-    TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
-}  // namespace
-
-DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
-    : method_{method},
-      context_{Value::Parameter(0)},
-      resid_{Value::Parameter(1)},
-      inflater_{method->AllocRegister()},
-      xml_{method->AllocRegister()},
-      attrs_{method->AllocRegister()},
-      classname_tmp_{method->AllocRegister()},
-      xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next",
-                                                       Prototype{TypeDescriptor::Int()})},
-      try_create_view_{method->dex_file()->GetOrDeclareMethod(
-          kLayoutInflater, "tryCreateView",
-          Prototype{kView, kView, kString, kContext, kAttributeSet})},
-      generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
-          kViewGroup, "generateLayoutParams",
-          Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
-                    kAttributeSet})},
-      add_view_{method->dex_file()->GetOrDeclareMethod(
-          kViewGroup, "addView",
-          Prototype{TypeDescriptor::Void(),
-                    kView,
-                    TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
-
-void DexViewBuilder::BuildGetLayoutInflater(Value dest) {
-  // dest = LayoutInflater.from(context);
-  auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod(
-      kLayoutInflater, "from", Prototype{kLayoutInflater, kContext});
-  method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_));
-}
-
-void DexViewBuilder::BuildGetResources(Value dest) {
-  // dest = context.getResources();
-  auto get_resources =
-      method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources});
-  method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_));
-}
-
-void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) {
-  // dest = resources.getLayout(resid);
-  auto get_layout = method_->dex_file()->GetOrDeclareMethod(
-      kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()});
-  method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid));
-}
-
-void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest,
-                                                       dex::Value layout_resource) {
-  // dest = Xml.asAttributeSet(layout_resource);
-  auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod(
-      TypeDescriptor::FromClassname("android.util.Xml"),
-      "asAttributeSet",
-      Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
-  method_->AddInstruction(
-      Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
-}
-
-void DexViewBuilder::BuildXmlNext() {
-  // xml_.next();
-  method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
-}
-
-void DexViewBuilder::Start() {
-  BuildGetLayoutInflater(/*dest=*/inflater_);
-  BuildGetResources(/*dest=*/xml_);
-  BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
-  BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
-
-  // Advance past start document tag
-  BuildXmlNext();
-}
-
-void DexViewBuilder::Finish() {}
-
-namespace {
-std::string ResolveName(const std::string& name) {
-  if (name == "View") return "android.view.View";
-  if (name == "ViewGroup") return "android.view.ViewGroup";
-  if (name.find('.') == std::string::npos) {
-    return StringPrintf("android.widget.%s", name.c_str());
-  }
-  return name;
-}
-}  // namespace
-
-void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) {
-  // dest = inflater_.tryCreateView(parent, classname, context_, attrs_);
-  method_->AddInstruction(Instruction::InvokeVirtualObject(
-      try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_));
-}
-
-void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
-  bool const is_root_view = view_stack_.empty();
-
-  // Advance to start tag
-  BuildXmlNext();
-
-  LiveRegister view = AcquireRegister();
-  // try to create the view using the factories
-  method_->BuildConstString(classname_tmp_,
-                            name);  // TODO: the need to fully qualify the classname
-  if (is_root_view) {
-    LiveRegister null = AcquireRegister();
-    method_->BuildConst4(null, 0);
-    BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
-  } else {
-    BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
-  }
-  auto label = method_->MakeLabel();
-  // branch if not null
-  method_->AddInstruction(
-      Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
-
-  // If null, create the class directly.
-  method_->BuildNew(view,
-                    TypeDescriptor::FromClassname(ResolveName(name)),
-                    Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
-                    context_,
-                    attrs_);
-
-  method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
-
-  if (is_viewgroup) {
-    // Cast to a ViewGroup so we can add children later.
-    const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor());
-    method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index)));
-  }
-
-  if (!is_root_view) {
-    // layout_params = parent.generateLayoutParams(attrs);
-    LiveRegister layout_params{AcquireRegister()};
-    method_->AddInstruction(Instruction::InvokeVirtualObject(
-        generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
-    view_stack_.push_back({std::move(view), std::move(layout_params)});
-  } else {
-    view_stack_.push_back({std::move(view), {}});
-  }
-}
-
-void DexViewBuilder::FinishView() {
-  if (view_stack_.size() == 1) {
-    method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
-  } else {
-    // parent.add(view, layout_params)
-    method_->AddInstruction(Instruction::InvokeVirtual(
-        add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
-    // xml.next(); // end tag
-    method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
-  }
-  PopViewStack();
-}
-
-LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
-
-Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
-Value DexViewBuilder::GetCurrentLayoutParams() const {
-  return view_stack_.back().layout_params.value();
-}
-Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
-
-void DexViewBuilder::PopViewStack() {
-  // Unconditionally release the view register.
-  view_stack_.pop_back();
-}
-
-}  // namespace startop
diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h
deleted file mode 100644
index a34ed1f..0000000
--- a/startop/view_compiler/dex_layout_compiler.h
+++ /dev/null
@@ -1,123 +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.
- */
-
-#ifndef DEX_LAYOUT_COMPILER_H_
-#define DEX_LAYOUT_COMPILER_H_
-
-#include "dex_builder.h"
-
-#include <codecvt>
-#include <locale>
-#include <string>
-#include <vector>
-
-namespace startop {
-
-// This visitor does the actual view compilation, using a supplied builder.
-template <typename Builder>
-class LayoutCompilerVisitor {
- public:
-  explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {}
-
-  void VisitStartDocument() { builder_->Start(); }
-  void VisitEndDocument() { builder_->Finish(); }
-  void VisitStartTag(const std::u16string& name) {
-    parent_stack_.push_back(ViewEntry{
-        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}});
-  }
-  void VisitEndTag() {
-    auto entry = parent_stack_.back();
-    parent_stack_.pop_back();
-
-    if (parent_stack_.empty()) {
-      GenerateCode(entry);
-    } else {
-      parent_stack_.back().children.push_back(entry);
-    }
-  }
-
- private:
-  struct ViewEntry {
-    std::string name;
-    std::vector<ViewEntry> children;
-  };
-
-  void GenerateCode(const ViewEntry& view) {
-    builder_->StartView(view.name, !view.children.empty());
-    for (const auto& child : view.children) {
-      GenerateCode(child);
-    }
-    builder_->FinishView();
-  }
-
-  Builder* builder_;
-
-  std::vector<ViewEntry> parent_stack_;
-};
-
-class DexViewBuilder {
- public:
-  DexViewBuilder(dex::MethodBuilder* method);
-
-  void Start();
-  void Finish();
-  void StartView(const std::string& name, bool is_viewgroup);
-  void FinishView();
-
- private:
-  // Accessors for the stack of views that are under construction.
-  dex::LiveRegister AcquireRegister();
-  dex::Value GetCurrentView() const;
-  dex::Value GetCurrentLayoutParams() const;
-  dex::Value GetParentView() const;
-  void PopViewStack();
-
-  // Methods to simplify building different code fragments.
-  void BuildGetLayoutInflater(dex::Value dest);
-  void BuildGetResources(dex::Value dest);
-  void BuildGetLayoutResource(dex::Value dest, dex::Value resources, dex::Value resid);
-  void BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource);
-  void BuildXmlNext();
-  void BuildTryCreateView(dex::Value dest, dex::Value parent, dex::Value classname);
-
-  dex::MethodBuilder* method_;
-
-  // Parameters to the generated method
-  dex::Value const context_;
-  dex::Value const resid_;
-
-  // Registers used for code generation
-  const dex::LiveRegister inflater_;
-  const dex::LiveRegister xml_;
-  const dex::LiveRegister attrs_;
-  const dex::LiveRegister classname_tmp_;
-
-  const dex::MethodDeclData xml_next_;
-  const dex::MethodDeclData try_create_view_;
-  const dex::MethodDeclData generate_layout_params_;
-  const dex::MethodDeclData add_view_;
-
-  // Keep track of the views currently in progress.
-  struct ViewEntry {
-    dex::LiveRegister view;
-    std::optional<dex::LiveRegister> layout_params;
-  };
-  std::vector<ViewEntry> view_stack_;
-};
-
-}  // namespace startop
-
-#endif  // DEX_LAYOUT_COMPILER_H_
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
deleted file mode 100644
index 5dda59e..0000000
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ /dev/null
@@ -1,353 +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.
- */
-
-#include "android-base/logging.h"
-#include "dex_builder.h"
-
-#include <fstream>
-#include <string>
-
-// Adding tests here requires changes in several other places. See README.md in
-// the view_compiler directory for more information.
-
-using namespace startop::dex;
-using namespace std;
-
-void GenerateTrivialDexFile(const string& outdir) {
-  DexBuilder dex_file;
-
-  ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.Trivial")};
-  cbuilder.set_source_file("dex_testcase_generator.cc#GenerateTrivialDexFile");
-
-  slicer::MemView image{dex_file.CreateImage()};
-  std::ofstream out_file(outdir + "/trivial.dex");
-  out_file.write(image.ptr<const char>(), image.size());
-}
-
-// Generates test cases that test around 1 instruction.
-void GenerateSimpleTestCases(const string& outdir) {
-  DexBuilder dex_file;
-
-  ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.SimpleTests")};
-  cbuilder.set_source_file("dex_testcase_generator.cc#GenerateSimpleTestCases");
-
-  // int return5() { return 5; }
-  auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
-  {
-    LiveRegister r{return5.AllocRegister()};
-    return5.BuildConst4(r, 5);
-    return5.BuildReturn(r);
-  }
-  return5.Encode();
-
-  // int return5() { return 5; }
-  auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
-  auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
-  [&](MethodBuilder& method) {
-    LiveRegister five{method.AllocRegister()};
-    method.BuildConst4(five, 5);
-    LiveRegister object{method.AllocRegister()};
-    method.BuildNew(
-        object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
-    method.BuildReturn(object, /*is_object=*/true);
-  }(returnInteger5);
-  returnInteger5.Encode();
-
-  // // int returnParam(int x) { return x; }
-  auto returnParam{cbuilder.CreateMethod("returnParam",
-                                         Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
-  returnParam.BuildReturn(Value::Parameter(0));
-  returnParam.Encode();
-
-  // int returnStringLength(String x) { return x.length(); }
-  auto string_type{TypeDescriptor::FromClassname("java.lang.String")};
-  MethodDeclData string_length{
-      dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()})};
-
-  auto returnStringLength{
-      cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
-  {
-    LiveRegister result = returnStringLength.AllocRegister();
-    returnStringLength.AddInstruction(
-        Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
-    returnStringLength.BuildReturn(result);
-  }
-  returnStringLength.Encode();
-
-  // int returnIfZero(int x) { if (x == 0) { return 5; } else { return 3; } }
-  MethodBuilder returnIfZero{cbuilder.CreateMethod(
-      "returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
-  {
-    LiveRegister resultIfZero{returnIfZero.AllocRegister()};
-    Value else_target{returnIfZero.MakeLabel()};
-    returnIfZero.AddInstruction(Instruction::OpWithArgs(
-        Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
-    // else branch
-    returnIfZero.BuildConst4(resultIfZero, 3);
-    returnIfZero.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfZero));
-    // then branch
-    returnIfZero.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
-    returnIfZero.BuildConst4(resultIfZero, 5);
-    returnIfZero.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfZero));
-  }
-  returnIfZero.Encode();
-
-  // int returnIfNotZero(int x) { if (x != 0) { return 5; } else { return 3; } }
-  MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
-      "returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
-  {
-    LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()};
-    Value else_target{returnIfNotZero.MakeLabel()};
-    returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
-        Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
-    // else branch
-    returnIfNotZero.BuildConst4(resultIfNotZero, 3);
-    returnIfNotZero.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero));
-    // then branch
-    returnIfNotZero.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
-    returnIfNotZero.BuildConst4(resultIfNotZero, 5);
-    returnIfNotZero.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero));
-  }
-  returnIfNotZero.Encode();
-
-  // Make sure backwards branches work too.
-  //
-  // Pseudo code for test:
-  // {
-  //   zero = 0;
-  //   result = 1;
-  //   if (zero == 0) goto B;
-  // A:
-  //   return result;
-  // B:
-  //   result = 2;
-  //   if (zero == 0) goto A;
-  //   result = 3;
-  //   return result;
-  // }
-  // If it runs correctly, this test should return 2.
-  MethodBuilder backwardsBranch{
-      cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})};
-  [](MethodBuilder& method) {
-    LiveRegister zero = method.AllocRegister();
-    LiveRegister result = method.AllocRegister();
-    Value labelA = method.MakeLabel();
-    Value labelB = method.MakeLabel();
-    method.BuildConst4(zero, 0);
-    method.BuildConst4(result, 1);
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBranchEqz, /*dest=*/{}, zero, labelB));
-
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, labelA));
-    method.BuildReturn(result);
-
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, labelB));
-    method.BuildConst4(result, 2);
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBranchEqz, /*dest=*/{}, zero, labelA));
-
-    method.BuildConst4(result, 3);
-    method.BuildReturn(result);
-  }(backwardsBranch);
-  backwardsBranch.Encode();
-
-  // Test that we can make a null value. Basically:
-  //
-  // public static String returnNull() { return null; }
-  MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
-  [](MethodBuilder& method) {
-    LiveRegister zero = method.AllocRegister();
-    method.BuildConst4(zero, 0);
-    method.BuildReturn(zero, /*is_object=*/true);
-  }(returnNull);
-  returnNull.Encode();
-
-  // Test that we can make String literals. Basically:
-  //
-  // public static String makeString() { return "Hello, World!"; }
-  MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
-  [](MethodBuilder& method) {
-    LiveRegister string = method.AllocRegister();
-    method.BuildConstString(string, "Hello, World!");
-    method.BuildReturn(string, /*is_object=*/true);
-  }(makeString);
-  makeString.Encode();
-
-  // Make sure strings are sorted correctly.
-  //
-  // int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } }
-  MethodBuilder returnStringIfZeroAB{
-      cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
-  [&](MethodBuilder& method) {
-    LiveRegister resultIfZero{method.AllocRegister()};
-    Value else_target{method.MakeLabel()};
-    method.AddInstruction(Instruction::OpWithArgs(
-        Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
-    // else branch
-    method.BuildConstString(resultIfZero, "b");
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
-    // then branch
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
-    method.BuildConstString(resultIfZero, "a");
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
-    method.Encode();
-  }(returnStringIfZeroAB);
-  // int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } }
-  MethodBuilder returnStringIfZeroBA{
-      cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
-  [&](MethodBuilder& method) {
-    LiveRegister resultIfZero{method.AllocRegister()};
-    Value else_target{method.MakeLabel()};
-    method.AddInstruction(Instruction::OpWithArgs(
-        Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
-    // else branch
-    method.BuildConstString(resultIfZero, "a");
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
-    // then branch
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
-    method.BuildConstString(resultIfZero, "b");
-    method.AddInstruction(
-        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
-    method.Encode();
-  }(returnStringIfZeroBA);
-
-  // Make sure we can invoke static methods that return an object
-  // String invokeStaticReturnObject(int n, int radix) { return java.lang.Integer.toString(n,
-  // radix); }
-  MethodBuilder invokeStaticReturnObject{
-      cbuilder.CreateMethod("invokeStaticReturnObject",
-                            Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
-  [&](MethodBuilder& method) {
-    LiveRegister result{method.AllocRegister()};
-    MethodDeclData to_string{dex_file.GetOrDeclareMethod(
-        TypeDescriptor::FromClassname("java.lang.Integer"),
-        "toString",
-        Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
-    method.AddInstruction(Instruction::InvokeStaticObject(
-        to_string.id, result, Value::Parameter(0), Value::Parameter(1)));
-    method.BuildReturn(result, /*is_object=*/true);
-    method.Encode();
-  }(invokeStaticReturnObject);
-
-  // Make sure we can invoke virtual methods that return an object
-  // String invokeVirtualReturnObject(String s, int n) { return s.substring(n); }
-  MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
-      "invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
-  [&](MethodBuilder& method) {
-    LiveRegister result{method.AllocRegister()};
-    MethodDeclData substring{dex_file.GetOrDeclareMethod(
-        string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
-    method.AddInstruction(Instruction::InvokeVirtualObject(
-        substring.id, result, Value::Parameter(0), Value::Parameter(1)));
-    method.BuildReturn(result, /*is_object=*/true);
-    method.Encode();
-  }(invokeVirtualReturnObject);
-
-  // Make sure we can cast objects
-  // String castObjectToString(Object o) { return (String)o; }
-  MethodBuilder castObjectToString{cbuilder.CreateMethod(
-      "castObjectToString",
-      Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})};
-  [&](MethodBuilder& method) {
-    const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor());
-    method.AddInstruction(
-        Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index)));
-    method.BuildReturn(Value::Parameter(0), /*is_object=*/true);
-    method.Encode();
-  }(castObjectToString);
-
-  TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass");
-
-  // Read a static field
-  // int readStaticField() { return TestClass.staticInteger; }
-  MethodBuilder readStaticField{
-      cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
-  [&](MethodBuilder& method) {
-    const ir::FieldDecl* field =
-        dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
-    LiveRegister result{method.AllocRegister()};
-    method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
-    method.BuildReturn(result, /*is_object=*/false);
-    method.Encode();
-  }(readStaticField);
-
-  // Set a static field
-  // void setStaticField() { TestClass.staticInteger = 7; }
-  MethodBuilder setStaticField{
-      cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
-  [&](MethodBuilder& method) {
-    const ir::FieldDecl* field =
-        dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
-    LiveRegister number{method.AllocRegister()};
-    method.BuildConst4(number, 7);
-    method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
-    method.BuildReturn();
-    method.Encode();
-  }(setStaticField);
-
-  // Read an instance field
-  // int readInstanceField(TestClass obj) { return obj.instanceField; }
-  MethodBuilder readInstanceField{
-      cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})};
-  [&](MethodBuilder& method) {
-    const ir::FieldDecl* field =
-        dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
-    LiveRegister result{method.AllocRegister()};
-    method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
-    method.BuildReturn(result, /*is_object=*/false);
-    method.Encode();
-  }(readInstanceField);
-
-  // Set an instance field
-  // void setInstanceField(TestClass obj) { obj.instanceField = 7; }
-  MethodBuilder setInstanceField{
-      cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})};
-  [&](MethodBuilder& method) {
-    const ir::FieldDecl* field =
-        dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
-    LiveRegister number{method.AllocRegister()};
-    method.BuildConst4(number, 7);
-    method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
-    method.BuildReturn();
-    method.Encode();
-  }(setInstanceField);
-
-  slicer::MemView image{dex_file.CreateImage()};
-  std::ofstream out_file(outdir + "/simple.dex");
-  out_file.write(image.ptr<const char>(), image.size());
-}
-
-int main(int argc, char** argv) {
-  CHECK_EQ(argc, 2);
-
-  string outdir = argv[1];
-
-  GenerateTrivialDexFile(outdir);
-  GenerateSimpleTestCases(outdir);
-}
diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc
deleted file mode 100644
index 920caee..0000000
--- a/startop/view_compiler/java_lang_builder.cc
+++ /dev/null
@@ -1,115 +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.
- */
-
-#include "java_lang_builder.h"
-
-#include "android-base/stringprintf.h"
-
-using android::base::StringPrintf;
-using std::string;
-
-void JavaLangViewBuilder::Start() const {
-  out_ << StringPrintf("package %s;\n", package_.c_str())
-       << "import android.content.Context;\n"
-          "import android.content.res.Resources;\n"
-          "import android.content.res.XmlResourceParser;\n"
-          "import android.util.AttributeSet;\n"
-          "import android.util.Xml;\n"
-          "import android.view.*;\n"
-          "import android.widget.*;\n"
-          "\n"
-          "public final class CompiledView {\n"
-          "\n"
-          "static <T extends View> T createView(Context context, AttributeSet attrs, View parent, "
-          "String name, LayoutInflater.Factory factory, LayoutInflater.Factory2 factory2) {"
-          "\n"
-          "  if (factory2 != null) {\n"
-          "    return (T)factory2.onCreateView(parent, name, context, attrs);\n"
-          "  } else if (factory != null) {\n"
-          "    return (T)factory.onCreateView(name, context, attrs);\n"
-          "  }\n"
-          // TODO: find a way to call the private factory
-          "  return null;\n"
-          "}\n"
-          "\n"
-          "  public static View inflate(Context context) {\n"
-          "    try {\n"
-          "      LayoutInflater inflater = LayoutInflater.from(context);\n"
-          "      LayoutInflater.Factory factory = inflater.getFactory();\n"
-          "      LayoutInflater.Factory2 factory2 = inflater.getFactory2();\n"
-          "      Resources res = context.getResources();\n"
-       << StringPrintf("      XmlResourceParser xml = res.getLayout(%s.R.layout.%s);\n",
-                       package_.c_str(),
-                       layout_name_.c_str())
-       << "      AttributeSet attrs = Xml.asAttributeSet(xml);\n"
-          // The Java-language XmlPullParser needs a call to next to find the start document tag.
-          "      xml.next(); // start document\n";
-}
-
-void JavaLangViewBuilder::Finish() const {
-  out_ << "    } catch (Exception e) {\n"
-          "      return null;\n"
-          "    }\n"  // end try
-          "  }\n"    // end inflate
-          "}\n";     // end CompiledView
-}
-
-void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) {
-  const string view_var = MakeVar("view");
-  const string layout_var = MakeVar("layout");
-  std::string parent = "null";
-  if (!view_stack_.empty()) {
-    const StackEntry& parent_entry = view_stack_.back();
-    parent = parent_entry.view_var;
-  }
-  out_ << "      xml.next(); // <" << class_name << ">\n"
-       << StringPrintf("      %s %s = createView(context, attrs, %s, \"%s\", factory, factory2);\n",
-                       class_name.c_str(),
-                       view_var.c_str(),
-                       parent.c_str(),
-                       class_name.c_str())
-       << StringPrintf("      if (%s == null) %s = new %s(context, attrs);\n",
-                       view_var.c_str(),
-                       view_var.c_str(),
-                       class_name.c_str());
-  if (!view_stack_.empty()) {
-    out_ << StringPrintf("      ViewGroup.LayoutParams %s = %s.generateLayoutParams(attrs);\n",
-                         layout_var.c_str(),
-                         parent.c_str());
-  }
-  view_stack_.push_back({class_name, view_var, layout_var});
-}
-
-void JavaLangViewBuilder::FinishView() {
-  const StackEntry var = view_stack_.back();
-  view_stack_.pop_back();
-  if (!view_stack_.empty()) {
-    const string& parent = view_stack_.back().view_var;
-    out_ << StringPrintf("      xml.next(); // </%s>\n", var.class_name.c_str())
-         << StringPrintf("      %s.addView(%s, %s);\n",
-                         parent.c_str(),
-                         var.view_var.c_str(),
-                         var.layout_params_var.c_str());
-  } else {
-    out_ << StringPrintf("      return %s;\n", var.view_var.c_str());
-  }
-}
-
-const std::string JavaLangViewBuilder::MakeVar(std::string prefix) {
-  std::stringstream v;
-  v << prefix << view_id_++;
-  return v.str();
-}
diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h
deleted file mode 100644
index 69356d3..0000000
--- a/startop/view_compiler/java_lang_builder.h
+++ /dev/null
@@ -1,65 +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.
- */
-#ifndef JAVA_LANG_BUILDER_H_
-#define JAVA_LANG_BUILDER_H_
-
-#include <iostream>
-#include <sstream>
-#include <vector>
-
-// Build Java language code to instantiate views.
-//
-// This has a very small interface to make it easier to generate additional
-// backends, such as a direct-to-DEX version.
-class JavaLangViewBuilder {
- public:
-  JavaLangViewBuilder(std::string package, std::string layout_name, std::ostream& out = std::cout)
-      : package_(package), layout_name_(layout_name), out_(out) {}
-
-  // Begin generating a class. Adds the package boilerplate, etc.
-  void Start() const;
-  // Finish generating a class, closing off any open curly braces, etc.
-  void Finish() const;
-
-  // Begin creating a view (i.e. process the opening tag)
-  void StartView(const std::string& class_name, bool is_viewgroup);
-  // Finish a view, after all of its child nodes have been processed.
-  void FinishView();
-
- private:
-  const std::string MakeVar(std::string prefix);
-
-  std::string const package_;
-  std::string const layout_name_;
-
-  std::ostream& out_;
-
-  size_t view_id_ = 0;
-
-  struct StackEntry {
-      // The class name for this view object
-      const std::string class_name;
-
-      // The variable name that is holding the view object
-      const std::string view_var;
-
-      // The variable name that holds the object's layout parameters
-      const std::string layout_params_var;
-  };
-  std::vector<StackEntry> view_stack_;
-};
-
-#endif  // JAVA_LANG_BUILDER_H_
diff --git a/startop/view_compiler/layout_validation.cc b/startop/view_compiler/layout_validation.cc
deleted file mode 100644
index 8c77377..0000000
--- a/startop/view_compiler/layout_validation.cc
+++ /dev/null
@@ -1,42 +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.
- */
-
-#include "layout_validation.h"
-
-#include "android-base/stringprintf.h"
-
-namespace startop {
-
-void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
-  if (0 == name.compare(u"merge")) {
-    message_ = "Merge tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"include")) {
-    message_ = "Include tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"view")) {
-    message_ = "View tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"fragment")) {
-    message_ = "Fragment tags are not supported";
-    can_compile_ = false;
-  }
-}
-
-}  // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/layout_validation.h b/startop/view_compiler/layout_validation.h
deleted file mode 100644
index bed34bb..0000000
--- a/startop/view_compiler/layout_validation.h
+++ /dev/null
@@ -1,46 +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.
- */
-
-#ifndef LAYOUT_VALIDATION_H_
-#define LAYOUT_VALIDATION_H_
-
-#include "dex_builder.h"
-
-#include <string>
-
-namespace startop {
-
-// This visitor determines whether a layout can be compiled. Since we do not currently support all
-// features, such as includes and merges, we need to pre-validate the layout before we start
-// compiling.
-class LayoutValidationVisitor {
- public:
-  void VisitStartDocument() const {}
-  void VisitEndDocument() const {}
-  void VisitStartTag(const std::u16string& name);
-  void VisitEndTag() const {}
-
-  const std::string& message() const { return message_; }
-  bool can_compile() const { return can_compile_; }
-
- private:
-  std::string message_{"Okay"};
-  bool can_compile_{true};
-};
-
-}  // namespace startop
-
-#endif  // LAYOUT_VALIDATION_H_
diff --git a/startop/view_compiler/layout_validation_test.cc b/startop/view_compiler/layout_validation_test.cc
deleted file mode 100644
index b74cdae..0000000
--- a/startop/view_compiler/layout_validation_test.cc
+++ /dev/null
@@ -1,163 +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.
- */
-#include "tinyxml_layout_parser.h"
-
-#include "gtest/gtest.h"
-
-using startop::CanCompileLayout;
-using std::string;
-
-namespace {
-void ValidateXmlText(const string& xml, bool expected) {
-  tinyxml2::XMLDocument doc;
-  doc.Parse(xml.c_str());
-  EXPECT_EQ(CanCompileLayout(doc), expected);
-}
-}  // namespace
-
-TEST(LayoutValidationTest, SingleButtonLayout) {
-  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:text="Hello, World!">
-
-</Button>)";
-  ValidateXmlText(xml, /*expected=*/true);
-}
-
-TEST(LayoutValidationTest, SmallConstraintLayout) {
-  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
-<android.support.constraint.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <Button
-        android:id="@+id/button6"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="16dp"
-        android:layout_marginBottom="16dp"
-        android:text="Button"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent" />
-
-    <Button
-        android:id="@+id/button7"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="8dp"
-        android:layout_marginBottom="16dp"
-        android:text="Button2"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/button6" />
-
-    <Button
-        android:id="@+id/button8"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="8dp"
-        android:layout_marginBottom="16dp"
-        android:text="Button1"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/button7" />
-</android.support.constraint.ConstraintLayout>)";
-  ValidateXmlText(xml, /*expected=*/true);
-}
-
-TEST(LayoutValidationTest, MergeNode) {
-  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <TextView
-        android:id="@+id/textView3"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="TextView" />
-
-    <Button
-        android:id="@+id/button9"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Button" />
-</merge>)";
-  ValidateXmlText(xml, /*expected=*/false);
-}
-
-TEST(LayoutValidationTest, IncludeLayout) {
-  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
-<android.support.constraint.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <include
-        layout="@layout/single_button_layout"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-</android.support.constraint.ConstraintLayout>)";
-  ValidateXmlText(xml, /*expected=*/false);
-}
-
-TEST(LayoutValidationTest, ViewNode) {
-  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
-<android.support.constraint.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <view
-        class="android.support.design.button.MaterialButton"
-        id="@+id/view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-</android.support.constraint.ConstraintLayout>)";
-  ValidateXmlText(xml, /*expected=*/false);
-}
-
-TEST(LayoutValidationTest, FragmentNode) {
-  // This test case is from https://developer.android.com/guide/components/fragments
-  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <fragment android:name="com.example.news.ArticleListFragment"
-            android:id="@+id/list"
-            android:layout_weight="1"
-            android:layout_width="0dp"
-            android:layout_height="match_parent" />
-    <fragment android:name="com.example.news.ArticleReaderFragment"
-            android:id="@+id/viewer"
-            android:layout_weight="2"
-            android:layout_width="0dp"
-            android:layout_height="match_parent" />
-</LinearLayout>)";
-  ValidateXmlText(xml, /*expected=*/false);
-}
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
deleted file mode 100644
index 11ecde2..0000000
--- a/startop/view_compiler/main.cc
+++ /dev/null
@@ -1,172 +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.
- */
-
-#include "gflags/gflags.h"
-
-#include "android-base/stringprintf.h"
-#include "apk_layout_compiler.h"
-#include "dex_builder.h"
-#include "dex_layout_compiler.h"
-#include "java_lang_builder.h"
-#include "layout_validation.h"
-#include "tinyxml_layout_parser.h"
-#include "util.h"
-
-#include "tinyxml2.h"
-
-#include <fstream>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace {
-
-using namespace tinyxml2;
-using android::base::StringPrintf;
-using startop::dex::ClassBuilder;
-using startop::dex::DexBuilder;
-using startop::dex::MethodBuilder;
-using startop::dex::Prototype;
-using startop::dex::TypeDescriptor;
-using namespace startop::util;
-using std::string;
-
-constexpr char kStdoutFilename[]{"stdout"};
-
-DEFINE_bool(apk, false, "Compile layouts in an APK");
-DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
-DEFINE_int32(infd, -1, "Read input from the given file descriptor");
-DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
-DEFINE_string(package, "", "The package name for the generated class (required)");
-
-template <typename Visitor>
-class XmlVisitorAdapter : public XMLVisitor {
- public:
-  explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
-
-  bool VisitEnter(const XMLDocument& /*doc*/) override {
-    visitor_->VisitStartDocument();
-    return true;
-  }
-
-  bool VisitExit(const XMLDocument& /*doc*/) override {
-    visitor_->VisitEndDocument();
-    return true;
-  }
-
-  bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
-    visitor_->VisitStartTag(
-        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
-            element.Name()));
-    return true;
-  }
-
-  bool VisitExit(const XMLElement& /*element*/) override {
-    visitor_->VisitEndTag();
-    return true;
-  }
-
- private:
-  Visitor* visitor_;
-};
-
-template <typename Builder>
-void CompileLayout(XMLDocument* xml, Builder* builder) {
-  startop::LayoutCompilerVisitor visitor{builder};
-  XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
-  xml->Accept(&adapter);
-}
-
-}  // end namespace
-
-int main(int argc, char** argv) {
-  constexpr size_t kProgramName = 0;
-  constexpr size_t kFileNameParam = 1;
-  constexpr size_t kNumRequiredArgs = 1;
-
-  gflags::SetUsageMessage(
-      "Compile XML layout files into equivalent Java language code\n"
-      "\n"
-      "  example usage:  viewcompiler layout.xml --package com.example.androidapp");
-  gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
-
-  gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
-  if (argc < kNumRequiredArgs || cmd.is_default) {
-    gflags::ShowUsageWithFlags(argv[kProgramName]);
-    return 1;
-  }
-
-  const bool is_stdout = FLAGS_out == kStdoutFilename;
-
-  std::ofstream outfile;
-  if (!is_stdout) {
-    outfile.open(FLAGS_out);
-  }
-
-  if (FLAGS_apk) {
-    const startop::CompilationTarget target =
-        FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage;
-    if (FLAGS_infd >= 0) {
-      startop::CompileApkLayoutsFd(
-          android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile);
-    } else {
-      if (argc < 2) {
-        gflags::ShowUsageWithFlags(argv[kProgramName]);
-        return 1;
-      }
-      const char* const filename = argv[kFileNameParam];
-      startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile);
-    }
-    return 0;
-  }
-
-  const char* const filename = argv[kFileNameParam];
-  const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
-
-  XMLDocument xml;
-  xml.LoadFile(filename);
-
-  string message{};
-  if (!startop::CanCompileLayout(xml, &message)) {
-    LOG(ERROR) << "Layout not supported: " << message;
-    return 1;
-  }
-
-  if (FLAGS_dex) {
-    DexBuilder dex_file;
-    string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
-    ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
-    MethodBuilder method{compiled_view.CreateMethod(
-        layout_name,
-        Prototype{TypeDescriptor::FromClassname("android.view.View"),
-                  TypeDescriptor::FromClassname("android.content.Context"),
-                  TypeDescriptor::Int()})};
-    startop::DexViewBuilder builder{&method};
-    CompileLayout(&xml, &builder);
-    method.Encode();
-
-    slicer::MemView image{dex_file.CreateImage()};
-
-    (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
-  } else {
-    // Generate Java language output.
-    JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
-
-    CompileLayout(&xml, &builder);
-  }
-  return 0;
-}
diff --git a/startop/view_compiler/tinyxml_layout_parser.cc b/startop/view_compiler/tinyxml_layout_parser.cc
deleted file mode 100644
index 1b3a81f..0000000
--- a/startop/view_compiler/tinyxml_layout_parser.cc
+++ /dev/null
@@ -1,34 +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.
- */
-#include "tinyxml_layout_parser.h"
-
-#include "layout_validation.h"
-
-namespace startop {
-
-bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message) {
-  LayoutValidationVisitor validator;
-  TinyXmlVisitorAdapter adapter{&validator};
-  xml.Accept(&adapter);
-
-  if (message != nullptr) {
-    *message = validator.message();
-  }
-
-  return validator.can_compile();
-}
-
-}  // namespace startop
diff --git a/startop/view_compiler/tinyxml_layout_parser.h b/startop/view_compiler/tinyxml_layout_parser.h
deleted file mode 100644
index 8f714a2..0000000
--- a/startop/view_compiler/tinyxml_layout_parser.h
+++ /dev/null
@@ -1,65 +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.
- */
-#ifndef TINYXML_LAYOUT_PARSER_H_
-#define TINYXML_LAYOUT_PARSER_H_
-
-#include "tinyxml2.h"
-
-#include <codecvt>
-#include <locale>
-#include <string>
-
-namespace startop {
-
-template <typename Visitor>
-class TinyXmlVisitorAdapter : public tinyxml2::XMLVisitor {
- public:
-  explicit TinyXmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
-
-  bool VisitEnter(const tinyxml2::XMLDocument& /*doc*/) override {
-    visitor_->VisitStartDocument();
-    return true;
-  }
-
-  bool VisitExit(const tinyxml2::XMLDocument& /*doc*/) override {
-    visitor_->VisitEndDocument();
-    return true;
-  }
-
-  bool VisitEnter(const tinyxml2::XMLElement& element,
-                  const tinyxml2::XMLAttribute* /*firstAttribute*/) override {
-    visitor_->VisitStartTag(
-        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
-            element.Name()));
-    return true;
-  }
-
-  bool VisitExit(const tinyxml2::XMLElement& /*element*/) override {
-    visitor_->VisitEndTag();
-    return true;
-  }
-
- private:
-  Visitor* visitor_;
-};
-
-// Returns whether a layout resource represented by a TinyXML document is supported by the layout
-// compiler.
-bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message = nullptr);
-
-}  // namespace startop
-
-#endif  // TINYXML_LAYOUT_PARSER_H_
diff --git a/startop/view_compiler/util.cc b/startop/view_compiler/util.cc
deleted file mode 100644
index c34d7b0..0000000
--- a/startop/view_compiler/util.cc
+++ /dev/null
@@ -1,38 +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.
- */
-
-#include "util.h"
-
-using std::string;
-
-namespace startop {
-namespace util {
-
-// TODO: see if we can borrow this from somewhere else, like aapt2.
-string FindLayoutNameFromFilename(const string& filename) {
-  size_t start = filename.rfind('/');
-  if (start == string::npos) {
-    start = 0;
-  } else {
-    start++;  // advance past '/' character
-  }
-  size_t end = filename.find('.', start);
-
-  return filename.substr(start, end - start);
-}
-
-}  // namespace util
-}  // namespace startop
diff --git a/startop/view_compiler/util.h b/startop/view_compiler/util.h
deleted file mode 100644
index 0176175..0000000
--- a/startop/view_compiler/util.h
+++ /dev/null
@@ -1,29 +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.
- */
-#ifndef VIEW_COMPILER_UTIL_H_
-#define VIEW_COMPILER_UTIL_H_
-
-#include <string>
-
-namespace startop {
-namespace util {
-
-std::string FindLayoutNameFromFilename(const std::string& filename);
-
-}  // namespace util
-}  // namespace startop
-
-#endif  // VIEW_COMPILER_UTIL_H_
diff --git a/startop/view_compiler/util_test.cc b/startop/view_compiler/util_test.cc
deleted file mode 100644
index 50682a0..0000000
--- a/startop/view_compiler/util_test.cc
+++ /dev/null
@@ -1,34 +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.
- */
-
-#include "util.h"
-
-#include "gtest/gtest.h"
-
-using std::string;
-
-namespace startop {
-namespace util {
-
-TEST(UtilTest, FindLayoutNameFromFilename) {
-  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("foo/bar.xml"));
-  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("bar.xml"));
-  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("./foo/bar.xml"));
-  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("/foo/bar.xml"));
-}
-
-}  // namespace util
-}  // namespace startop
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 038b93f..042b2a3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3148,6 +3148,14 @@
     public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY = "roaming_operator_string_array";
 
     /**
+     * Config to show the roaming indicator (i.e. the "R" icon) from the status bar when roaming.
+     * The roaming indicator will be shown if this is {@code true} and will not be shown if this is
+     * {@code false}.
+     */
+    @FlaggedApi(Flags.FLAG_HIDE_ROAMING_ICON)
+    public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
+
+    /**
      * URL from which the proto containing the public key of the Carrier used for
      * IMSI encryption will be downloaded.
      * @hide
@@ -3313,11 +3321,11 @@
      * If {@code false} the SPN display checks if the current MCC/MNC is different from the
      * SIM card's MCC/MNC.
      *
-     * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
-     * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
-     * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
-     * @see KEY_ROAMING_OPERATOR_STRING_ARRAY
-     * @see KEY_FORCE_HOME_NETWORK_BOOL
+     * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+     * @see #KEY_ROAMING_OPERATOR_STRING_ARRAY
+     * @see #KEY_FORCE_HOME_NETWORK_BOOL
      *
      * @hide
      */
@@ -8798,7 +8806,9 @@
          * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE},
          * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP},
          * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP},
-         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP}
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_3072_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_4096_BIT_MODP}
          */
         public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
                 KEY_PREFIX + "diffie_hellman_groups_int_array";
@@ -8866,7 +8876,8 @@
 
         /**
          * List of supported encryption algorithms for child session. Possible values are
-         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
          */
         public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
@@ -10193,6 +10204,7 @@
                 false);
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_SHOW_ROAMING_INDICATOR_BOOL, true);
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index cee2efb..f161f31 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -43,6 +44,7 @@
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.internal.telephony.flags.Flags;
 import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
@@ -2957,7 +2959,8 @@
      * @param number  The phone number used for WPS call.
      * @return {@code true} if number matches WPS pattern and {@code false} otherwise.
      */
-    public static boolean isWpsCallNumber(@Nullable String number) {
+    @FlaggedApi(Flags.FLAG_ENABLE_WPS_CHECK_API_FLAG)
+    public static boolean isWpsCallNumber(@NonNull String number) {
         return (number != null) && (number.startsWith(PREFIX_WPS)
                 || number.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
                 || number.startsWith(PREFIX_WPS_CLIR_DEACTIVATE));
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index ecd7039..d30078d 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -29,6 +29,7 @@
 
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.ArrayList;
@@ -545,6 +546,11 @@
             try {
                 iSession.setListener(mIImsCallSessionListenerProxy);
             } catch (RemoteException e) {
+                if (Flags.ignoreAlreadyTerminatedIncomingCallBeforeRegisteringListener()) {
+                    // Registering listener failed, so other operations are not allowed.
+                    Log.e(TAG, "ImsCallSession : " + e);
+                    mClosed = true;
+                }
             }
         } else {
             mClosed = true;
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 6b47db1..e2cd4f8 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -469,6 +469,9 @@
      * The satellite service should report the NTN signal strength via
      * ISatelliteListener#onNtnSignalStrengthChanged when the NTN signal strength changes.
      *
+     * Note: This API can be invoked multiple times. If the modem is already in the expected
+     * state from a previous request, subsequent invocations may be ignored.
+     *
      * @param resultCallback The callback to receive the error code result of the operation.
      *
      * Valid result codes returned:
@@ -485,6 +488,9 @@
      * be called when device is screen off to save power by not letting signal strength updates to
      * wake up application processor.
      *
+     * Note: This API can be invoked multiple times. If the modem is already in the expected
+     * state from a previous request, subsequent invocations may be ignored.
+     *
      * @param resultCallback The callback to receive the error code result of the operation.
      *
      * Valid result codes returned:
diff --git a/tests/notification/Android.bp b/tests/notification/Android.bp
deleted file mode 100644
index 1c1b5a2..0000000
--- a/tests/notification/Android.bp
+++ /dev/null
@@ -1,16 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
-    name: "NotificationTests",
-    // Include all test java files.
-    srcs: ["src/**/*.java"],
-    libs: ["android.test.runner.stubs"],
-    sdk_version: "21",
-}
diff --git a/tests/notification/AndroidManifest.xml b/tests/notification/AndroidManifest.xml
deleted file mode 100644
index 7cee00a7..0000000
--- a/tests/notification/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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 xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.frameworks.tests.notification"
-    >
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="com.android.frameworks.tests.notification"
-        android:label="Frameworks Notification Tests" />
-</manifest>
diff --git a/tests/notification/OWNERS b/tests/notification/OWNERS
deleted file mode 100644
index 396fd12..0000000
--- a/tests/notification/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/notification/OWNERS
diff --git a/tests/notification/res/drawable-nodpi/arubin_hed.jpeg b/tests/notification/res/drawable-nodpi/arubin_hed.jpeg
deleted file mode 100644
index c6d8ae9..0000000
--- a/tests/notification/res/drawable-nodpi/arubin_hed.jpeg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/bucket.png b/tests/notification/res/drawable-nodpi/bucket.png
deleted file mode 100644
index c865649..0000000
--- a/tests/notification/res/drawable-nodpi/bucket.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/matias_hed.jpg b/tests/notification/res/drawable-nodpi/matias_hed.jpg
deleted file mode 100644
index 8cc3081..0000000
--- a/tests/notification/res/drawable-nodpi/matias_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/page_hed.jpg b/tests/notification/res/drawable-nodpi/page_hed.jpg
deleted file mode 100644
index ea950c8..0000000
--- a/tests/notification/res/drawable-nodpi/page_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/romainguy_hed.jpg b/tests/notification/res/drawable-nodpi/romainguy_hed.jpg
deleted file mode 100644
index 5b7643e..0000000
--- a/tests/notification/res/drawable-nodpi/romainguy_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg b/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg
deleted file mode 100644
index 68473ba..0000000
--- a/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/add.png b/tests/notification/res/drawable-xhdpi/add.png
deleted file mode 100644
index 7226b3d..0000000
--- a/tests/notification/res/drawable-xhdpi/add.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png b/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png
deleted file mode 100644
index ca20a91..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_end_call.png b/tests/notification/res/drawable-xhdpi/ic_end_call.png
deleted file mode 100644
index c464a6d..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_media_next.png b/tests/notification/res/drawable-xhdpi/ic_media_next.png
deleted file mode 100644
index 4def965..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_media_next.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_menu_upload.png b/tests/notification/res/drawable-xhdpi/ic_menu_upload.png
deleted file mode 100644
index f1438ed..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_menu_upload.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/icon.png b/tests/notification/res/drawable-xhdpi/icon.png
deleted file mode 100644
index 189e85b..0000000
--- a/tests/notification/res/drawable-xhdpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png b/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png
deleted file mode 100644
index 658d04f..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png b/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png
deleted file mode 100644
index 5ae7782..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_email.png b/tests/notification/res/drawable-xhdpi/stat_notify_email.png
deleted file mode 100644
index 23c4672..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_email.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png b/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png
deleted file mode 100644
index 8719eff..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_sms.png b/tests/notification/res/drawable-xhdpi/stat_notify_sms.png
deleted file mode 100644
index 323cb3d..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_sms.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png b/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png
deleted file mode 100644
index 26dcda35..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png b/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png
deleted file mode 100644
index b8b2f8a..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png b/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png
deleted file mode 100644
index 12cae9f..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png b/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png
deleted file mode 100644
index db42b7c..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/layout/full_screen.xml b/tests/notification/res/layout/full_screen.xml
deleted file mode 100644
index 6ff7552..0000000
--- a/tests/notification/res/layout/full_screen.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <ImageView
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:src="@drawable/page_hed"
-        android:onClick="dismiss"
-        />
-</FrameLayout>
\ No newline at end of file
diff --git a/tests/notification/res/layout/main.xml b/tests/notification/res/layout/main.xml
deleted file mode 100644
index f5a740f..0000000
--- a/tests/notification/res/layout/main.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    >
-    <LinearLayout android:id="@+id/linearLayout1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_width="match_parent" android:layout_margin="35dp">
-        <Button android:id="@+id/button1" android:text="@string/post_button_label" android:layout_height="wrap_content" android:layout_width="match_parent" android:onClick="doPost"></Button>
-        <Button android:id="@+id/button2" android:text="@string/remove_button_label" android:layout_height="wrap_content" android:layout_width="match_parent" android:onClick="doRemove"></Button>
-    </LinearLayout>
-</FrameLayout>
diff --git a/tests/notification/res/values/dimens.xml b/tests/notification/res/values/dimens.xml
deleted file mode 100644
index 21e7bc3..0000000
--- a/tests/notification/res/values/dimens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2012, 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>
-    <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_width">64dp</dimen>
-    <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_height">64dp</dimen>
-</resources>
diff --git a/tests/notification/res/values/strings.xml b/tests/notification/res/values/strings.xml
deleted file mode 100644
index 80bf103..0000000
--- a/tests/notification/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="hello">Hello World, NotificationShowcaseActivity!</string>
-    <string name="app_name">NotificationShowcase</string>
-    <string name="post_button_label">Post Notifications</string>
-    <string name="remove_button_label">Remove Notifications</string>
-    <string name="answered">call answered</string>
-    <string name="ignored">call ignored</string>
-    <string name="full_screen_name">Full Screen Activity</string>
-</resources>
diff --git a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java b/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
deleted file mode 100644
index 5d639f6..0000000
--- a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2015 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.app;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.SystemClock;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.style.StyleSpan;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-import java.lang.reflect.Method;
-import java.lang.InterruptedException;
-import java.lang.NoSuchMethodError;
-import java.lang.NoSuchMethodException;
-import java.util.ArrayList;
-
-import com.android.frameworks.tests.notification.R;
-
-public class NotificationTests extends AndroidTestCase {
-    private static final String TAG = "NOTEST";
-    public static void L(String msg, Object... args) {
-        Log.v(TAG, (args == null || args.length == 0) ? msg : String.format(msg, args));
-    }
-
-    public static final String ACTION_CREATE = "create";
-    public static final int NOTIFICATION_ID = 31338;
-
-    public static final boolean SHOW_PHONE_CALL = false;
-    public static final boolean SHOW_INBOX = true;
-    public static final boolean SHOW_BIG_TEXT = true;
-    public static final boolean SHOW_BIG_PICTURE = true;
-    public static final boolean SHOW_MEDIA = true;
-    public static final boolean SHOW_STOPWATCH = false;
-    public static final boolean SHOW_SOCIAL = false;
-    public static final boolean SHOW_CALENDAR = false;
-    public static final boolean SHOW_PROGRESS = false;
-
-    private static Bitmap getBitmap(Context context, int resId) {
-        int largeIconWidth = (int) context.getResources()
-                .getDimension(R.dimen.notification_large_icon_width);
-        int largeIconHeight = (int) context.getResources()
-                .getDimension(R.dimen.notification_large_icon_height);
-        Drawable d = context.getResources().getDrawable(resId);
-        Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas(b);
-        d.setBounds(0, 0, largeIconWidth, largeIconHeight);
-        d.draw(c);
-        return b;
-    }
-
-    private static PendingIntent makeEmailIntent(Context context, String who) {
-        final Intent intent = new Intent(android.content.Intent.ACTION_SENDTO,
-                Uri.parse("mailto:" + who));
-        return PendingIntent.getActivity(
-                context, 0, intent,
-                PendingIntent.FLAG_CANCEL_CURRENT);
-    }
-
-    static final String[] LINES = new String[] {
-            "Uh oh",
-            "Getting kicked out of this room",
-            "I'll be back in 5-10 minutes.",
-            "And now \u2026 I have to find my shoes. \uD83D\uDC63",
-            "\uD83D\uDC5F \uD83D\uDC5F",
-            "\uD83D\uDC60 \uD83D\uDC60",
-    };
-    static final int MAX_LINES = 5;
-    public static Notification makeBigTextNotification(Context context, int update, int id,
-            long when) {
-        String personUri = null;
-        /*
-        Cursor c = null;
-        try {
-            String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY };
-            String selections = ContactsContract.Contacts.DISPLAY_NAME + " = 'Mike Cleron'";
-            final ContentResolver contentResolver = context.getContentResolver();
-            c = contentResolver.query(ContactsContract.Contacts.CONTENT_URI,
-                    projection, selections, null, null);
-            if (c != null && c.getCount() > 0) {
-                c.moveToFirst();
-                int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
-                int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
-                String lookupKey = c.getString(lookupIdx);
-                long contactId = c.getLong(idIdx);
-                Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
-                personUri = lookupUri.toString();
-            }
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        if (TextUtils.isEmpty(personUri)) {
-            Log.w(TAG, "failed to find contact for Mike Cleron");
-        } else {
-            Log.w(TAG, "Mike Cleron is " + personUri);
-        }
-        */
-
-        StringBuilder longSmsText = new StringBuilder();
-        int end = 2 + update;
-        if (end > LINES.length) {
-            end = LINES.length;
-        }
-        final int start = Math.max(0, end - MAX_LINES);
-        for (int i=start; i<end; i++) {
-            if (i >= LINES.length) break;
-            if (i > start) longSmsText.append("\n");
-            longSmsText.append(LINES[i]);
-        }
-        if (update > 2) {
-            when = System.currentTimeMillis();
-        }
-        Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle()
-                .bigText(longSmsText);
-        Notification bigText = new Notification.Builder(context)
-                .setContentTitle("Mike Cleron")
-                .setContentIntent(ToastService.getPendingIntent(context, "Clicked on bigText"))
-                .setContentText(longSmsText)
-                        //.setTicker("Mike Cleron: " + longSmsText)
-                .setWhen(when)
-                .setLargeIcon(getBitmap(context, R.drawable.bucket))
-                .setPriority(Notification.PRIORITY_HIGH)
-                .setNumber(update)
-                .setSmallIcon(R.drawable.stat_notify_talk_text)
-                .setStyle(bigTextStyle)
-                .setDefaults(Notification.DEFAULT_SOUND)
-                .addPerson(personUri)
-                .build();
-        return bigText;
-    }
-
-    public static Notification makeUploadNotification(Context context, int progress, long when) {
-        Notification.Builder uploadNotification = new Notification.Builder(context)
-                .setContentTitle("File Upload")
-                .setContentText("foo.txt")
-                .setPriority(Notification.PRIORITY_MIN)
-                .setContentIntent(ToastService.getPendingIntent(context, "Clicked on Upload"))
-                .setWhen(when)
-                .setSmallIcon(R.drawable.ic_menu_upload)
-                .setProgress(100, Math.min(progress, 100), false);
-        return uploadNotification.build();
-    }
-
-    static SpannableStringBuilder BOLD(CharSequence str) {
-        final SpannableStringBuilder ssb = new SpannableStringBuilder(str);
-        ssb.setSpan(new StyleSpan(Typeface.BOLD), 0, ssb.length(), 0);
-        return ssb;
-    }
-
-    public static class ToastService extends IntentService {
-
-        private static final String TAG = "ToastService";
-
-        private static final String ACTION_TOAST = "toast";
-
-        private Handler handler;
-
-        public ToastService() {
-            super(TAG);
-        }
-        public ToastService(String name) {
-            super(name);
-        }
-
-        @Override
-        public int onStartCommand(Intent intent, int flags, int startId) {
-            handler = new Handler();
-            return super.onStartCommand(intent, flags, startId);
-        }
-
-        @Override
-        protected void onHandleIntent(Intent intent) {
-            Log.v(TAG, "clicked a thing! intent=" + intent.toString());
-            if (intent.hasExtra("text")) {
-                final String text = intent.getStringExtra("text");
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        Toast.makeText(ToastService.this, text, Toast.LENGTH_LONG).show();
-                        Log.v(TAG, "toast " + text);
-                    }
-                });
-            }
-        }
-
-        public static PendingIntent getPendingIntent(Context context, String text) {
-            Intent toastIntent = new Intent(context, ToastService.class);
-            toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            toastIntent.setAction(ACTION_TOAST + ":" + text); // one per toast message
-            toastIntent.putExtra("text", text);
-            PendingIntent pi = PendingIntent.getService(
-                    context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            return pi;
-        }
-    }
-
-    public static void sleepIfYouCan(int ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {}
-    }
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-    }
-
-    public static String summarize(Notification n) {
-        return String.format("<notif title=\"%s\" icon=0x%08x view=%s>",
-                n.extras.get(Notification.EXTRA_TITLE),
-                n.icon,
-                String.valueOf(n.contentView));
-    }
-    
-    public void testCreate() throws Exception {
-        ArrayList<Notification> mNotifications = new ArrayList<Notification>();
-        NotificationManager noMa =
-                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
-        L("Constructing notifications...");
-        if (SHOW_BIG_TEXT) {
-            int bigtextId = mNotifications.size();
-            final long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = makeBigTextNotification(mContext, 0, bigtextId, System.currentTimeMillis());
-            L("  %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(n);
-        }
-
-        int uploadId = mNotifications.size();
-        long uploadWhen = System.currentTimeMillis();
-
-        if (SHOW_PROGRESS) {
-            mNotifications.add(makeUploadNotification(mContext, 0, uploadWhen));
-        }
-
-        if (SHOW_PHONE_CALL) {
-            int phoneId = mNotifications.size();
-            final PendingIntent fullscreenIntent
-                    = FullScreenActivity.getPendingIntent(mContext, phoneId);
-            final long time = SystemClock.currentThreadTimeMillis();
-            Notification phoneCall = new Notification.Builder(mContext)
-                    .setContentTitle("Incoming call")
-                    .setContentText("Matias Duarte")
-                    .setLargeIcon(getBitmap(mContext, R.drawable.matias_hed))
-                    .setSmallIcon(R.drawable.stat_sys_phone_call)
-                    .setDefaults(Notification.DEFAULT_SOUND)
-                    .setPriority(Notification.PRIORITY_MAX)
-                    .setContentIntent(fullscreenIntent)
-                    .setFullScreenIntent(fullscreenIntent, true)
-                    .addAction(R.drawable.ic_dial_action_call, "Answer",
-                            ToastService.getPendingIntent(mContext, "Clicked on Answer"))
-                    .addAction(R.drawable.ic_end_call, "Ignore",
-                            ToastService.getPendingIntent(mContext, "Clicked on Ignore"))
-                    .setOngoing(true)
-                    .addPerson(Uri.fromParts("tel", "1 (617) 555-1212", null).toString())
-                    .build();
-            L("  %s: create=%dms", phoneCall.toString(), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(phoneCall);
-        }
-
-        if (SHOW_STOPWATCH) {
-            final long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = new Notification.Builder(mContext)
-                    .setContentTitle("Stopwatch PRO")
-                    .setContentText("Counting up")
-                    .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Stopwatch"))
-                    .setSmallIcon(R.drawable.stat_notify_alarm)
-                    .setUsesChronometer(true)
-                    .build();
-            L("  %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(n);
-        }
-
-        if (SHOW_CALENDAR) {
-            final long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = new Notification.Builder(mContext)
-                    .setContentTitle("J Planning")
-                    .setContentText("The Botcave")
-                    .setWhen(System.currentTimeMillis())
-                    .setSmallIcon(R.drawable.stat_notify_calendar)
-                    .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on calendar event"))
-                    .setContentInfo("7PM")
-                    .addAction(R.drawable.stat_notify_snooze, "+10 min",
-                            ToastService.getPendingIntent(mContext, "snoozed 10 min"))
-                    .addAction(R.drawable.stat_notify_snooze_longer, "+1 hour",
-                            ToastService.getPendingIntent(mContext, "snoozed 1 hr"))
-                    .addAction(R.drawable.stat_notify_email, "Email",
-                            ToastService.getPendingIntent(mContext,
-                                    "Congratulations, you just destroyed someone's inbox zero"))
-                    .build();
-            L("  %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(n);
-        }
-
-        if (SHOW_BIG_PICTURE) {
-            BitmapDrawable d =
-                    (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.romainguy_rockaway);
-            final long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = new Notification.Builder(mContext)
-                        .setContentTitle("Romain Guy")
-                        .setContentText("I was lucky to find a Canon 5D Mk III at a local Bay Area "
-                                + "store last week but I had not been able to try it in the field "
-                                + "until tonight. After a few days of rain the sky finally cleared "
-                                + "up. Rockaway Beach did not disappoint and I was finally able to "
-                                + "see what my new camera feels like when shooting landscapes.")
-                        .setSmallIcon(android.R.drawable.stat_notify_chat)
-                        .setContentIntent(
-                                ToastService.getPendingIntent(mContext, "Clicked picture"))
-                        .setLargeIcon(getBitmap(mContext, R.drawable.romainguy_hed))
-                        .addAction(R.drawable.add, "Add to Gallery",
-                                ToastService.getPendingIntent(mContext, "Added"))
-                        .setStyle(new Notification.BigPictureStyle()
-                                .bigPicture(d.getBitmap()))
-                        .build();
-            L("  %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(n);
-        }
-
-        if (SHOW_INBOX) {
-            final long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = new Notification.Builder(mContext)
-                    .setContentTitle("New mail")
-                    .setContentText("3 new messages")
-                    .setSubText("example@gmail.com")
-                    .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Mail"))
-                    .setSmallIcon(R.drawable.stat_notify_email)
-                    .setStyle(new Notification.InboxStyle()
-                                    .setSummaryText("example@gmail.com")
-                                    .addLine(BOLD("Alice:").append(" hey there!"))
-                                    .addLine(BOLD("Bob:").append(" hi there!"))
-                                    .addLine(BOLD("Charlie:").append(" Iz IN UR EMAILZ!!"))
-                    ).build();
-            L("  %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(n);
-        }
-
-        if (SHOW_SOCIAL) {
-            final long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = new Notification.Builder(mContext)
-                    .setContentTitle("Social Network")
-                    .setContentText("You were mentioned in a post")
-                    .setContentInfo("example@gmail.com")
-                    .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Social"))
-                    .setSmallIcon(android.R.drawable.stat_notify_chat)
-                    .setPriority(Notification.PRIORITY_LOW)
-                    .build();
-            L("  %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
-            mNotifications.add(n);
-        }
-
-        L("Posting notifications...");
-        for (int i=0; i<mNotifications.size(); i++) {
-            final int count = 4;
-            for (int j=0; j<count; j++) {
-                long time = SystemClock.currentThreadTimeMillis();
-                final Notification n = mNotifications.get(i);
-                noMa.notify(NOTIFICATION_ID + i, n);
-                time = SystemClock.currentThreadTimeMillis() - time;
-                L("  %s: notify=%dms (%d/%d)", summarize(n), time,
-                        j + 1, count);
-                sleepIfYouCan(150);
-            }
-        }
-
-        sleepIfYouCan(1000);
-
-        L("Canceling notifications...");
-        for (int i=0; i<mNotifications.size(); i++) {
-            final Notification n = mNotifications.get(i);
-            long time = SystemClock.currentThreadTimeMillis();
-            noMa.cancel(NOTIFICATION_ID + i);
-            time = SystemClock.currentThreadTimeMillis() - time;
-            L("  %s: cancel=%dms", summarize(n), time);
-        }
-
-        sleepIfYouCan(500);
-
-        L("Parceling notifications...");
-        // we want to be able to use this test on older OSes that do not have getOpenAshmemSize
-        Method getOpenAshmemSize = null;
-        try {
-            getOpenAshmemSize = Parcel.class.getMethod("getOpenAshmemSize");
-        } catch (NoSuchMethodException ex) {
-        }
-        for (int i=0; i<mNotifications.size(); i++) {
-            Parcel p = Parcel.obtain();
-            {
-                final Notification n = mNotifications.get(i);
-                long time = SystemClock.currentThreadTimeMillis();
-                n.writeToParcel(p, 0);
-                time = SystemClock.currentThreadTimeMillis() - time;
-                L("  %s: write parcel=%dms size=%d ashmem=%s",
-                        summarize(n), time, p.dataPosition(),
-                        (getOpenAshmemSize != null)
-                            ? getOpenAshmemSize.invoke(p)
-                            : "???");
-                p.setDataPosition(0);
-            }
-
-            long time = SystemClock.currentThreadTimeMillis();
-            final Notification n2 = Notification.CREATOR.createFromParcel(p);
-            time = SystemClock.currentThreadTimeMillis() - time;
-            L("  %s: parcel read=%dms", summarize(n2), time);
-
-            time = SystemClock.currentThreadTimeMillis();
-            noMa.notify(NOTIFICATION_ID + i, n2);
-            time = SystemClock.currentThreadTimeMillis() - time;
-            L("  %s: notify=%dms", summarize(n2), time);
-        }
-
-        sleepIfYouCan(500);
-
-        L("Canceling notifications...");
-        for (int i=0; i<mNotifications.size(); i++) {
-            long time = SystemClock.currentThreadTimeMillis();
-            final Notification n = mNotifications.get(i);
-            noMa.cancel(NOTIFICATION_ID + i);
-            time = SystemClock.currentThreadTimeMillis() - time;
-            L("  %s: cancel=%dms", summarize(n), time);
-        }
-
-
-//            if (SHOW_PROGRESS) {
-//                ProgressService.startProgressUpdater(this, uploadId, uploadWhen, 0);
-//            }
-    }
-
-    public static class FullScreenActivity extends Activity {
-        public static final String EXTRA_ID = "id";
-
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            setContentView(R.layout.full_screen);
-            final Intent intent = getIntent();
-            if (intent != null && intent.hasExtra(EXTRA_ID)) {
-                final int id = intent.getIntExtra(EXTRA_ID, -1);
-                if (id >= 0) {
-                    NotificationManager noMa =
-                            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-                    noMa.cancel(NOTIFICATION_ID + id);
-                }
-            }
-        }
-
-        public void dismiss(View v) {
-            finish();
-        }
-
-        public static PendingIntent getPendingIntent(Context context, int id) {
-            Intent fullScreenIntent = new Intent(context, FullScreenActivity.class);
-            fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            fullScreenIntent.putExtra(EXTRA_ID, id);
-            PendingIntent pi = PendingIntent.getActivity(
-                    context, 22, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            return pi;
-        }
-    }
-}
-
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index a1a39ff..359ef83 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -125,6 +125,17 @@
                 TUNNEL_CONNECTION_PARAMS);
     }
 
+    private static VcnGatewayConnectionConfig.Builder newBuilderMinimal() {
+        final VcnGatewayConnectionConfig.Builder builder =
+                new VcnGatewayConnectionConfig.Builder(
+                        "newBuilderMinimal", TUNNEL_CONNECTION_PARAMS);
+        for (int caps : EXPOSED_CAPS) {
+            builder.addExposedCapability(caps);
+        }
+
+        return builder;
+    }
+
     private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions(
             VcnGatewayConnectionConfig.Builder builder,
             Set<Integer> gatewayOptions,
@@ -273,6 +284,7 @@
 
         assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
         assertEquals(MAX_MTU, config.getMaxMtu());
+        assertTrue(config.isSafeModeEnabled());
 
         assertFalse(
                 config.hasGatewayOption(
@@ -290,6 +302,13 @@
     }
 
     @Test
+    public void testBuilderAndGettersSafeModeDisabled() {
+        final VcnGatewayConnectionConfig config = newBuilderMinimal().enableSafeMode(false).build();
+
+        assertFalse(config.isSafeModeEnabled());
+    }
+
+    @Test
     public void testPersistableBundle() {
         final VcnGatewayConnectionConfig config = buildTestConfig();
 
@@ -305,6 +324,13 @@
     }
 
     @Test
+    public void testPersistableBundleSafeModeDisabled() {
+        final VcnGatewayConnectionConfig config = newBuilderMinimal().enableSafeMode(false).build();
+
+        assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+    }
+
+    @Test
     public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
         PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
         configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
@@ -411,4 +437,18 @@
         assertEquals(config, configEqual);
         assertNotEquals(config, configNotEqual);
     }
+
+    @Test
+    public void testSafeModeEnableDisableEquality() throws Exception {
+        final VcnGatewayConnectionConfig config = newBuilderMinimal().build();
+        final VcnGatewayConnectionConfig configEqual = newBuilderMinimal().build();
+
+        assertEquals(config.isSafeModeEnabled(), configEqual.isSafeModeEnabled());
+
+        final VcnGatewayConnectionConfig configNotEqual =
+                newBuilderMinimal().enableSafeMode(false).build();
+
+        assertEquals(config, configEqual);
+        assertNotEquals(config, configNotEqual);
+    }
 }
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 1b1e93bd..a08f385 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -323,6 +323,7 @@
             "should only be used together with the --static-lib flag.",
         &options_.merge_only);
     AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
+    AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_);
   }
 
   int Action(const std::vector<std::string>& args) override;
@@ -347,6 +348,7 @@
   std::optional<std::string> stable_id_file_path_;
   std::vector<std::string> split_args_;
   std::optional<std::string> trace_folder_;
+  std::vector<std::string> feature_flags_args_;
 };
 
 }// namespace aapt