Merge "[aapt2] Generate @FlaggedApi annotations" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 95b6155..003b7f8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@
     ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
     ":android.security.flags-aconfig-java{.generated_srcjars}",
     ":android.view.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
     ":camera_platform_flags_core_java_lib{.generated_srcjars}",
     ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
     ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
@@ -40,6 +41,8 @@
     ":android.app.flags-aconfig-java{.generated_srcjars}",
     ":android.credentials.flags-aconfig-java{.generated_srcjars}",
     ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
+    ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
+    ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
 ]
 
 filegroup {
@@ -240,6 +243,24 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// View.accessibility
+aconfig_declarations {
+    name: "android.view.accessibility.flags-aconfig",
+    package: "android.view.accessibility",
+    srcs: ["core/java/android/view/accessibility/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.view.accessibility.flags-aconfig-java",
+    aconfig_declarations: "android.view.accessibility.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+cc_aconfig_library {
+    name: "aconfig_view_accessibility_flags_c_lib",
+    aconfig_declarations: "android.view.accessibility.flags-aconfig",
+}
+
 // Widget
 aconfig_declarations {
     name: "android.widget.flags-aconfig",
@@ -383,3 +404,32 @@
     aconfig_declarations: "android.view.contentprotection.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Voice
+aconfig_declarations {
+    name: "android.service.voice.flags-aconfig",
+    package: "android.service.voice.flags",
+    srcs: ["core/java/android/service/voice/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.service.voice.flags-aconfig-java",
+    aconfig_declarations: "android.service.voice.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Autofill
+aconfig_declarations {
+    name: "android.service.autofill.flags-aconfig",
+    package: "android.service.autofill",
+    srcs: [
+        "services/autofill/bugfixes.aconfig",
+        "services/autofill/features.aconfig"
+    ],
+}
+
+java_aconfig_library {
+    name: "android.service.autofill.flags-aconfig-java",
+    aconfig_declarations: "android.service.autofill.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index a4a0b4b..887f7fe 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,6 +13,10 @@
     name: "service-jobscheduler",
     installable: true,
 
+    defaults: [
+        "service-jobscheduler-aconfig-libraries",
+    ],
+
     srcs: [
         "java/**/*.java",
         ":framework-jobscheduler-shared-srcs",
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
new file mode 100644
index 0000000..24ecd3d
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -0,0 +1,29 @@
+// JobScheduler
+aconfig_declarations {
+    name: "service-job.flags-aconfig",
+    package: "com.android.server.job",
+    srcs: [
+        "job.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "service-jobscheduler-job.flags-aconfig-java",
+    aconfig_declarations: "service-job.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    visibility: ["//frameworks/base:__subpackages__"],
+}
+
+service_jobscheduler_aconfig_srcjars = [
+    ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
+]
+
+// Aconfig declarations and libraries for the core framework
+java_defaults {
+    name: "service-jobscheduler-aconfig-libraries",
+    // Add java_aconfig_libraries to here to add them to the core framework
+    srcs: service_jobscheduler_aconfig_srcjars,
+    // Add aconfig-annotations-lib as a dependency for the optimization
+    libs: ["aconfig-annotations-lib"],
+    visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
new file mode 100644
index 0000000..4e3cb7d
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.job"
+
+flag {
+    name: "relax_prefetch_connectivity_constraint_only_on_charger"
+    namespace: "backstagepower"
+    description: "Only relax a prefetch job's connectivity constraint when the device is charging"
+    bug: "299329948"
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f252a0b..158d914 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1030,6 +1030,12 @@
                 "light_idle_to_initial_flex";
         private static final String KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX = "light_max_idle_to_flex";
         private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
+        private static final String KEY_LIGHT_IDLE_INCREASE_LINEARLY =
+                "light_idle_increase_linearly";
+        private static final String KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS =
+                "light_idle_linear_increase_factor_ms";
+        private static final String KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS =
+                "light_idle_flex_linear_increase_factor_ms";
         private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
         private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
                 "light_idle_maintenance_min_budget";
@@ -1079,6 +1085,10 @@
         private long mDefaultLightIdleTimeoutMaxFlex =
                 !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
         private float mDefaultLightIdleFactor = 2f;
+        private boolean mDefaultLightIdleIncreaseLinearly;
+        private long mDefaultLightIdleLinearIncreaseFactorMs = mDefaultLightIdleTimeout;
+        private long mDefaultLightIdleFlexLinearIncreaseFactorMs =
+                mDefaultLightIdleTimeoutInitialFlex;
         private long mDefaultLightMaxIdleTimeout =
                 !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
         private long mDefaultLightIdleMaintenanceMinBudget =
@@ -1174,6 +1184,37 @@
         public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
 
         /**
+         * Whether to increase the light idle mode time linearly or exponentially.
+         * If true, will increase linearly
+         * (i.e. {@link #LIGHT_IDLE_TIMEOUT} + x * {@link #LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS}).
+         * If false, will increase by exponentially
+         * (i.e. {@link #LIGHT_IDLE_TIMEOUT} * ({@link #LIGHT_IDLE_FACTOR} ^ x)).
+         * This will also impact how the light idle flex value
+         * ({@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX}) is increased (using
+         * {@link #LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS} for the linear increase)..
+         *
+         * @see #KEY_LIGHT_IDLE_INCREASE_LINEARLY
+         */
+        public boolean LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly;
+
+        /**
+         * Amount of time to increase the light idle time by, if increasing it linearly.
+         *
+         * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS
+         * @see #LIGHT_IDLE_INCREASE_LINEARLY
+         */
+        public long LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs;
+
+        /**
+         * Amount of time to increase the light idle flex time by, if increasing it linearly.
+         *
+         * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS
+         * @see #LIGHT_IDLE_INCREASE_LINEARLY
+         */
+        public long LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS =
+                mDefaultLightIdleFlexLinearIncreaseFactorMs;
+
+        /**
          * This is the maximum time we will stay in light idle mode.
          *
          * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
@@ -1409,6 +1450,16 @@
                     mDefaultLightIdleTimeoutMaxFlex);
             mDefaultLightIdleFactor = res.getFloat(
                     com.android.internal.R.integer.device_idle_light_idle_factor);
+            mDefaultLightIdleIncreaseLinearly = res.getBoolean(
+                    com.android.internal.R.bool.device_idle_light_idle_increase_linearly);
+            mDefaultLightIdleLinearIncreaseFactorMs = getTimeout(res.getInteger(
+                    com.android.internal.R.integer
+                            .device_idle_light_idle_linear_increase_factor_ms),
+                    mDefaultLightIdleLinearIncreaseFactorMs);
+            mDefaultLightIdleFlexLinearIncreaseFactorMs = getTimeout(res.getInteger(
+                    com.android.internal.R.integer
+                            .device_idle_light_idle_flex_linear_increase_factor_ms),
+                    mDefaultLightIdleFlexLinearIncreaseFactorMs);
             mDefaultLightMaxIdleTimeout = getTimeout(
                     res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms),
                     mDefaultLightMaxIdleTimeout);
@@ -1487,6 +1538,9 @@
             LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mDefaultLightIdleTimeoutInitialFlex;
             LIGHT_IDLE_TIMEOUT_MAX_FLEX = mDefaultLightIdleTimeoutMaxFlex;
             LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
+            LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly;
+            LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs;
+            LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleFlexLinearIncreaseFactorMs;
             LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
             LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
             LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
@@ -1556,6 +1610,21 @@
                             LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
                                     KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
                             break;
+                        case KEY_LIGHT_IDLE_INCREASE_LINEARLY:
+                            LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean(
+                                    KEY_LIGHT_IDLE_INCREASE_LINEARLY,
+                                    mDefaultLightIdleIncreaseLinearly);
+                            break;
+                        case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS:
+                            LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
+                                    KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
+                                    mDefaultLightIdleLinearIncreaseFactorMs);
+                            break;
+                        case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS:
+                            LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
+                                    KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
+                                    mDefaultLightIdleFlexLinearIncreaseFactorMs);
+                            break;
                         case KEY_LIGHT_MAX_IDLE_TIMEOUT:
                             LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
                                     KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
@@ -1716,6 +1785,20 @@
             pw.print(LIGHT_IDLE_FACTOR);
             pw.println();
 
+            pw.print("    "); pw.print(KEY_LIGHT_IDLE_INCREASE_LINEARLY); pw.print("=");
+            pw.print(LIGHT_IDLE_INCREASE_LINEARLY);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS);
+            pw.print("=");
+            pw.print(LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS);
+            pw.print("=");
+            pw.print(LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS);
+            pw.println();
+
             pw.print("    "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT, pw);
             pw.println();
@@ -3694,10 +3777,18 @@
                 }
                 mMaintenanceStartTime = 0;
                 scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex, true);
-                mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
-                        (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
-                mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX,
-                        (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
+                if (!mConstants.LIGHT_IDLE_INCREASE_LINEARLY) {
+                    mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
+                            (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
+                    mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+                            (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
+                } else {
+                    mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
+                            mNextLightIdleDelay + mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS);
+                    mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+                            mNextLightIdleDelayFlex
+                                    + mConstants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS);
+                }
                 moveToLightStateLocked(LIGHT_STATE_IDLE, reason);
                 addEvent(EVENT_LIGHT_IDLE, null);
                 mGoingIdleWakeLock.acquire();
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 43d2ae9..f47766e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -1335,13 +1335,13 @@
     private void handleOpTimeoutLocked() {
         switch (mVerb) {
             case VERB_BINDING:
-                onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
+                // The system may have been too busy. Don't drop the job or trigger an ANR.
+                onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true,
                         /* texCounterMetricId */
                         "job_scheduler.value_cntr_w_uid_slow_app_response_binding",
                         /* debugReason */ "timed out while binding",
                         /* anrMessage */ "Timed out while trying to bind",
-                        CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
-                            mRunningJob.getUid()));
+                        /* triggerAnr */ false);
                 break;
             case VERB_STARTING:
                 // Client unresponsive - wedged or failed to respond in time. We don't really
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 6d938de..45f15db 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
@@ -22,6 +22,8 @@
 
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER;
+import static com.android.server.job.Flags.relaxPrefetchConnectivityConstraintOnlyOnCharger;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -929,6 +931,11 @@
             // Need to at least know the estimated download bytes for a prefetch job.
             return false;
         }
+        if (relaxPrefetchConnectivityConstraintOnlyOnCharger()) {
+            if (!mService.isBatteryCharging()) {
+                return false;
+            }
+        }
 
         // See if we match after relaxing any unmetered request
         final NetworkCapabilities.Builder builder =
@@ -1735,6 +1742,14 @@
             Predicate<JobStatus> predicate) {
         final long nowElapsed = sElapsedRealtimeClock.millis();
 
+        pw.println("Aconfig flags:");
+        pw.increaseIndent();
+        pw.print(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER,
+                relaxPrefetchConnectivityConstraintOnlyOnCharger());
+        pw.println();
+        pw.decreaseIndent();
+        pw.println();
+
         if (mRequestedWhitelistJobs.size() > 0) {
             pw.print("Requested standby exceptions:");
             for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
diff --git a/api/Android.bp b/api/Android.bp
index 45e70719..222275f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -82,7 +82,6 @@
         "framework-media",
         "framework-mediaprovider",
         "framework-ondevicepersonalization",
-        "framework-pdf",
         "framework-permission",
         "framework-permission-s",
         "framework-scheduling",
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index fbcaa52..8d8fc12 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -195,6 +195,8 @@
 
 doc_defaults {
     name: "framework-docs-default",
+    sdk_version: "none",
+    system_modules: "none",
     libs: framework_docs_only_libs + [
         "stub-annotations",
         "unsupportedappusage",
@@ -209,6 +211,7 @@
     custom_template: "droiddoc-templates-sdk",
     resourcesdir: "docs/html/reference/images/",
     resourcesoutdir: "reference/android/images/",
+    lint_baseline: "javadoc-lint-baseline",
     hdf: [
         "dac true",
         "sdk.codename O",
diff --git a/api/api.go b/api/api.go
index 83804c6..8df6dab 100644
--- a/api/api.go
+++ b/api/api.go
@@ -115,6 +115,7 @@
 }
 
 type Bazel_module struct {
+	Label              *string
 	Bp2build_available *bool
 }
 type bazelProperties struct {
@@ -141,6 +142,8 @@
 	ModuleTag string
 	// public, system, module-lib or system-server
 	Scope string
+	// True if there is a bp2build definition for this module
+	Bp2buildDefined bool
 }
 
 func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
@@ -153,8 +156,10 @@
 	if txt.Scope != "public" {
 		filename = txt.Scope + "-" + filename
 	}
+	moduleName := ctx.ModuleName() + "-" + filename
+
 	props := genruleProps{}
-	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
+	props.Name = proptools.StringPtr(moduleName)
 	props.Tools = []string{"metalava"}
 	props.Out = []string{filename}
 	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
@@ -172,7 +177,20 @@
 		},
 	}
 	props.Visibility = []string{"//visibility:public"}
-	ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable)
+	bazelProps := bazelProperties{
+		&Bazel_module{
+			Bp2build_available: proptools.BoolPtr(false),
+		},
+	}
+	if txt.Bp2buildDefined {
+		moduleDir := ctx.ModuleDir()
+		if moduleDir == android.Bp2BuildTopLevel {
+			moduleDir = ""
+		}
+		label := fmt.Sprintf("//%s:%s", moduleDir, moduleName)
+		bazelProps.Label = &label
+	}
+	ctx.CreateModule(genrule.GenRuleFactory, &props, &bazelProps)
 }
 
 func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
@@ -304,38 +322,43 @@
 
 	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
 	distFilename := []string{"android.txt", "android-removed.txt"}
+	bp2BuildDefined := []bool{true, false}
 	for i, f := range []string{"current.txt", "removed.txt"} {
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename:  f,
-			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-" + f,
-			Modules:      bootclasspath,
-			ModuleTag:    "{.public" + tagSuffix[i],
-			Scope:        "public",
+			TxtFilename:     f,
+			DistFilename:    distFilename[i],
+			BaseTxt:         ":non-updatable-" + f,
+			Modules:         bootclasspath,
+			ModuleTag:       "{.public" + tagSuffix[i],
+			Scope:           "public",
+			Bp2buildDefined: bp2BuildDefined[i],
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename:  f,
-			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-system-" + f,
-			Modules:      bootclasspath,
-			ModuleTag:    "{.system" + tagSuffix[i],
-			Scope:        "system",
+			TxtFilename:     f,
+			DistFilename:    distFilename[i],
+			BaseTxt:         ":non-updatable-system-" + f,
+			Modules:         bootclasspath,
+			ModuleTag:       "{.system" + tagSuffix[i],
+			Scope:           "system",
+			Bp2buildDefined: bp2BuildDefined[i],
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename:  f,
-			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-module-lib-" + f,
-			Modules:      bootclasspath,
-			ModuleTag:    "{.module-lib" + tagSuffix[i],
-			Scope:        "module-lib",
+			TxtFilename:     f,
+			DistFilename:    distFilename[i],
+			BaseTxt:         ":non-updatable-module-lib-" + f,
+			Modules:         bootclasspath,
+			ModuleTag:       "{.module-lib" + tagSuffix[i],
+			Scope:           "module-lib",
+			Bp2buildDefined: bp2BuildDefined[i],
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename:  f,
-			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-system-server-" + f,
-			Modules:      system_server_classpath,
-			ModuleTag:    "{.system-server" + tagSuffix[i],
-			Scope:        "system-server",
+			TxtFilename:     f,
+			DistFilename:    distFilename[i],
+			BaseTxt:         ":non-updatable-system-server-" + f,
+			Modules:         system_server_classpath,
+			ModuleTag:       "{.system-server" + tagSuffix[i],
+			Scope:           "system-server",
+			Bp2buildDefined: bp2BuildDefined[i],
 		})
 	}
 	for _, txt := range textFiles {
diff --git a/api/api_test.go b/api/api_test.go
index 1f4c2af..70f2162 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -33,6 +33,8 @@
 	t.Helper()
 	runCombinedApisTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {
 		ctx.RegisterModuleType("java_defaults", java.DefaultsFactory)
+		ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory)
+		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	})
 }
 
@@ -44,6 +46,33 @@
     bootclasspath: ["bcp"],
     system_server_classpath: ["ssc"],
 }
+
+java_sdk_library {
+		name: "bcp",
+		srcs: ["a.java", "b.java"],
+		shared_library: false,
+}
+java_sdk_library {
+		name: "ssc",
+		srcs: ["a.java", "b.java"],
+		shared_library: false,
+}
+filegroup {
+    name: "non-updatable-current.txt",
+    srcs: ["current.txt"],
+}
+filegroup {
+    name: "non-updatable-system-current.txt",
+    srcs: ["system-current.txt"],
+}
+filegroup {
+    name: "non-updatable-module-lib-current.txt",
+    srcs: ["system-removed.txt"],
+}
+filegroup {
+    name: "non-updatable-system-server-current.txt",
+    srcs: ["system-lint-baseline.txt"],
+}
 `,
 		Filesystem: map[string]string{
 			"a/Android.bp": `
@@ -51,27 +80,35 @@
 				name: "android.jar_defaults",
 			}
 			`,
+			"api/current.txt":        "",
+			"api/removed.txt":        "",
+			"api/system-current.txt": "",
+			"api/system-removed.txt": "",
+			"api/test-current.txt":   "",
+			"api/test-removed.txt":   "",
 		},
+		StubbedBuildDefinitions:    []string{"bcp", "ssc", "non-updatable-current.txt", "non-updatable-system-current.txt", "non-updatable-module-lib-current.txt", "non-updatable-system-server-current.txt"},
+		ExpectedHandcraftedModules: []string{"foo-current.txt", "foo-system-current.txt", "foo-module-lib-current.txt", "foo-system-server-current.txt"},
 		ExpectedBazelTargets: []string{
 			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-current.txt", bp2build.AttrNameToString{
 				"scope": `"public"`,
-				"base":  `":non-updatable-current.txt__BP2BUILD__MISSING__DEP"`,
-				"deps":  `[":bcp__BP2BUILD__MISSING__DEP"]`,
+				"base":  `":non-updatable-current.txt"`,
+				"deps":  `[":bcp"]`,
 			}),
 			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-current.txt", bp2build.AttrNameToString{
 				"scope": `"system"`,
-				"base":  `":non-updatable-system-current.txt__BP2BUILD__MISSING__DEP"`,
-				"deps":  `[":bcp__BP2BUILD__MISSING__DEP"]`,
+				"base":  `":non-updatable-system-current.txt"`,
+				"deps":  `[":bcp"]`,
 			}),
 			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-module-lib-current.txt", bp2build.AttrNameToString{
 				"scope": `"module-lib"`,
-				"base":  `":non-updatable-module-lib-current.txt__BP2BUILD__MISSING__DEP"`,
-				"deps":  `[":bcp__BP2BUILD__MISSING__DEP"]`,
+				"base":  `":non-updatable-module-lib-current.txt"`,
+				"deps":  `[":bcp"]`,
 			}),
 			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-server-current.txt", bp2build.AttrNameToString{
 				"scope": `"system-server"`,
-				"base":  `":non-updatable-system-server-current.txt__BP2BUILD__MISSING__DEP"`,
-				"deps":  `[":ssc__BP2BUILD__MISSING__DEP"]`,
+				"base":  `":non-updatable-system-server-current.txt"`,
+				"deps":  `[":ssc"]`,
 			}),
 		},
 	})
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
new file mode 100644
index 0000000..762d9d1
--- /dev/null
+++ b/api/javadoc-lint-baseline
@@ -0,0 +1,336 @@
+android/adservices/ondevicepersonalization/DownloadCompletedInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedInput [101]
+android/adservices/ondevicepersonalization/DownloadCompletedOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedOutput [101]
+android/adservices/ondevicepersonalization/EventLogRecord.java:13: lint: Unresolved link/see tag "RequestRecordRecord" in android.adservices.ondevicepersonalization.EventLogRecord [101]
+android/adservices/ondevicepersonalization/EventUrlProvider.java:43: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onEvent IsolatedWorker#onEvent" in android.adservices.ondevicepersonalization.EventUrlProvider [101]
+android/adservices/ondevicepersonalization/ExecuteInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteInput [101]
+android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
+android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute() OnDevicePersonalizationManager#execute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
+android/adservices/ondevicepersonalization/ExecuteOutput.java:31: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
+android/adservices/ondevicepersonalization/ExecuteOutput.java:93: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput.Builder [101]
+android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.IsolatedService [101]
+android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onWebViewEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
+android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "WebView" in android.adservices.ondevicepersonalization.IsolatedService [101]
+android/adservices/ondevicepersonalization/IsolatedWorker.java:9: lint: Unresolved link/see tag "RunTimeException" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
+android/adservices/ondevicepersonalization/IsolatedWorker.java:57: lint: Unresolved link/see tag "#onExecute()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
+android/adservices/ondevicepersonalization/IsolatedWorker.java:74: lint: Unresolved link/see tag "#onRender()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:-11: lint: Unresolved link/see tag "requestSurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedService#onExecute() IsolatedService#onExecute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:19: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView#getHostToken()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "execute" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "#execute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:64: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:69: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:70: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
+android/adservices/ondevicepersonalization/RenderInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderInput [101]
+android/adservices/ondevicepersonalization/RenderInput.java:53: lint: Unresolved link/see tag "onExecute" in android.adservices.ondevicepersonalization.RenderInput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderOutput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#requestSurfacePackage() OnDevicePersonalizationManager#requestSurfacePackage()" in android.adservices.ondevicepersonalization.RenderOutput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:41: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:52: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:114: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
+android/adservices/ondevicepersonalization/RenderOutput.java:127: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
+android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.RenderingConfig [101]
+android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderingConfig [101]
+android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderingConfig [101]
+android/adservices/ondevicepersonalization/RenderingConfig.java:33: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig [101]
+android/adservices/ondevicepersonalization/RenderingConfig.java:85: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig.Builder [101]
+android/adservices/ondevicepersonalization/RequestLogRecord.java:19: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RequestLogRecord [101]
+android/adservices/ondevicepersonalization/SurfacePackageToken.java:20: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.SurfacePackageToken [101]
+android/adservices/ondevicepersonalization/WebViewEventInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
+android/adservices/ondevicepersonalization/WebViewEventInput.java:30: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
+android/adservices/ondevicepersonalization/WebViewEventInput.java:41: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.EventUrlProvider#createEventTrackingUrlWithResponse() EventUrlProvider#createEventTrackingUrlWithResponse()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
+android/adservices/ondevicepersonalization/WebViewEventOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventOutput [101]
+android/app/ActivityOptions.java:366: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101]
+android/app/ActivityOptions.java:370: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101]
+android/app/ActivityOptions.java:384: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101]
+android/app/ApplicationStartInfo.java:96: lint: Unresolved link/see tag "#START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE" in android.app.ApplicationStartInfo [101]
+android/app/BroadcastOptions.java:132: lint: Unresolved link/see tag "#setDeliveryGroupMatchingFilter(android.content.IntentFilter)" in android.app.BroadcastOptions [101]
+android/app/GrammaticalInflectionManager.java:60: lint: Unresolved link/see tag "android.os.Environment#getDataSystemCeDirectory(int)" in android.app.GrammaticalInflectionManager [101]
+android/app/Notification.java:509: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.app.Notification [101]
+android/app/Notification.java:650: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.app.Notification [101]
+android/app/Notification.java:1866: lint: Unresolved link/see tag "/*missing*/" in android.app.Notification.Action [101]
+android/app/Notification.java:4796: lint: Unresolved link/see tag "android.content.pm.ShortcutInfo#setLongLived() ShortcutInfo#setLongLived()" in android.app.Notification.MessagingStyle [101]
+android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyManager.java:7428: lint: Unresolved link/see tag "android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Drawables DevicePolicyResources.Drawables" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyManager [101]
+android/app/admin/DevicePolicyResourcesManager.java:179: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyResourcesManager [101]
+android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
+android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
+android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.AppSearchSession [101]
+android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.AppSearchSession [101]
+android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#isFeatureSupported" in android.app.appsearch.AppSearchSession [101]
+android/app/appsearch/JoinSpec.java:219: lint: Unresolved link/see tag "android.app.appsearch.SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE" in android.app.appsearch.JoinSpec.Builder [101]
+android/app/appsearch/SearchSpec.java:230: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec [101]
+android/app/appsearch/SearchSpec.java:237: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec [101]
+android/app/appsearch/SearchSpec.java:244: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec [101]
+android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
+android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
+android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101]
+android/app/job/JobParameters.java:128: lint: Unresolved link/see tag "android.app.job.JobInfo.Builder#setPeriodic(boolean) periodic jobs" in android.app.job.JobParameters [101]
+android/app/sdksandbox/AppOwnedSdkSandboxInterface.java:9: lint: Unresolved link/see tag "SdkSandboxController#getAppOwnedSdkSandboxInterfaces" in android.app.sdksandbox.AppOwnedSdkSandboxInterface [101]
+android/app/sdksandbox/SdkSandboxManager.java:112: lint: Unresolved link/see tag "AppOwnedSdkSandboxInterfaces" in android.app.sdksandbox.SdkSandboxManager [101]
+android/companion/CompanionDeviceService.java:273: lint: Unresolved link/see tag "android.companion.AssociationInfo#isSelfManaged() self-managed" in android.companion.CompanionDeviceService [101]
+android/companion/CompanionDeviceService.java:282: lint: Unresolved link/see tag "android.companion.AssociationInfo#isSelfManaged() self-managed" in android.companion.CompanionDeviceService [101]
+android/companion/virtual/VirtualDevice.java:15: lint: Unresolved link/see tag "android.companion.virtual.VirtualDeviceManager.VirtualDevice VirtualDeviceManager.VirtualDevice" in android.companion.virtual.VirtualDevice [101]
+android/companion/virtual/VirtualDevice.java:70: lint: Unresolved link/see tag "android.companion.virtual.VirtualDeviceParams.Builder#setName(String)" in android.companion.virtual.VirtualDevice [101]
+android/content/AttributionSource.java:291: lint: Unresolved link/see tag "setNextAttributionSource" in android.content.AttributionSource.Builder [101]
+android/content/Context.java:2872: lint: Unresolved link/see tag "android.telephony.MmsManager" in android.content.Context [101]
+android/content/Intent.java:4734: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
+android/content/Intent.java:4760: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
+android/content/Intent.java:4778: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
+android/content/Intent.java:4802: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
+android/content/om/OverlayIdentifier.java:20: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)" in android.content.om.OverlayIdentifier [101]
+android/content/om/OverlayInfo.java:78: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)" in android.content.om.OverlayInfo [101]
+android/content/om/OverlayManager.java:9: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction#commit()" in android.content.om.OverlayManager [101]
+android/content/pm/PackageInstaller.java:2232: lint: Unresolved link/see tag "android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS INSTALL_GRANT_RUNTIME_PERMISSIONS" in android.content.pm.PackageInstaller.SessionParams [101]
+android/content/pm/ServiceInfo.java:176: lint: Unresolved link/see tag "android.app.job.JobInfo.Builder#setDataTransfer" in android.content.pm.ServiceInfo [101]
+android/content/pm/verify/domain/DomainVerificationUserState.java:82: lint: Unresolved link/see tag "android.content.pm.verify.domain.DomainVerificationUserState.DomainState DomainState" in android.content.pm.verify.domain.DomainVerificationUserState [101]
+android/content/res/Resources.java:958: lint: Unresolved link/see tag "android.annotation.UiContext" in android.content.res.Resources [101]
+android/credentials/CreateCredentialException.java:22: lint: Unresolved link/see tag "android.credentials.CredentialManager#createCredential(android.credentials.CreateCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#createCredential(CreateCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.CreateCredentialException [101]
+android/credentials/CreateCredentialException.java:101: lint: Unresolved link/see tag "android.credentials.CredentialManager#createCredential(android.credentials.CreateCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#createCredential(CreateCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.CreateCredentialException [101]
+android/credentials/CreateCredentialRequest.java:107: lint: Unresolved link/see tag "androidx.credentials.CreateCredentialRequest" in android.credentials.CreateCredentialRequest.Builder [101]
+android/credentials/CredentialDescription.java:89: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mSupportedElementKeys CredentialDescription#mSupportedElementKeys" in android.credentials.CredentialDescription [101]
+android/credentials/CredentialDescription.java:89: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mType CredentialDescription#mType" in android.credentials.CredentialDescription [101]
+android/credentials/CredentialDescription.java:101: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mSupportedElementKeys CredentialDescription#mSupportedElementKeys" in android.credentials.CredentialDescription [101]
+android/credentials/CredentialDescription.java:101: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mType CredentialDescription#mType" in android.credentials.CredentialDescription [101]
+android/credentials/GetCredentialException.java:22: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.GetCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.GetCredentialException [101]
+android/credentials/GetCredentialException.java:103: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.GetCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver)" in android.credentials.GetCredentialException [101]
+android/credentials/PrepareGetCredentialResponse.java:20: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse [101]
+android/credentials/PrepareGetCredentialResponse.java:68: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse [101]
+android/credentials/PrepareGetCredentialResponse.java:83: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle [101]
+android/graphics/Paint.java:838: lint: Unresolved link/see tag "android.annotation.ColorLong ColorLong" in android.graphics.Paint [101]
+android/graphics/text/LineBreaker.java:246: lint: Unresolved link/see tag "StaticLayout.Builder#setUseBoundsForWidth(boolean)" in android.graphics.text.LineBreaker.Builder [101]
+android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2361: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2361: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors tables" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2361: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2390: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2390: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2402: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2402: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2445: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2445: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2473: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2473: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraCharacteristics.java:2473: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
+android/hardware/camera2/CameraMetadata.java:1934: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#level-3-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101]
+android/hardware/camera2/CameraMetadata.java:1980: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#full-level-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101]
+android/hardware/camera2/CameraMetadata.java:2019: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101]
+android/hardware/camera2/CameraMetadata.java:2038: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#limited-level-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101]
+android/hardware/camera2/CameraMetadata.java:2533: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101]
+android/hardware/camera2/CameraMetadata.java:3095: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101]
+android/hardware/camera2/CaptureRequest.java:704: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101]
+android/hardware/camera2/CaptureRequest.java:1501: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101]
+android/hardware/camera2/CaptureResult.java:923: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
+android/hardware/camera2/CaptureResult.java:2337: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
+android/hardware/input/InputManager.java:215: lint: Unresolved link/see tag "android.hardware.input.InputManagerGlobal#getInputDevice InputManagerGlobal#getInputDevice" in android.hardware.input.InputManager.InputDeviceListener [101]
+android/inputmethodservice/AbstractInputMethodService.java:155: lint: Unresolved link/see tag "android.app.ActivityThread ActivityThread" in android.inputmethodservice.AbstractInputMethodService [101]
+android/inputmethodservice/InputMethodService.java:1078: lint: Unresolved link/see tag "android.widget.Editor" in android.inputmethodservice.InputMethodService [101]
+android/location/GnssSignalType.java:14: lint: Unresolved link/see tag "android.location.GnssStatus.ConstellationType GnssStatus.ConstellationType" in android.location.GnssSignalType [101]
+android/location/GnssSignalType.java:48: lint: Unresolved link/see tag "android.location.GnssStatus.ConstellationType GnssStatus.ConstellationType" in android.location.GnssSignalType [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ALARM AttributeSdkUsage#USAGE_ALARM" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANT AttributeSdkUsage#USAGE_ASSISTANT" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_GAME AttributeSdkUsage#USAGE_GAME" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_MEDIA AttributeSdkUsage#USAGE_MEDIA" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_EVENT AttributeSdkUsage#USAGE_NOTIFICATION_EVENT" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_UNKNOWN AttributeSdkUsage#USAGE_UNKNOWN" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION AttributeSdkUsage#USAGE_VOICE_COMMUNICATION" in android.media.AudioAttributes.Builder [101]
+android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING" in android.media.AudioAttributes.Builder [101]
+android/media/AudioFormat.java:963: lint: Unresolved link/see tag "android.media.AudioSystem#OUT_CHANNEL_COUNT_MAX AudioSystem#OUT_CHANNEL_COUNT_MAX" in android.media.AudioFormat.Builder [101]
+android/media/AudioManager.java:275: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
+android/media/AudioManager.java:287: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
+android/media/AudioManager.java:311: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
+android/media/AudioManager.java:313: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
+android/media/AudioMetadata.java:118: lint: Unresolved link/see tag "android.media.AudioPresentation.ContentClassifier One of {@link android.media.AudioPresentation#CONTENT_UNKNOWN AudioPresentation#CONTENT_UNKNOWN},     {@link android.media.AudioPresentation#CONTENT_MAIN AudioPresentation#CONTENT_MAIN},     {@link android.media.AudioPresentation#CONTENT_MUSIC_AND_EFFECTS AudioPresentation#CONTENT_MUSIC_AND_EFFECTS},     {@link android.media.AudioPresentation#CONTENT_VISUALLY_IMPAIRED AudioPresentation#CONTENT_VISUALLY_IMPAIRED},     {@link android.media.AudioPresentation#CONTENT_HEARING_IMPAIRED AudioPresentation#CONTENT_HEARING_IMPAIRED},     {@link android.media.AudioPresentation#CONTENT_DIALOG AudioPresentation#CONTENT_DIALOG},     {@link android.media.AudioPresentation#CONTENT_COMMENTARY AudioPresentation#CONTENT_COMMENTARY},     {@link android.media.AudioPresentation#CONTENT_EMERGENCY AudioPresentation#CONTENT_EMERGENCY},     {@link android.media.AudioPresentation#CONTENT_VOICEOVER AudioPresentation#CONTENT_VOICEOVER}." in android.media.AudioMetadata.Format [101]
+android/media/MediaRouter2.java:162: lint: Unresolved link/see tag "#getInstance(android.content.Context,java.lang.String)" in android.media.MediaRouter2 [101]
+android/media/midi/MidiUmpDeviceService.java:-1: lint: Unresolved link/see tag "#MidiDeviceService" in android.media.midi.MidiUmpDeviceService [101]
+android/media/tv/SectionRequest.java:44: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionRequest [101]
+android/media/tv/SectionResponse.java:39: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionResponse [101]
+android/media/tv/TableRequest.java:48: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableRequest [101]
+android/media/tv/TableResponse.java:82: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableResponse [101]
+android/net/EthernetNetworkSpecifier.java:21: lint: Unresolved link/see tag "android.net.EthernetManager" in android.net.EthernetNetworkSpecifier [101]
+android/net/eap/EapSessionConfig.java:120: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
+android/net/eap/EapSessionConfig.java:135: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
+android/net/eap/EapSessionConfig.java:148: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
+android/net/eap/EapSessionConfig.java:161: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
+android/net/eap/EapSessionConfig.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaConfig [101]
+android/net/eap/EapSessionConfig.java:390: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaPrimeConfig [101]
+android/net/eap/EapSessionConfig.java:587: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapSimConfig [101]
+android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.MloLink [101]
+android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.MloLink [101]
+android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_6_GHZ WifiScanner#WIFI_BAND_6_GHZ" in android.net.wifi.MloLink [101]
+android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_UNSPECIFIED WifiScanner#WIFI_BAND_UNSPECIFIED" in android.net.wifi.MloLink [101]
+android/net/wifi/SoftApConfiguration.java:9: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder SoftApConfiguration.Builder" in android.net.wifi.SoftApConfiguration [101]
+android/net/wifi/SoftApConfiguration.java:66: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setSsid(java.lang.String) Builder#setSsid(String)" in android.net.wifi.SoftApConfiguration [101]
+android/net/wifi/SoftApConfiguration.java:85: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setWifiSsid(android.net.wifi.WifiSsid) Builder#setWifiSsid(WifiSsid)" in android.net.wifi.SoftApConfiguration [101]
+android/net/wifi/SoftApConfiguration.java:96: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBssid(android.net.MacAddress) Builder#setBssid(MacAddress)" in android.net.wifi.SoftApConfiguration [101]
+android/net/wifi/SoftApConfiguration.java:107: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setPassphrase(java.lang.String,int) Builder#setPassphrase(String, int)" in android.net.wifi.SoftApConfiguration [101]
+android/net/wifi/SoftApConfiguration.java:118: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setHiddenSsid(boolean) Builder#setHiddenSsid(boolean)" in android.net.wifi.SoftApConfiguration [101]
+android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101]
+android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101]
+android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101]
+android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101]
+android/net/wifi/WifiManager.java:2466: lint: Unresolved link/see tag "TelephonyManager#hasCarrierPrivileges()." in android.net.wifi.WifiManager [101]
+android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig [101]
+android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig [101]
+android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101]
+android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101]
+android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
+android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
+android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
+android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
+android/os/BugreportManager.java:146: lint: Unresolved link/see tag "android.os.BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT" in android.os.BugreportManager.BugreportCallback [101]
+android/os/PowerManager.java:796: lint: Unresolved link/see tag "android.os.Temperature" in android.os.PowerManager.OnThermalStatusChangedListener [101]
+android/os/RemoteException.java:49: lint: Unresolved link/see tag "android.os.DeadSystemRuntimeException DeadSystemRuntimeException" in android.os.RemoteException [101]
+android/provider/Settings.java:374: lint: Unresolved link/see tag "android.credentials.CredentialManager#isEnabledCredentialProviderService()" in android.provider.Settings [101]
+android/provider/Settings.java:908: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService" in android.provider.Settings [101]
+android/provider/Settings.java:2181: lint: Unresolved link/see tag "android.app.time.TimeManager" in android.provider.Settings.Global [101]
+android/provider/Settings.java:2195: lint: Unresolved link/see tag "android.app.time.TimeManager" in android.provider.Settings.Global [101]
+android/security/KeyStoreException.java:27: lint: Unresolved link/see tag "android.security.KeyStoreException.PublicErrorCode PublicErrorCode" in android.security.KeyStoreException [101]
+android/service/autofill/FillResponse.java:86: lint: Unresolved link/see tag "setFieldClassificationIds" in android.service.autofill.FillResponse.Builder [101]
+android/service/autofill/SaveInfo.java:623: lint: Unresolved link/see tag "FillRequest.getHints()" in android.service.autofill.SaveInfo.Builder [101]
+android/service/credentials/Action.java:3: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.Action [101]
+android/service/credentials/Action.java:3: lint: Unresolved link/see tag "androidx.credentials.provider.Action" in android.service.credentials.Action [101]
+android/service/credentials/BeginCreateCredentialResponse.java:85: lint: Unresolved link/see tag "Manifest.permission.PROVIDE_REMOTE_CREDENTIALS" in android.service.credentials.BeginCreateCredentialResponse.Builder [101]
+android/service/credentials/BeginGetCredentialResponse.java:80: lint: Unresolved link/see tag "Manifest.permission.PROVIDE_REMOTE_CREDENTIALS" in android.service.credentials.BeginGetCredentialResponse.Builder [101]
+android/service/credentials/CallingAppInfo.java:73: lint: Unresolved link/see tag "android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN" in android.service.credentials.CallingAppInfo [101]
+android/service/credentials/CreateEntry.java:6: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.CreateEntry [101]
+android/service/credentials/CreateEntry.java:6: lint: Unresolved link/see tag "androidx.credentials.provider.CreateEntry" in android.service.credentials.CreateEntry [101]
+android/service/credentials/CredentialEntry.java:11: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.CredentialEntry [101]
+android/service/credentials/CredentialEntry.java:11: lint: Unresolved link/see tag "androidx.credentials.provider.CredentialEntry" in android.service.credentials.CredentialEntry [101]
+android/service/credentials/RemoteEntry.java:13: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.RemoteEntry [101]
+android/service/credentials/RemoteEntry.java:13: lint: Unresolved link/see tag "androidx.credentials.provider.RemoteEntry" in android.service.credentials.RemoteEntry [101]
+android/service/notification/NotificationListenerService.java:417: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService notification assistant" in android.service.notification.NotificationListenerService [101]
+android/service/notification/NotificationListenerService.java:435: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService notification assistant" in android.service.notification.NotificationListenerService [101]
+android/service/notification/NotificationListenerService.java:1155: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService NotificationAssistantService" in android.service.notification.NotificationListenerService.Ranking [101]
+android/service/notification/NotificationListenerService.java:1166: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService NotificationAssistantService" in android.service.notification.NotificationListenerService.Ranking [101]
+android/service/quickaccesswallet/WalletCard.java:285: lint: Unresolved link/see tag "PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS" in android.service.quickaccesswallet.WalletCard.Builder [101]
+android/service/voice/VoiceInteractionSession.java:293: lint: Unresolved link/see tag "android.service.voice.VoiceInteractionService#KEY_SHOW_SESSION_ID VoiceInteractionService#KEY_SHOW_SESSION_ID" in android.service.voice.VoiceInteractionSession [101]
+android/telecom/Call.java:94: lint: unable to parse link/see tag: #playDtmfTone(char [101]
+android/telecom/CallControl.java:163: lint: Unresolved link/see tag "android.telecom.CallStreamingService CallStreamingService" in android.telecom.CallControl [101]
+android/telecom/CallControlCallback.java:63: lint: Unresolved link/see tag "android.telecom.CallAttributes.CallType" in android.telecom.CallControlCallback [101]
+android/telecom/PhoneAccountSuggestion.java:17: lint: Unresolved link/see tag "android.telecom.PhoneAccountSuggestionService PhoneAccountSuggestionService" in android.telecom.PhoneAccountSuggestion [101]
+android/telephony/CarrierConfigManager.java:2934: lint: Unresolved link/see tag "android.telephony.TelephonyManager.PremiumCapability TelephonyManager.PremiumCapability" in android.telephony.CarrierConfigManager [101]
+android/telephony/CarrierConfigManager.java:4020: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:4108: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:3920: lint: Unresolved link/see tag "android.telephony.ims.SipDelegateManager" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:4001: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:4089: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/NetworkRegistrationInfo.java:131: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.NetworkRegistrationInfo [101]
+android/telephony/PhoneStateListener.java:293: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.PhoneStateListener [101]
+android/telephony/PhoneStateListener.java:544: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.PhoneStateListener [101]
+android/telephony/SubscriptionManager.java:1385: lint: Unresolved link/see tag "Build.VERSION_CODES.P" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
+android/telephony/SubscriptionManager.java:1385: lint: Unresolved link/see tag "Build.VERSION_CODES.V" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
+android/telephony/SubscriptionPlan.java:134: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.SubscriptionPlan [101]
+android/telephony/SubscriptionPlan.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.SubscriptionPlan.Builder [101]
+android/telephony/TelephonyCallback.java:113: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.TelephonyCallback.CallDisconnectCauseListener [101]
+android/telephony/TelephonyManager.java:2544: lint: Unresolved link/see tag "android.telephony.TelephonyManager.CarrierRestrictionStatus CarrierRestrictionStatus" in android.telephony.TelephonyManager [101]
+android/telephony/TelephonyManager.java:2841: lint: Unresolved link/see tag "android.telephony.TelephonyManager.SetOpportunisticSubscriptionResult TelephonyManager.SetOpportunisticSubscriptionResult" in android.telephony.TelephonyManager [101]
+android/telephony/TelephonyManager.java:3273: lint: Unresolved link/see tag "android.telephony.TelephonyManager.PurchasePremiumCapabilityResult PurchasePremiumCapabilityResult" in android.telephony.TelephonyManager [101]
+android/telephony/TelephonyManager.java:3989: lint: Unresolved link/see tag "android.telephony.TelephonyManager.MobileDataPolicy MobileDataPolicy" in android.telephony.TelephonyManager [101]
+android/telephony/data/ApnSetting.java:39: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV4() DataCallResponse#getMtuV4()" in android.telephony.data.ApnSetting [101]
+android/telephony/data/ApnSetting.java:50: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV6() DataCallResponse#getMtuV6()" in android.telephony.data.ApnSetting [101]
+android/telephony/data/ApnSetting.java:597: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV4() DataCallResponse#getMtuV4()" in android.telephony.data.ApnSetting.Builder [101]
+android/telephony/data/ApnSetting.java:611: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV6() DataCallResponse#getMtuV6()" in android.telephony.data.ApnSetting.Builder [101]
+android/telephony/euicc/EuiccManager.java:331: lint: Unresolved link/see tag "android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE" in android.telephony.euicc.EuiccManager [101]
+android/telephony/ims/ImsMmTelManager.java:117: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.ImsMmTelManager [101]
+android/telephony/ims/ImsRcsManager.java:43: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.ImsRcsManager [101]
+android/telephony/ims/ProvisioningManager.java:102: lint: Unresolved link/see tag "android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability MmTelFeature.MmTelCapabilities.MmTelCapability" in android.telephony.ims.ProvisioningManager [101]
+android/telephony/ims/ProvisioningManager.java:102: lint: Unresolved link/see tag "android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.ims.ProvisioningManager [101]
+android/telephony/ims/ProvisioningManager.java:136: lint: Unresolved link/see tag "android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag ImsRcsManager.RcsImsCapabilityFlag" in android.telephony.ims.ProvisioningManager [101]
+android/telephony/ims/RegistrationManager.java:21: lint: Unresolved link/see tag "android.telephony.ims.feature.ImsFeature ImsFeature" in android.telephony.ims.RegistrationManager [101]
+android/telephony/ims/RegistrationManager.java:24: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.RegistrationManager [101]
+android/text/DynamicLayout.java:141: lint: Unresolved link/see tag "LineBreakconfig" in android.text.DynamicLayout [101]
+android/text/WordSegmentFinder.java:13: lint: Unresolved link/see tag "android.text.method.WordIterator WordIterator" in android.text.WordSegmentFinder [101]
+android/view/InputDevice.java:71: lint: Unresolved link/see tag "InputManagerGlobal.InputDeviceListener" in android.view.InputDevice [101]
+android/view/PixelCopy.java:468: lint: Unresolved link/see tag "android.view.PixelCopy.CopyResultStatus CopyResultStatus" in android.view.PixelCopy.Result [101]
+android/view/ScrollFeedbackProvider.java:-25: lint: Unresolved link/see tag "InputManager" in android.view.ScrollFeedbackProvider [101]
+android/view/ScrollFeedbackProvider.java:-25: lint: Unresolved link/see tag "InputManager#getInputDeviceIds()" in android.view.ScrollFeedbackProvider [101]
+android/view/SurfaceControl.java:823: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101]
+android/view/SurfaceControl.java:900: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101]
+android/view/SurfaceControl.java:908: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101]
+android/view/View.java:1647: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setAccessibilityPaneTitle(View, CharSequence)" in android.view.View [101]
+android/view/View.java:4669: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setScreenReaderFocusable(View, boolean)" in android.view.View [101]
+android/view/View.java:4712: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setAccessibilityHeading(View, boolean)" in android.view.View [101]
+android/view/WindowManager.java:230: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowManager [101]
+android/view/WindowManager.java:247: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowManager [101]
+android/view/WindowManager.java:822: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
+android/view/WindowManager.java:832: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
+android/view/WindowMetrics.java:22: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
+android/view/WindowMetrics.java:57: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
+android/view/WindowMetrics.java:114: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
+android/view/WindowMetrics.java:127: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
+android/view/accessibility/AccessibilityNodeInfo.java:368: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#addAccessibilityAction(View, AccessibilityNodeInfoCompat.AccessibilityActionCompat)" in android.view.accessibility.AccessibilityNodeInfo [101]
+android/view/accessibility/AccessibilityNodeInfo.java:3246: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#addAccessibilityAction(View, AccessibilityNodeInfoCompat.AccessibilityActionCompat)" in android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction [101]
+android/view/displayhash/DisplayHashResultCallback.java:38: lint: Unresolved link/see tag "android.view.displayhash.DisplayHashResultCallback.DisplayHashErrorCode DisplayHashErrorCode" in android.view.displayhash.DisplayHashResultCallback [101]
+android/view/inputmethod/EditorInfo.java:107: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.EditorInfo [101]
+android/view/inputmethod/EditorInfo.java:122: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.EditorInfo [101]
+android/view/inputmethod/InputMethodManager.java:423: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101]
+android/view/inputmethod/InputMethodManager.java:447: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101]
+android/view/inputmethod/InputMethodManager.java:456: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101]
+android/view/inspector/PropertyReader.java:141: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.view.inspector.PropertyReader [101]
+android/window/BackEvent.java:24: lint: Unresolved link/see tag "android.window.BackMotionEvent BackMotionEvent" in android.window.BackEvent [101]
+
+android/net/wifi/SoftApConfiguration.java:173: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101]
+android/content/pm/ActivityInfo.java:1197: lint: Unresolved link/see tag "android.view.ViewRootImpl" in android.content.pm.ActivityInfo [101]
+android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101]
+android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware#enabledSinceTargetSdkVersion" in android.os.UserManager [101]
+android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android.service.voice.AlwaysOnHotwordDetector [101]
+android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "STATE_HARDWARE_UNAVAILABLE" in android.service.voice.AlwaysOnHotwordDetector [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onFailure" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onUnknownFailure" in android [101]
+android/telephony/TelephonyRegistryManager.java:242: lint: Unresolved link/see tag "#listenFromListener" in android [101]
+android/view/animation/AnimationUtils.java:64: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in android.view.animation.AnimationUtils [101]
+android/view/contentcapture/ContentCaptureSession.java:188: lint: Unresolved link/see tag "UPSIDE_DOWN_CAKE" in android.view.contentcapture.ContentCaptureSession [101]
+com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in com.android.internal.policy.PhoneWindow [101]
+
+com/android/server/companion/virtual/VirtualDeviceImpl.java:134: lint: Unresolved link/see tag "DisplayManager" in android [101]
+com/android/server/companion/virtual/VirtualDeviceImpl.java:134: lint: Unresolved link/see tag "VirtualDeviceManager.VirtualDevice" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "IdentifierType#DAB_SID_EXT" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "RadioTuner" in android [101]
+com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "ComponentName" in android [101]
+com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "IllegalArgumentException" in android [101]
+com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "MediaSession#setMediaButtonBroadcastReceiver(ComponentName)" in android [101]
+com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "IllegalArgumentException" in android [101]
+com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "MediaSession#setMediaButtonReceiver(PendingIntent)" in android [101]
+com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "PendingIntent" in android [101]
+com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "Build.VERSION_CODES#S API 31" in android [101]
+com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequireUserAction" in android [101]
+com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
+com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34" in android [101]
+com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)" in android [101]
+com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101]
+com/android/server/devicepolicy/DevicePolicyManagerService.java:860: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101]
+android/telephony/SubscriptionManager.java:1370: lint: Unresolved link/see tag "Build.VERSION_CODES.Q" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
+
+android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:-4: lint: Invalid tag: @Override [131]
+android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:-1: lint: Invalid tag: @Override [131]
+android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:2: lint: Invalid tag: @Override [131]
+android/os/BatteryStatsManager.java:260: lint: Invalid tag: @Deprecated [131]
+android/os/BatteryStatsManager.java:275: lint: Invalid tag: @Deprecated [131]
+android/view/WindowManager.java:906: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
+android/view/WindowManager.java:916: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
+
+java/lang/ClassLoader.java:853: lint: Unknown tag: @systemProperty [103]
diff --git a/core/api/current.txt b/core/api/current.txt
index 2b50e38..d037c31 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4463,7 +4463,7 @@
     method public void onProvideAssistData(android.os.Bundle);
     method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]);
-    method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int);
     method @CallSuper protected void onRestart();
     method protected void onRestoreInstanceState(@NonNull android.os.Bundle);
     method public void onRestoreInstanceState(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle);
@@ -4505,7 +4505,7 @@
     method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent);
     method public void requestFullscreenMode(int, @Nullable android.os.OutcomeReceiver<java.lang.Void,java.lang.Throwable>);
     method public final void requestPermissions(@NonNull String[], int);
-    method public final void requestPermissions(@NonNull String[], int, int);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public final void requestPermissions(@NonNull String[], int, int);
     method public final void requestShowKeyboardShortcuts();
     method @Deprecated public boolean requestVisibleBehind(boolean);
     method public final boolean requestWindowFeature(int);
@@ -4553,7 +4553,7 @@
     method public void setVrModeEnabled(boolean, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean shouldDockBigOverlays();
     method public boolean shouldShowRequestPermissionRationale(@NonNull String);
-    method public boolean shouldShowRequestPermissionRationale(@NonNull String, int);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public boolean shouldShowRequestPermissionRationale(@NonNull String, int);
     method public boolean shouldUpRecreateTask(android.content.Intent);
     method public boolean showAssist(android.os.Bundle);
     method @Deprecated public final void showDialog(int);
@@ -9769,7 +9769,7 @@
     method public int describeContents();
     method public void enforceCallingUid();
     method @Nullable public String getAttributionTag();
-    method public int getDeviceId();
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public int getDeviceId();
     method @Nullable public android.content.AttributionSource getNext();
     method @Nullable public String getPackageName();
     method public int getPid();
@@ -9785,7 +9785,7 @@
     ctor public AttributionSource.Builder(@NonNull android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource build();
     method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
-    method @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
     method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
@@ -18539,8 +18539,10 @@
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
     ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
     ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
+    ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement);
     method public javax.crypto.Cipher getCipher();
     method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+    method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
     method public javax.crypto.Mac getMac();
     method @Nullable public android.security.identity.PresentationSession getPresentationSession();
     method public java.security.Signature getSignature();
@@ -42982,7 +42984,6 @@
     field public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
     field public static final String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
     field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
-    field @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
     field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
     field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
     field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
@@ -44904,7 +44905,7 @@
     method public int getSubscriptionType();
     method public int getUsageSetting();
     method public boolean isEmbedded();
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isNtn();
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isOnlyNonTerrestrialNetwork();
     method public boolean isOpportunistic();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SubscriptionInfo> CREATOR;
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 57e2e73..5a4be65 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -472,6 +472,10 @@
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    field @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
+  }
+
   public class NetworkScan {
     method @Deprecated public void stop() throws android.os.RemoteException;
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 358c8e7..a99eeb0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3946,7 +3946,7 @@
     field public static final int DELETE_FAILED_OWNER_BLOCKED = -4; // 0xfffffffc
     field public static final int DELETE_KEEP_DATA = 1; // 0x1
     field public static final int DELETE_SUCCEEDED = 1; // 0x1
-    field public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
+    field @FlaggedApi("android.permission.flags.device_aware_permission_apis") public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
     field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 40c6fa8..aa48451 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -875,7 +875,7 @@
     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 public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @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();
   }
 
@@ -1903,7 +1903,7 @@
     method public android.media.PlaybackParams setAudioStretchMode(int);
   }
 
-  public final class RingtoneSelection {
+  @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection {
     method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int);
     method public int getSoundSource();
     method @Nullable public android.net.Uri getSoundUri();
@@ -1915,14 +1915,16 @@
     field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3
     field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1
     field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2
-    field public static final int SOUND_SOURCE_DEFAULT = 0; // 0x0
     field public static final int SOUND_SOURCE_OFF = 1; // 0x1
+    field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
+    field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0
     field public static final int SOUND_SOURCE_URI = 2; // 0x2
-    field public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; // 0x3
+    field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4
     field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa
-    field public static final int VIBRATION_SOURCE_DEFAULT = 0; // 0x0
     field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb
     field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1
+    field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
+    field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0
     field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
   }
 
@@ -2610,17 +2612,17 @@
 
 package android.os.vibrator.persistence {
 
-  public class ParsedVibration {
+  @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public class ParsedVibration {
     method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects();
     method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
   }
 
-  public final class VibrationXmlParser {
+  @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlParser {
     method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException;
     method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException;
   }
 
-  public final class VibrationXmlSerializer {
+  @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlSerializer {
     method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException;
   }
 
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 107be8b..93e39d5 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -191,8 +191,14 @@
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
 UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED
 UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
@@ -203,6 +209,10 @@
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
 UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7bc6f9bf..e51a41e8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -31,6 +31,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.IdRes;
 import android.annotation.IntDef;
 import android.annotation.LayoutRes;
@@ -98,6 +99,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.permission.flags.Flags;
 import android.service.voice.VoiceInteractionSession;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
@@ -5556,8 +5558,7 @@
      *                    reported to {@link #onRequestPermissionsResult}.
      *                    Should be >= 0.
      * @param deviceId The app is requesting permissions for this device. The primary/physical
-     *                 device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link
-     *                 android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices}
+     *                 device is assigned {@link Context#DEVICE_ID_DEFAULT}, and virtual devices
      *                 are assigned unique device Ids.
      *
      * @throws IllegalArgumentException if requestCode is negative.
@@ -5567,6 +5568,7 @@
      * @see #shouldShowRequestPermissionRationale
      * @see Context#DEVICE_ID_DEFAULT
      */
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
     public final void requestPermissions(@NonNull String[] permissions, int requestCode,
             int deviceId) {
         if (requestCode < 0) {
@@ -5634,12 +5636,12 @@
      *                     {@link android.content.pm.PackageManager#PERMISSION_GRANTED} or
      *                     {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
      * @param deviceId The deviceId for which permissions were requested. The primary/physical
-     *                 device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link
-     *                 android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices}
+     *                 device is assigned {@link Context#DEVICE_ID_DEFAULT}, and virtual devices
      *                 are assigned unique device Ids.
      *
      * @see #requestPermissions
      */
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
             @NonNull int[] grantResults, int deviceId) {
         onRequestPermissionsResult(requestCode, permissions, grantResults);
@@ -5664,8 +5666,7 @@
      *
      * @param permission A permission your app wants to request.
      * @param deviceId The app is requesting permissions for this device. The primary/physical
-     *                 device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link
-     *                 android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices}
+     *                 device is assigned {@link Context#DEVICE_ID_DEFAULT}, and virtual devices
      *                 are assigned unique device Ids.
      * @return Whether you should show permission rationale UI.
      *
@@ -5673,6 +5674,7 @@
      * @see #requestPermissions
      * @see #onRequestPermissionsResult
      */
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
     public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) {
         final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
                 : createDeviceContext(deviceId).getPackageManager();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ca10d14..1aa8ebe 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1486,13 +1486,13 @@
             AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
 
     /**
-     * Allows the assistant app to get the training data from the PCC sandbox to improve the
+     * Allows the assistant app to get the training data from the trusted process to improve the
      * hotword training model.
      *
      * @hide
      */
-    public static final int OP_RECEIVE_SANDBOX_TRAINING_DATA =
-            AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRAINING_DATA;
+    public static final int OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA =
+            AppProtoEnums.APP_OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA;
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1640,7 +1640,7 @@
             OPSTR_CAMERA_SANDBOXED,
             OPSTR_RECORD_AUDIO_SANDBOXED,
             OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
-            OPSTR_RECEIVE_SANDBOX_TRAINING_DATA
+            OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA
     })
     public @interface AppOpString {}
 
@@ -2261,13 +2261,13 @@
             "android:receive_sandbox_trigger_audio";
 
     /**
-     * Allows the assistant app to get the training data from the PCC sandbox to improve
+     * Allows the assistant app to get the training data from the trusted process to improve
      * the hotword training model.
      *
      * @hide
      */
-    public static final String OPSTR_RECEIVE_SANDBOX_TRAINING_DATA =
-            "android:receive_sandbox_training_data";
+    public static final String OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA =
+            "android:receive_trusted_process_training_data";
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -2811,9 +2811,9 @@
                 OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
                 "RECEIVE_SANDBOX_TRIGGER_AUDIO")
                 .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
-        new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRAINING_DATA,
-                OPSTR_RECEIVE_SANDBOX_TRAINING_DATA,
-                "RECEIVE_SANDBOX_TRAINING_DATA").build()
+        new AppOpInfo.Builder(OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA,
+                OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA,
+                "RECEIVE_TRUSTED_PROCESS_TRAINING_DATA").build()
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 7b3d017..d15c79f 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -468,6 +468,15 @@
      */
     public static final int SUBREASON_EXCESSIVE_BINDER_OBJECTS = 29;
 
+    /**
+     * The process was killed by the [kernel] Out-of-memory (OOM) killer; this
+     * would be set only when the reason is {@link #REASON_LOW_MEMORY}.
+     *
+     * For internal use only.
+     * @hide
+     */
+    public static final int SUBREASON_OOM_KILL = 30;
+
     // If there is any OEM code which involves additional app kill reasons, it should
     // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
 
@@ -644,6 +653,7 @@
         SUBREASON_PACKAGE_UPDATE,
         SUBREASON_UNDELIVERED_BROADCAST,
         SUBREASON_EXCESSIVE_BINDER_OBJECTS,
+        SUBREASON_OOM_KILL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SubReason {}
@@ -1371,6 +1381,8 @@
                 return "UNDELIVERED BROADCAST";
             case SUBREASON_EXCESSIVE_BINDER_OBJECTS:
                 return "EXCESSIVE BINDER OBJECTS";
+            case SUBREASON_OOM_KILL:
+                return "OOM KILL";
             default:
                 return "UNKNOWN";
         }
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index a6a57cd..37111e9 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -473,9 +473,8 @@
      * available.
      * For {@link #STARTUP_STATE_ERROR}, no additional timestamps are guaranteed available.
      * For {@link #STARTUP_STATE_FIRST_FRAME_DRAWN}, timestamps
-     * {@link #START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE}, {@link #START_TIMESTAMP_APPLICATION_ONCREATE},
-     * {@link #START_TIMESTAMP_BIND_APPLICATION}, and {@link #START_TIMESTAMP_FIRST_FRAME} will
-     * additionally be available.
+     * {@link #START_TIMESTAMP_APPLICATION_ONCREATE}, {@link #START_TIMESTAMP_BIND_APPLICATION},
+     * and {@link #START_TIMESTAMP_FIRST_FRAME} will additionally be available.
      *
      * Timestamp {@link #START_TIMESTAMP_FULLY_DRAWN} is never guaranteed to be available as it is
      * dependant on devloper calling {@link Activity#reportFullyDrawn}.
diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java
index bc6fe61..a55121a 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -100,7 +100,7 @@
 
     /**
      * Sets the current grammatical gender for all privileged applications. The value will be
-     * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)
+     * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)}
      *
      * @param grammaticalGender the terms of address the user preferred in system.
      *
@@ -121,8 +121,7 @@
     }
 
     /**
-     * Get the current grammatical gender of privileged application from the encrypted file,
-     * which is stored under {@link android.os.Environment#getDataSystemCeDirectory(int)}.
+     * Get the current grammatical gender of privileged application from the encrypted file.
      *
      * @return the value of grammatical gender
      *
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 93c2b5a..dd7db23 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9195,6 +9195,12 @@
      * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
      * <p>
      *
+     * <p>
+     * Starting at {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V} the
+     * {@link Notification#FLAG_NO_CLEAR NO_CLEAR flag} will be set for valid MediaStyle
+     * notifications.
+     * <p>
+     *
      * To use this style with your Notification, feed it to
      * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
      * <pre class="prettyprint">
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2fb428b..a84845a 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -240,6 +240,12 @@
     public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
 
     /**
+     * The length limit of Association tag.
+     * @hide
+     */
+    private static final int ASSOCIATION_TAG_LENGTH_LIMIT = 100;
+
+    /**
      * Callback for applications to receive updates about and the outcome of
      * {@link AssociationRequest} issued via {@code associate()} call.
      *
@@ -1409,7 +1415,7 @@
     /**
      * Sets the {@link AssociationInfo#getTag() tag} for this association.
      *
-     * <p>The length of the tag must be at most 20 characters.
+     * <p>The length of the tag must be at most 100 characters to save disk space.
      *
      * <p>This allows to store useful information about the associated devices.
      *
@@ -1421,8 +1427,8 @@
     public void setAssociationTag(int associationId, @NonNull String tag) {
         Objects.requireNonNull(tag, "tag cannot be null");
 
-        if (tag.length() > 20) {
-            throw new IllegalArgumentException("Length of the tag must be at most 20 characters");
+        if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) {
+            throw new IllegalArgumentException("Length of the tag must be at most 100 characters");
         }
 
         try {
diff --git a/core/java/android/companion/virtualnative/OWNERS b/core/java/android/companion/virtualnative/OWNERS
new file mode 100644
index 0000000..2968104
--- /dev/null
+++ b/core/java/android/companion/virtualnative/OWNERS
@@ -0,0 +1 @@
+include /services/companion/java/com/android/server/companion/virtual/OWNERS
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 15678a7..bfc1eec 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -31,6 +32,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.permission.PermissionManager;
+import android.permission.flags.Flags;
 import android.util.ArraySet;
 
 import com.android.internal.annotations.Immutable;
@@ -163,6 +165,7 @@
 
     /** @hide */
     @TestApi
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
     public AttributionSource(int uid, int pid, @Nullable String packageName,
             @Nullable String attributionTag, @NonNull IBinder token,
             @Nullable String[] renouncedPermissions,
@@ -528,6 +531,7 @@
      * <p>
      * This device ID is used for permissions checking during attribution source validation.
      */
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
     public int getDeviceId() {
         return mAttributionSourceState.deviceId;
     }
@@ -715,6 +719,7 @@
          *
          * @return the builder
          */
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
         public @NonNull Builder setDeviceId(int deviceId) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x12;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3fc515d..8fbe50c3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4802,6 +4802,7 @@
      * @hide
      */
     @SystemApi
+    @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
     public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID =
             "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
 
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index ea0f049..3e12a1a 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -13,3 +13,10 @@
     description: "Bind wallpaper service on its own thread instead of system_server's main handler during a user switch."
     bug: "302100344"
 }
+
+flag {
+    name: "support_communal_profile"
+    namespace: "multiuser"
+    description: "Framework support for communal profile."
+    bug: "285426179"
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 231e4bc..1b130a9 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -53,6 +53,15 @@
         mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
     }
 
+    /**
+     * Returns candidate provider data list.
+     *
+     * @hide
+     */
+    public List<GetCredentialProviderData> getCandidateProviderDataList() {
+        return mCandidateProviderDataList;
+    }
+
     protected GetCandidateCredentialsResponse(Parcel in) {
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
         in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index af448f0..490ff64 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -20,8 +20,10 @@
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -53,6 +55,7 @@
 import java.util.concurrent.Executor;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 import javax.crypto.Mac;
 
 /**
@@ -748,7 +751,7 @@
      * A wrapper class for the cryptographic operations supported by BiometricPrompt.
      *
      * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
-     * {@link IdentityCredential}, and {@link PresentationSession}.
+     * {@link IdentityCredential}, {@link PresentationSession} and {@link KeyAgreement}.
      *
      * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
      * time-based. This is specified during key creation via the timeout parameter of the
@@ -793,6 +796,11 @@
             super(session);
         }
 
+        @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+        public CryptoObject(@NonNull KeyAgreement keyAgreement) {
+            super(keyAgreement);
+        }
+
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -834,6 +842,15 @@
         public @Nullable PresentationSession getPresentationSession() {
             return super.getPresentationSession();
         }
+
+        /**
+         * Get {@link KeyAgreement} object.
+         * @return {@link KeyAgreement} object or null if this doesn't contain one.
+         */
+        @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+        public @Nullable KeyAgreement getKeyAgreement() {
+            return super.getKeyAgreement();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 267ef36..151f819 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -16,6 +16,9 @@
 
 package android.hardware.biometrics;
 
+import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
+
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.security.identity.IdentityCredential;
 import android.security.identity.PresentationSession;
@@ -24,6 +27,7 @@
 import java.security.Signature;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 import javax.crypto.Mac;
 
 /**
@@ -62,6 +66,11 @@
         mCrypto = session;
     }
 
+    @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+    public CryptoObject(@NonNull KeyAgreement keyAgreement) {
+        mCrypto = keyAgreement;
+    }
+
     /**
      * Get {@link Signature} object.
      * @return {@link Signature} object or null if this doesn't contain one.
@@ -105,6 +114,15 @@
     }
 
     /**
+     * Get {@link PresentationSession} object.
+     * @return {@link PresentationSession} object or null if this doesn't contain one.
+     */
+    @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+    public KeyAgreement getKeyAgreement() {
+        return mCrypto instanceof KeyAgreement ? (KeyAgreement) mCrypto : null;
+    }
+
+    /**
      * @hide
      * @return the opId associated with this object or 0 if none
      */
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index f594377..5bfda70 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -24,12 +24,14 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
+import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
 
 import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE;
 import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_HAS_ENROLLED_FINGERPRINTS;
 import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_IS_HARDWARE_DETECTED;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -76,6 +78,7 @@
 import java.util.concurrent.Executor;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 import javax.crypto.Mac;
 
 /**
@@ -312,6 +315,16 @@
         public PresentationSession getPresentationSession() {
             return super.getPresentationSession();
         }
+
+        /**
+         * Get {@link KeyAgreement} object.
+         * @return {@link KeyAgreement} object or null if this doesn't contain one.
+         * @hide
+         */
+        @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+        public KeyAgreement getKeyAgreement() {
+            return super.getKeyAgreement();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index bbfed24..883f157 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -82,8 +82,8 @@
         DVORAK(4, LAYOUT_TYPE_DVORAK),
         COLEMAK(5, LAYOUT_TYPE_COLEMAK),
         WORKMAN(6, LAYOUT_TYPE_WORKMAN),
-        TURKISH_F(7, LAYOUT_TYPE_TURKISH_F),
-        TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q),
+        TURKISH_Q(7, LAYOUT_TYPE_TURKISH_Q),
+        TURKISH_F(8, LAYOUT_TYPE_TURKISH_F),
         EXTENDED(9, LAYOUT_TYPE_EXTENDED);
 
         private final int mValue;
diff --git a/core/java/android/os/OomKillRecord.java b/core/java/android/os/OomKillRecord.java
new file mode 100644
index 0000000..151a65f
--- /dev/null
+++ b/core/java/android/os/OomKillRecord.java
@@ -0,0 +1,63 @@
+/*
+ * 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.os;
+
+
+/**
+ * Expected data to get back from the OOM event's file.
+ * Note that this should be equivalent to the struct <b>OomKill</b> inside
+ * <pre>
+ * system/memory/libmeminfo/libmemevents/include/memevents.h
+ * </pre>
+ *
+ * @hide
+ */
+public final class OomKillRecord {
+    private long mTimeStampInMillis;
+    private int mPid;
+    private int mUid;
+    private String mProcessName;
+    private short mOomScoreAdj;
+
+    public OomKillRecord(long timeStampInMillis, int pid, int uid,
+                            String processName, short oomScoreAdj) {
+        this.mTimeStampInMillis = timeStampInMillis;
+        this.mPid = pid;
+        this.mUid = uid;
+        this.mProcessName = processName;
+        this.mOomScoreAdj = oomScoreAdj;
+    }
+
+    public long getTimestampMilli() {
+        return mTimeStampInMillis;
+    }
+
+    public int getPid() {
+        return mPid;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public String getProcessName() {
+        return mProcessName;
+    }
+
+    public short getOomScoreAdj() {
+        return mOomScoreAdj;
+    }
+}
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index c01ef3d..88f62f3 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -12,4 +12,18 @@
     name: "haptics_customization_enabled"
     description: "Enables the haptics customization feature"
     bug: "241918098"
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "haptics"
+    name: "haptics_customization_ringtone_v2_enabled"
+    description: "Enables the usage of the new RingtoneV2 class"
+    bug: "241918098"
+}
+
+flag {
+    namespace: "haptics"
+    name: "enable_vibration_serialization_apis"
+    description: "Enables the APIs for vibration serialization/deserialization."
+    bug: "245129509"
+}
diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java
index ded74ea..3d1deea 100644
--- a/core/java/android/os/vibrator/persistence/ParsedVibration.java
+++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java
@@ -16,6 +16,7 @@
 
 package android.os.vibrator.persistence;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -34,6 +35,7 @@
  *
  * @hide
  */
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
 @TestApi
 public class ParsedVibration {
     private final List<VibrationEffect> mEffects;
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index e08cc42..fed1053 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -16,6 +16,7 @@
 
 package android.os.vibrator.persistence;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -115,6 +116,7 @@
  *
  * @hide
  */
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
 @TestApi
 public final class VibrationXmlParser {
     private static final String TAG = "VibrationXmlParser";
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index 1cdfa4f..2880454 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -16,6 +16,7 @@
 
 package android.os.vibrator.persistence;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
@@ -42,6 +43,7 @@
  *
  * @hide
  */
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
 @TestApi
 public final class VibrationXmlSerializer {
 
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 5626b94..d8534dd 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -2,6 +2,7 @@
 
 flag {
   name: "device_aware_permission_apis"
+  is_fixed_read_only: true
   namespace: "permissions"
   description: "enable device aware permission APIs"
   bug: "274852670"
@@ -19,4 +20,4 @@
     namespace: "permissions"
     description: "enable role controller in system server"
     bug: "302562590"
-}
\ No newline at end of file
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d09f0a8..36d3180 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5664,6 +5664,37 @@
         public static final String SHOW_ROTARY_INPUT = "show_rotary_input";
 
         /**
+         * The screen backlight brightness for automatic mode.
+         *
+         * <p>Value should be one of:
+         *      <ul>
+         *        <li>SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT
+         *        <li>SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL
+         *        <li>SCREEN_BRIGHTNESS_AUTOMATIC_DIM
+         *      </ul>
+         * @hide
+         */
+        public static final String SCREEN_BRIGHTNESS_FOR_ALS = "screen_brightness_for_als";
+
+        /**
+         * SCREEN_BRIGHTNESS_FOR_ALS value for automatic bright.
+         * @hide
+         */
+        public static final int SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT = 1;
+
+        /**
+         * SCREEN_BRIGHTNESS_FOR_ALS value for automatic normal.
+         * @hide
+         */
+        public static final int SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL = 2;
+
+        /**
+         * SCREEN_BRIGHTNESS_FOR_ALS value for automatic dim.
+         * @hide
+         */
+        public static final int SCREEN_BRIGHTNESS_AUTOMATIC_DIM = 3;
+
+        /**
          * Log raw orientation data from
          * {@link com.android.server.policy.WindowOrientationListener} for use with the
          * orientationplot.py tool.
@@ -11576,6 +11607,45 @@
                 "accessibility_magnification_joystick_enabled";
 
         /**
+         * Controls magnification enable gesture. Accessibility magnification can have one or more
+         * enable gestures.
+         *
+         * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE
+         * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP
+         * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP
+         * @hide
+         */
+        public static final String ACCESSIBILITY_MAGNIFICATION_GESTURE =
+                "accessibility_magnification_gesture";
+
+        /**
+         * Magnification enable gesture value that is a default value.
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE = 0x0;
+
+        /**
+         * Magnification enable gesture value is single finger triple tap.
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP = 0x1;
+
+        /**
+         * Magnification enable gesture value is two finger triple tap.
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP = 0x2;
+
+        /**
+         * Magnification enable gesture values include single finger triple tap and two finger
+         * triple tap.
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL =
+                ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP
+                | ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP;
+
+        /**
          * Controls magnification capability. Accessibility magnification is capable of at least one
          * of the magnification modes.
          *
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index be7b722..a2b0a66 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -153,6 +153,18 @@
     public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST =
             "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST";
 
+    /**
+     * The key to autofillId associated with the requested credential option and the corresponding
+     * credential entry. The associated autofillId will be contained inside the candidate query
+     * bundle of {@link android.credentials.CredentialOption} if requested through the
+     * {@link com.android.credentialmanager.autofill.CredentialAutofillService}. The resulting
+     * credential entry will  contain the autofillId inside its framework extras intent.
+     *
+     * @hide
+     */
+    public static final String EXTRA_AUTOFILL_ID =
+            "android.service.credentials.extra.AUTOFILL_ID";
+
     private static final String TAG = "CredProviderService";
 
      /**
diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
new file mode 100644
index 0000000..c414ef8
--- /dev/null
+++ b/core/java/android/service/voice/flags/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.service.voice.flags"
+
+flag {
+    name: "allow_training_data_egress_from_hds"
+    namespace: "machine_learning"
+    description: "This flag allows the hotword detection service to egress training data to the default assistant."
+    bug: "296074924"
+}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index c4d3070..a150187 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -23,6 +23,8 @@
 import android.window.PictureInPictureSurfaceTransaction;
 import android.window.TaskSnapshot;
 
+import com.android.internal.os.IResultReceiver;
+
 /**
  * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
  * runner control certain aspects of the recents animation, and to notify window manager when the
@@ -58,7 +60,7 @@
      *                          top resumed app, false otherwise.
      */
     @UnsupportedAppUsage
-    void finish(boolean moveHomeToTop, boolean sendUserLeaveHint);
+    void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, in IResultReceiver finishCb);
 
     /**
      * Called by the handler to indicate that the recents animation input consumer should be
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 2761aae..45b3fdd 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static com.android.window.flags.Flags.surfaceTrustedOverlay;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.graphics.Matrix;
@@ -35,7 +37,6 @@
  * @hide
  */
 public final class InputWindowHandle {
-
     /**
      * An internal annotation for all the {@link android.os.InputConfig} flags that can be
      * specified to {@link #inputConfig} to control the behavior of an input window. Only the
@@ -59,7 +60,6 @@
             InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
             InputConfig.IS_WALLPAPER,
             InputConfig.PAUSE_DISPATCHING,
-            InputConfig.TRUSTED_OVERLAY,
             InputConfig.WATCH_OUTSIDE_TOUCH,
             InputConfig.SLIPPERY,
             InputConfig.DISABLE_USER_ACTIVITY,
@@ -272,4 +272,13 @@
         }
         this.inputConfig &= ~inputConfig;
     }
+
+    public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
+            boolean isTrusted) {
+        if (surfaceTrustedOverlay()) {
+            t.setTrustedOverlay(sc, isTrusted);
+        } else if (isTrusted) {
+            inputConfig |= InputConfig.TRUSTED_OVERLAY;
+        }
+    }
 }
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index a0a172d..5a28d5f 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -199,12 +199,33 @@
      * @throws NullPointerException if {@code listener} is null.
      */
     public ScaleGestureDetector(@NonNull Context context, @NonNull OnScaleGestureListener listener,
-                                @Nullable Handler handler) {
+            @Nullable Handler handler) {
+        this(context, ViewConfiguration.get(context).getScaledTouchSlop() * 2,
+                ViewConfiguration.get(context).getScaledMinimumScalingSpan(), handler, listener);
+    }
+
+    /**
+     * Creates a ScaleGestureDetector with span slop and min span.
+     *
+     * @param context the application's context.
+     * @param spanSlop the threshold for interpreting a touch movement as scaling.
+     * @param minSpan the minimum threshold of scaling span. The span could be
+     *                overridden by other usages to specify a different scaling span, for instance,
+     *                if you need pinch gestures to continue closer together than the default.
+     * @param listener the listener invoked for all the callbacks, this must not be null.
+     * @param handler the handler to use for running deferred listener events.
+     *
+     * @throws NullPointerException if {@code listener} is null.
+     *
+     * @hide
+     */
+    public ScaleGestureDetector(@NonNull Context context, @NonNull int spanSlop,
+            @NonNull int minSpan, @Nullable Handler handler,
+            @NonNull OnScaleGestureListener listener) {
         mContext = context;
         mListener = listener;
-        final ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
-        mSpanSlop = viewConfiguration.getScaledTouchSlop() * 2;
-        mMinSpan = viewConfiguration.getScaledMinimumScalingSpan();
+        mSpanSlop = spanSlop;
+        mMinSpan = minSpan;
         mHandler = handler;
         // Quick scale is enabled by default after JB_MR2
         final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index effc127..57b19a8 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -407,7 +407,7 @@
     public @Nullable SurfacePackage getSurfacePackage() {
         if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
             return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"),
-                mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface);
+                mAccessibilityEmbeddedConnection, getInputTransferToken(), mRemoteInterface);
         } else {
             return null;
         }
@@ -526,10 +526,12 @@
     }
 
     /**
+     * Returns an input token used which can be used to request focus on the embedded surface.
+     *
      * @hide
      */
-    public IBinder getFocusGrantToken() {
-        return mWm.getFocusGrantToken(getWindowToken().asBinder());
+    public IBinder getInputTransferToken() {
+        return mWm.getInputTransferToken(getWindowToken().asBinder());
     }
 
     private void addWindowToken(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index afa3157..cf46bcc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -84,6 +84,7 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.accessibility.Flags.forceInvertColor;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
 
@@ -154,6 +155,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.sysprop.DisplayProperties;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
@@ -1744,8 +1746,23 @@
         return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
     }
 
-    private void updateForceDarkMode() {
-        if (mAttachInfo.mThreadedRenderer == null) return;
+    /** Returns true if force dark should be enabled according to various settings */
+    @VisibleForTesting
+    public boolean isForceDarkEnabled() {
+        if (forceInvertColor()) {
+            boolean isForceInvertEnabled = Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+                    /* def= */ 0,
+                    UserHandle.myUserId()) == 1;
+            // Force invert ignores all developer opt-outs.
+            // We also ignore dark theme, since the app developer can override the user's preference
+            // for dark mode in configuration.uiMode. Instead, we assume that the force invert
+            // setting will be enabled at the same time dark theme is in the Settings app.
+            if (isForceInvertEnabled) {
+                return true;
+            }
+        }
 
         boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
 
@@ -1757,8 +1774,12 @@
                     && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault);
             a.recycle();
         }
+        return useAutoDark;
+    }
 
-        if (mAttachInfo.mThreadedRenderer.setForceDark(useAutoDark)) {
+    private void updateForceDarkMode() {
+        if (mAttachInfo.mThreadedRenderer == null) return;
+        if (mAttachInfo.mThreadedRenderer.setForceDark(isForceDarkEnabled())) {
             // TODO: Don't require regenerating all display lists to apply this setting
             invalidateWorld(mView);
         }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e64274e..2f4bea0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1850,6 +1850,8 @@
                         to = "PHONE"),
                 @ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT,
                         to = "SYSTEM_ALERT"),
+                @ViewDebug.IntToString(from = TYPE_KEYGUARD,
+                        to = "KEYGUARD"),
                 @ViewDebug.IntToString(from = TYPE_TOAST,
                         to = "TOAST"),
                 @ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY,
@@ -1898,6 +1900,8 @@
                         to = "PRIVATE_PRESENTATION"),
                 @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION,
                         to = "VOICE_INTERACTION"),
+                @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_OVERLAY,
+                        to = "ACCESSIBILITY_OVERLAY"),
                 @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING,
                         to = "VOICE_INTERACTION_STARTING"),
                 @ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER,
@@ -1907,7 +1911,13 @@
                 @ViewDebug.IntToString(from = TYPE_SCREENSHOT,
                         to = "SCREENSHOT"),
                 @ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY,
-                        to = "APPLICATION_OVERLAY")
+                        to = "APPLICATION_OVERLAY"),
+                @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+                        to = "ACCESSIBILITY_MAGNIFICATION_OVERLAY"),
+                @ViewDebug.IntToString(from = TYPE_NOTIFICATION_SHADE,
+                        to = "NOTIFICATION_SHADE"),
+                @ViewDebug.IntToString(from = TYPE_STATUS_BAR_ADDITIONAL,
+                        to = "STATUS_BAR_ADDITIONAL")
         })
         @WindowType
         public int type;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7d3d283..8fe9b7b 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -58,7 +58,7 @@
         SurfaceControl mLeash;
         Rect mFrame;
         Rect mAttachedFrame;
-        IBinder mFocusGrantToken;
+        IBinder mInputTransferToken;
 
         State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client,
                 SurfaceControl leash, Rect frame) {
@@ -89,7 +89,7 @@
     private final Configuration mConfiguration;
     private final IWindowSession mRealWm;
     private final IBinder mHostInputToken;
-    private final IBinder mFocusGrantToken = new Binder();
+    private final IBinder mInputTransferToken = new Binder();
     private InsetsState mInsetsState;
     private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
     private final MergedConfiguration mTmpConfig = new MergedConfiguration();
@@ -109,17 +109,17 @@
         mConfiguration.setTo(configuration);
     }
 
-    IBinder getFocusGrantToken(IBinder window) {
+    IBinder getInputTransferToken(IBinder window) {
         synchronized (this) {
             // This can only happen if someone requested the focusGrantToken before setView was
             // called for the SCVH. In that case, use the root focusGrantToken since this will be
             // the same token sent to WMS for the root window once setView is called.
             if (mStateForWindow.isEmpty()) {
-                return mFocusGrantToken;
+                return mInputTransferToken;
             }
             State state = mStateForWindow.get(window);
             if (state != null) {
-                return state.mFocusGrantToken;
+                return state.mInputTransferToken;
             }
         }
 
@@ -207,9 +207,9 @@
             // Give the first window the mFocusGrantToken since that's the token the host can use
             // to give focus to the embedded.
             if (mStateForWindow.isEmpty()) {
-                state.mFocusGrantToken = mFocusGrantToken;
+                state.mInputTransferToken = mInputTransferToken;
             } else {
-                state.mFocusGrantToken = new Binder();
+                state.mInputTransferToken = new Binder();
             }
 
             mStateForWindow.put(window.asBinder(), state);
@@ -230,12 +230,13 @@
                             new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
                             window, mHostInputToken, attrs.flags, attrs.privateFlags,
                             attrs.inputFeatures, attrs.type,
-                            attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(),
+                            attrs.token, state.mInputTransferToken, attrs.getTitle().toString(),
                             outInputChannel);
                 } else {
                     mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
                             attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
-                            state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
+                            state.mInputTransferToken, attrs.getTitle().toString(),
+                            outInputChannel);
                 }
                 state.mInputChannelToken =
                         outInputChannel != null ? outInputChannel.getToken() : null;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
new file mode 100644
index 0000000..e31ad82
--- /dev/null
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.view.accessibility"
+
+flag {
+    namespace: "accessibility"
+    name: "force_invert_color"
+    description: "Enable force force-dark for smart inversion and dark theme everywhere"
+    bug: "282821643"
+}
\ No newline at end of file
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 79acfbb..103725d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -638,9 +638,13 @@
      * Base class for all actions that can be performed on an
      * inflated view.
      *
-     *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
+     * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
      */
     private abstract static class Action implements Parcelable {
+        @IdRes
+        @UnsupportedAppUsage
+        int mViewId;
+
         public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException;
 
@@ -664,7 +668,7 @@
         public abstract int getActionTag();
 
         public String getUniqueKey() {
-            return (getActionTag() + "_" + viewId);
+            return (getActionTag() + "_" + mViewId);
         }
 
         /**
@@ -684,16 +688,12 @@
         public void visitUris(@NonNull Consumer<Uri> visitor) {
             // Nothing to visit by default
         }
-
-        @IdRes
-        @UnsupportedAppUsage
-        int viewId;
     }
 
     /**
      * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
      */
-    private static abstract class RuntimeAction extends Action {
+    private abstract static class RuntimeAction extends Action {
         @Override
         public final int getActionTag() {
             return 0;
@@ -796,15 +796,16 @@
             for (int i = 0; i < mActions.size(); i++) {
                 Action action = mActions.get(i);
                 if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
-                        && itemsAction.viewId == viewId
+                        && itemsAction.mViewId == viewId
                         && itemsAction.mServiceIntent != null) {
-                    mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId,
-                            itemsAction.mServiceIntent));
+                    mActions.set(i,
+                            new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId,
+                                    itemsAction.mServiceIntent));
                     isActionReplaced = true;
                 } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
-                        && intentAction.viewId == viewId) {
+                        && intentAction.mViewId == viewId) {
                     mActions.set(i, new SetRemoteCollectionItemListAdapterAction(
-                            intentAction.viewId, intentAction.intent));
+                            intentAction.mViewId, intentAction.mIntent));
                     isActionReplaced = true;
                 } else if (action instanceof ViewGroupActionAdd groupAction
                         && groupAction.mNestedViews != null) {
@@ -838,7 +839,7 @@
                 if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
                         && itemsAction.mServiceIntent != null)
                         || (action instanceof SetRemoteViewsAdapterIntent intentAction
-                                && intentAction.intent != null)
+                                && intentAction.mIntent != null)
                         || (action instanceof ViewGroupActionAdd groupAction
                                 && groupAction.mNestedViews != null
                                 && groupAction.mNestedViews.hasLegacyLists())) {
@@ -856,11 +857,7 @@
         if (mLandscape != null && mLandscape.hasLegacyLists()) {
             return true;
         }
-        if (mPortrait != null && mPortrait.hasLegacyLists()) {
-            return true;
-        }
-
-        return false;
+        return mPortrait != null && mPortrait.hasLegacyLists();
     }
 
     private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
@@ -913,31 +910,31 @@
     }
 
     private static class SetEmptyView extends Action {
-        int emptyViewId;
+        int mEmptyViewId;
 
         SetEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
-            this.viewId = viewId;
-            this.emptyViewId = emptyViewId;
+            this.mViewId = viewId;
+            this.mEmptyViewId = emptyViewId;
         }
 
         SetEmptyView(Parcel in) {
-            this.viewId = in.readInt();
-            this.emptyViewId = in.readInt();
+            this.mViewId = in.readInt();
+            this.mEmptyViewId = in.readInt();
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(this.viewId);
-            out.writeInt(this.emptyViewId);
+            out.writeInt(this.mViewId);
+            out.writeInt(this.mEmptyViewId);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View view = root.findViewById(viewId);
+            final View view = root.findViewById(mViewId);
             if (!(view instanceof AdapterView<?>)) return;
 
             AdapterView<?> adapterView = (AdapterView<?>) view;
 
-            final View emptyView = root.findViewById(emptyViewId);
+            final View emptyView = root.findViewById(mEmptyViewId);
             if (emptyView == null) return;
 
             adapterView.setEmptyView(emptyView);
@@ -950,24 +947,27 @@
     }
 
     private static class SetPendingIntentTemplate extends Action {
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        PendingIntent mPendingIntentTemplate;
+
         public SetPendingIntentTemplate(@IdRes int id, PendingIntent pendingIntentTemplate) {
-            this.viewId = id;
-            this.pendingIntentTemplate = pendingIntentTemplate;
+            this.mViewId = id;
+            this.mPendingIntentTemplate = pendingIntentTemplate;
         }
 
         public SetPendingIntentTemplate(Parcel parcel) {
-            viewId = parcel.readInt();
-            pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+            mViewId = parcel.readInt();
+            mPendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
+            dest.writeInt(mViewId);
+            PendingIntent.writePendingIntentOrNullToParcel(mPendingIntentTemplate, dest);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
@@ -981,10 +981,10 @@
                     }
                 };
                 av.setOnItemClickListener(listener);
-                av.setTag(pendingIntentTemplate);
+                av.setTag(mPendingIntentTemplate);
             } else {
                 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
-                        "an AdapterView (id: " + viewId + ")");
+                        "an AdapterView (id: " + mViewId + ")");
                 return;
             }
         }
@@ -1015,65 +1015,65 @@
         public int getActionTag() {
             return SET_PENDING_INTENT_TEMPLATE_TAG;
         }
-
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        PendingIntent pendingIntentTemplate;
     }
 
     private static class SetRemoteViewsAdapterList extends Action {
+        int mViewTypeCount;
+        ArrayList<RemoteViews> mList;
+
         public SetRemoteViewsAdapterList(@IdRes int id, ArrayList<RemoteViews> list,
                 int viewTypeCount) {
-            this.viewId = id;
-            this.list = list;
-            this.viewTypeCount = viewTypeCount;
+            this.mViewId = id;
+            this.mList = list;
+            this.mViewTypeCount = viewTypeCount;
         }
 
         public SetRemoteViewsAdapterList(Parcel parcel) {
-            viewId = parcel.readInt();
-            viewTypeCount = parcel.readInt();
-            list = parcel.createTypedArrayList(RemoteViews.CREATOR);
+            mViewId = parcel.readInt();
+            mViewTypeCount = parcel.readInt();
+            mList = parcel.createTypedArrayList(RemoteViews.CREATOR);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeInt(viewTypeCount);
-            dest.writeTypedList(list, flags);
+            dest.writeInt(mViewId);
+            dest.writeInt(mViewTypeCount);
+            dest.writeTypedList(mList, flags);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             // Ensure that we are applying to an AppWidget root
             if (!(rootParent instanceof AppWidgetHostView)) {
                 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
-                        "AppWidgets (root id: " + viewId + ")");
+                        "AppWidgets (root id: " + mViewId + ")");
                 return;
             }
             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
                 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
-                        "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
+                        "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")");
                 return;
             }
 
             if (target instanceof AbsListView) {
                 AbsListView v = (AbsListView) target;
                 Adapter a = v.getAdapter();
-                if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
-                    ((RemoteViewsListAdapter) a).setViewsList(list);
+                if (a instanceof RemoteViewsListAdapter && mViewTypeCount <= a.getViewTypeCount()) {
+                    ((RemoteViewsListAdapter) a).setViewsList(mList);
                 } else {
-                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
+                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), mList, mViewTypeCount,
                             params.colorResources));
                 }
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
                 Adapter a = v.getAdapter();
-                if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
-                    ((RemoteViewsListAdapter) a).setViewsList(list);
+                if (a instanceof RemoteViewsListAdapter && mViewTypeCount <= a.getViewTypeCount()) {
+                    ((RemoteViewsListAdapter) a).setViewsList(mList);
                 } else {
-                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
+                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), mList, mViewTypeCount,
                             params.colorResources));
                 }
             }
@@ -1086,11 +1086,8 @@
 
         @Override
         public String getUniqueKey() {
-            return (SET_REMOTE_ADAPTER_TAG + "_" + viewId);
+            return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId);
         }
-
-        int viewTypeCount;
-        ArrayList<RemoteViews> list;
     }
 
     /**
@@ -1135,19 +1132,20 @@
     }
 
     private class SetRemoteCollectionItemListAdapterAction extends Action {
-        private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture;
+        @NonNull
+        private CompletableFuture<RemoteCollectionItems> mItemsFuture;
         final Intent mServiceIntent;
 
         SetRemoteCollectionItemListAdapterAction(@IdRes int id,
                 @NonNull RemoteCollectionItems items) {
-            viewId = id;
+            mViewId = id;
             items.setHierarchyRootData(getHierarchyRootData());
             mItemsFuture = CompletableFuture.completedFuture(items);
             mServiceIntent = null;
         }
 
         SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
-            viewId = id;
+            mViewId = id;
             mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
             setHierarchyRootData(getHierarchyRootData());
             mServiceIntent = intent;
@@ -1196,7 +1194,7 @@
         }
 
         SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mItemsFuture = CompletableFuture.completedFuture(
                     new RemoteCollectionItems(parcel, getHierarchyRootData()));
             mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
@@ -1226,7 +1224,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
             items.writeToParcel(dest, flags, /* attached= */ true);
             dest.writeTypedObject(mServiceIntent, flags);
@@ -1235,7 +1233,7 @@
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
-            View target = root.findViewById(viewId);
+            View target = root.findViewById(mViewId);
             if (target == null) return;
 
             RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
@@ -1243,13 +1241,13 @@
             // Ensure that we are applying to an AppWidget root
             if (!(rootParent instanceof AppWidgetHostView)) {
                 Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
-                        + "AppWidgets (root id: " + viewId + ")");
+                        + "AppWidgets (root id: " + mViewId + ")");
                 return;
             }
 
             if (!(target instanceof AdapterView)) {
                 Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not "
-                        + "an AdapterView (id: " + viewId + ")");
+                        + "an AdapterView (id: " + mViewId + ")");
                 return;
             }
 
@@ -1289,59 +1287,62 @@
 
         @Override
         public String getUniqueKey() {
-            return (SET_REMOTE_ADAPTER_TAG + "_" + viewId);
+            return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId);
         }
     }
 
     private class SetRemoteViewsAdapterIntent extends Action {
+        Intent mIntent;
+        boolean mIsAsync = false;
+
         public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
-            this.viewId = id;
-            this.intent = intent;
+            this.mViewId = id;
+            this.mIntent = intent;
         }
 
         public SetRemoteViewsAdapterIntent(Parcel parcel) {
-            viewId = parcel.readInt();
-            intent = parcel.readTypedObject(Intent.CREATOR);
+            mViewId = parcel.readInt();
+            mIntent = parcel.readTypedObject(Intent.CREATOR);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeTypedObject(intent, flags);
+            dest.writeInt(mViewId);
+            dest.writeTypedObject(mIntent, flags);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             // Ensure that we are applying to an AppWidget root
             if (!(rootParent instanceof AppWidgetHostView)) {
                 Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
-                        + "AppWidgets (root id: " + viewId + ")");
+                        + "AppWidgets (root id: " + mViewId + ")");
                 return;
             }
 
             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
                 Log.e(LOG_TAG, "Cannot setRemoteAdapter on a view which is not "
-                        + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
+                        + "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")");
                 return;
             }
 
             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
             // RemoteViewsService
             AppWidgetHostView host = (AppWidgetHostView) rootParent;
-            intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
+            mIntent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
                     .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
                             hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
 
             if (target instanceof AbsListView) {
                 AbsListView v = (AbsListView) target;
-                v.setRemoteViewsAdapter(intent, isAsync);
+                v.setRemoteViewsAdapter(mIntent, mIsAsync);
                 v.setRemoteViewsInteractionHandler(params.handler);
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
-                v.setRemoteViewsAdapter(intent, isAsync);
+                v.setRemoteViewsAdapter(mIntent, mIsAsync);
                 v.setRemoteViewsOnClickHandler(params.handler);
             }
         }
@@ -1349,8 +1350,8 @@
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
                 ActionApplyParams params) {
-            SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
-            copy.isAsync = true;
+            SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(mViewId, mIntent);
+            copy.mIsAsync = true;
             return copy;
         }
 
@@ -1358,9 +1359,6 @@
         public int getActionTag() {
             return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
         }
-
-        Intent intent;
-        boolean isAsync = false;
     }
 
     /**
@@ -1369,26 +1367,27 @@
      * to launch the provided {@link PendingIntent}.
      */
     private class SetOnClickResponse extends Action {
+        final RemoteResponse mResponse;
 
         SetOnClickResponse(@IdRes int id, RemoteResponse response) {
-            this.viewId = id;
+            this.mViewId = id;
             this.mResponse = response;
         }
 
         SetOnClickResponse(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mResponse = new RemoteResponse();
             mResponse.readFromParcel(parcel);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             mResponse.writeToParcel(dest, flags);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             if (mResponse.mPendingIntent != null) {
@@ -1397,7 +1396,7 @@
                 // AdapterView's children?
                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
                     Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
-                            + "(id: " + viewId + ")");
+                            + "(id: " + mViewId + ")");
                     ApplicationInfo appInfo = root.getContext().getApplicationInfo();
 
                     // We let this slide for HC and ICS so as to not break compatibility. It should
@@ -1435,8 +1434,6 @@
         public int getActionTag() {
             return SET_ON_CLICK_RESPONSE_TAG;
         }
-
-        final RemoteResponse mResponse;
     }
 
     /**
@@ -1446,32 +1443,31 @@
      * to launch the provided {@link PendingIntent}.
      */
     private class SetOnCheckedChangeResponse extends Action {
-
         private final RemoteResponse mResponse;
 
         SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) {
-            this.viewId = id;
+            this.mViewId = id;
             this.mResponse = response;
         }
 
         SetOnCheckedChangeResponse(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mResponse = new RemoteResponse();
             mResponse.readFromParcel(parcel);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             mResponse.writeToParcel(dest, flags);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
             if (!(target instanceof CompoundButton)) {
                 Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on "
-                        + "non-CompoundButton child (id: " + viewId + ")");
+                        + "non-CompoundButton child (id: " + mViewId + ")");
                 return;
             }
             CompoundButton button = (CompoundButton) target;
@@ -1481,7 +1477,7 @@
                 // must use setOnCheckedChangeFillInIntent instead.
                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
                     Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item "
-                            + "(id: " + viewId + ")");
+                            + "(id: " + mViewId + ")");
                     return;
                 }
                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
@@ -1648,36 +1644,40 @@
      * <p>
      */
     private static class SetDrawableTint extends Action {
+        boolean mTargetBackground;
+        @ColorInt int mColorFilter;
+        PorterDuff.Mode mFilterMode;
+
         SetDrawableTint(@IdRes int id, boolean targetBackground,
                 @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
-            this.viewId = id;
-            this.targetBackground = targetBackground;
-            this.colorFilter = colorFilter;
-            this.filterMode = mode;
+            this.mViewId = id;
+            this.mTargetBackground = targetBackground;
+            this.mColorFilter = colorFilter;
+            this.mFilterMode = mode;
         }
 
         SetDrawableTint(Parcel parcel) {
-            viewId = parcel.readInt();
-            targetBackground = parcel.readInt() != 0;
-            colorFilter = parcel.readInt();
-            filterMode = PorterDuff.intToMode(parcel.readInt());
+            mViewId = parcel.readInt();
+            mTargetBackground = parcel.readInt() != 0;
+            mColorFilter = parcel.readInt();
+            mFilterMode = PorterDuff.intToMode(parcel.readInt());
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeInt(targetBackground ? 1 : 0);
-            dest.writeInt(colorFilter);
-            dest.writeInt(PorterDuff.modeToInt(filterMode));
+            dest.writeInt(mViewId);
+            dest.writeInt(mTargetBackground ? 1 : 0);
+            dest.writeInt(mColorFilter);
+            dest.writeInt(PorterDuff.modeToInt(mFilterMode));
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             // Pick the correct drawable to modify for this view
             Drawable targetDrawable = null;
-            if (targetBackground) {
+            if (mTargetBackground) {
                 targetDrawable = target.getBackground();
             } else if (target instanceof ImageView) {
                 ImageView imageView = (ImageView) target;
@@ -1685,7 +1685,7 @@
             }
 
             if (targetDrawable != null) {
-                targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
+                targetDrawable.mutate().setColorFilter(mColorFilter, mFilterMode);
             }
         }
 
@@ -1693,10 +1693,6 @@
         public int getActionTag() {
             return SET_DRAWABLE_TINT_TAG;
         }
-
-        boolean targetBackground;
-        @ColorInt int colorFilter;
-        PorterDuff.Mode filterMode;
     }
 
     /**
@@ -1709,27 +1705,26 @@
      * <p>
      */
     private class SetRippleDrawableColor extends Action {
-
         ColorStateList mColorStateList;
 
         SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) {
-            this.viewId = id;
+            this.mViewId = id;
             this.mColorStateList = colorStateList;
         }
 
         SetRippleDrawableColor(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mColorStateList = parcel.readParcelable(null, android.content.res.ColorStateList.class);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeParcelable(mColorStateList, 0);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             // Pick the correct drawable to modify for this view
@@ -1756,23 +1751,23 @@
         final boolean mNext;
 
         ViewContentNavigation(@IdRes int viewId, boolean next) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             this.mNext = next;
         }
 
         ViewContentNavigation(Parcel in) {
-            this.viewId = in.readInt();
+            this.mViewId = in.readInt();
             this.mNext = in.readBoolean();
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(this.viewId);
+            out.writeInt(this.mViewId);
             out.writeBoolean(this.mNext);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View view = root.findViewById(viewId);
+            final View view = root.findViewById(mViewId);
             if (view == null) return;
 
             try {
@@ -1794,7 +1789,6 @@
     }
 
     private static class BitmapCache {
-
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         ArrayList<Bitmap> mBitmaps;
         SparseIntArray mBitmapHashes;
@@ -1861,45 +1855,45 @@
     }
 
     private class BitmapReflectionAction extends Action {
-        int bitmapId;
+        int mBitmapId;
         @UnsupportedAppUsage
-        Bitmap bitmap;
+        Bitmap mBitmap;
         @UnsupportedAppUsage
-        String methodName;
+        String mMethodName;
 
         BitmapReflectionAction(@IdRes int viewId, String methodName, Bitmap bitmap) {
-            this.bitmap = bitmap;
-            this.viewId = viewId;
-            this.methodName = methodName;
-            bitmapId = mBitmapCache.getBitmapId(bitmap);
+            this.mBitmap = bitmap;
+            this.mViewId = viewId;
+            this.mMethodName = methodName;
+            mBitmapId = mBitmapCache.getBitmapId(bitmap);
         }
 
         BitmapReflectionAction(Parcel in) {
-            viewId = in.readInt();
-            methodName = in.readString8();
-            bitmapId = in.readInt();
-            bitmap = mBitmapCache.getBitmapForId(bitmapId);
+            mViewId = in.readInt();
+            mMethodName = in.readString8();
+            mBitmapId = in.readInt();
+            mBitmap = mBitmapCache.getBitmapForId(mBitmapId);
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeString8(methodName);
-            dest.writeInt(bitmapId);
+            dest.writeInt(mViewId);
+            dest.writeString8(mMethodName);
+            dest.writeInt(mBitmapId);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
-            ReflectionAction ra = new ReflectionAction(viewId, methodName,
+            ReflectionAction ra = new ReflectionAction(mViewId, mMethodName,
                     BaseReflectionAction.BITMAP,
-                    bitmap);
+                    mBitmap);
             ra.apply(root, rootParent, params);
         }
 
         @Override
         public void setHierarchyRootData(HierarchyRootData rootData) {
-            bitmapId = rootData.mBitmapCache.getBitmapId(bitmap);
+            mBitmapId = rootData.mBitmapCache.getBitmapId(mBitmap);
         }
 
         @Override
@@ -1933,30 +1927,30 @@
         static final int BLEND_MODE = 17;
 
         @UnsupportedAppUsage
-        String methodName;
-        int type;
+        String mMethodName;
+        int mType;
 
         BaseReflectionAction(@IdRes int viewId, String methodName, int type) {
-            this.viewId = viewId;
-            this.methodName = methodName;
-            this.type = type;
+            this.mViewId = viewId;
+            this.mMethodName = methodName;
+            this.mType = type;
         }
 
         BaseReflectionAction(Parcel in) {
-            this.viewId = in.readInt();
-            this.methodName = in.readString8();
-            this.type = in.readInt();
+            this.mViewId = in.readInt();
+            this.mMethodName = in.readString8();
+            this.mType = in.readInt();
             //noinspection ConstantIfStatement
             if (false) {
-                Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
-                        + " methodName=" + this.methodName + " type=" + this.type);
+                Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.mViewId)
+                        + " methodName=" + this.mMethodName + " type=" + this.mType);
             }
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(this.viewId);
-            out.writeString8(this.methodName);
-            out.writeInt(this.type);
+            out.writeInt(this.mViewId);
+            out.writeString8(this.mMethodName);
+            out.writeInt(this.mType);
         }
 
         /**
@@ -1971,16 +1965,16 @@
 
         @Override
         public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View view = root.findViewById(viewId);
+            final View view = root.findViewById(mViewId);
             if (view == null) return;
 
-            Class<?> param = getParameterType(this.type);
+            Class<?> param = getParameterType(this.mType);
             if (param == null) {
-                throw new ActionException("bad type: " + this.type);
+                throw new ActionException("bad type: " + this.mType);
             }
             Object value = getParameterValue(view);
             try {
-                getMethod(view, this.methodName, param, false /* async */).invoke(view, value);
+                getMethod(view, this.mMethodName, param, false /* async */).invoke(view, value);
             } catch (Throwable ex) {
                 throw new ActionException(ex);
             }
@@ -1989,17 +1983,17 @@
         @Override
         public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
                 ActionApplyParams params) {
-            final View view = root.findViewById(viewId);
+            final View view = root.findViewById(mViewId);
             if (view == null) return ACTION_NOOP;
 
-            Class<?> param = getParameterType(this.type);
+            Class<?> param = getParameterType(this.mType);
             if (param == null) {
-                throw new ActionException("bad type: " + this.type);
+                throw new ActionException("bad type: " + this.mType);
             }
 
             Object value = getParameterValue(view);
             try {
-                MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
+                MethodHandle method = getMethod(view, this.mMethodName, param, true /* async */);
                 // Upload the bitmap to GPU if the parameter is of type Bitmap or Icon.
                 // Since bitmaps in framework are seldomly modified, this is supposed to accelerate
                 // the operations.
@@ -2025,7 +2019,7 @@
                     if (endAction instanceof ViewStub.ViewReplaceRunnable) {
                         root.createTree();
                         // Replace child tree
-                        root.findViewTreeById(viewId).replaceView(
+                        root.findViewTreeById(mViewId).replaceView(
                                 ((ViewStub.ViewReplaceRunnable) endAction).view);
                     }
                     return new RunnableAction(endAction);
@@ -2039,7 +2033,7 @@
 
         public final int mergeBehavior() {
             // smoothScrollBy is cumulative, everything else overwites.
-            if (methodName.equals("smoothScrollBy")) {
+            if (mMethodName.equals("smoothScrollBy")) {
                 return MERGE_APPEND;
             } else {
                 return MERGE_REPLACE;
@@ -2050,17 +2044,17 @@
         public final String getUniqueKey() {
             // Each type of reflection action corresponds to a setter, so each should be seen as
             // unique from the standpoint of merging.
-            return super.getUniqueKey() + this.methodName + this.type;
+            return super.getUniqueKey() + this.mMethodName + this.mType;
         }
 
         @Override
         public final boolean prefersAsyncApply() {
-            return this.type == URI || this.type == ICON;
+            return this.mType == URI || this.mType == ICON;
         }
 
         @Override
         public void visitUris(@NonNull Consumer<Uri> visitor) {
-            switch (this.type) {
+            switch (this.mType) {
                 case URI:
                     final Uri uri = (Uri) getParameterValue(null);
                     if (uri != null) visitor.accept(uri);
@@ -2076,53 +2070,53 @@
     /** Class for the reflection actions. */
     private static final class ReflectionAction extends BaseReflectionAction {
         @UnsupportedAppUsage
-        Object value;
+        Object mValue;
 
         ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
             super(viewId, methodName, type);
-            this.value = value;
+            this.mValue = value;
         }
 
         ReflectionAction(Parcel in) {
             super(in);
             // For some values that may have been null, we first check a flag to see if they were
             // written to the parcel.
-            switch (this.type) {
+            switch (this.mType) {
                 case BOOLEAN:
-                    this.value = in.readBoolean();
+                    this.mValue = in.readBoolean();
                     break;
                 case BYTE:
-                    this.value = in.readByte();
+                    this.mValue = in.readByte();
                     break;
                 case SHORT:
-                    this.value = (short) in.readInt();
+                    this.mValue = (short) in.readInt();
                     break;
                 case INT:
-                    this.value = in.readInt();
+                    this.mValue = in.readInt();
                     break;
                 case LONG:
-                    this.value = in.readLong();
+                    this.mValue = in.readLong();
                     break;
                 case FLOAT:
-                    this.value = in.readFloat();
+                    this.mValue = in.readFloat();
                     break;
                 case DOUBLE:
-                    this.value = in.readDouble();
+                    this.mValue = in.readDouble();
                     break;
                 case CHAR:
-                    this.value = (char) in.readInt();
+                    this.mValue = (char) in.readInt();
                     break;
                 case STRING:
-                    this.value = in.readString8();
+                    this.mValue = in.readString8();
                     break;
                 case CHAR_SEQUENCE:
-                    this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+                    this.mValue = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
                     break;
                 case URI:
-                    this.value = in.readTypedObject(Uri.CREATOR);
+                    this.mValue = in.readTypedObject(Uri.CREATOR);
                     break;
                 case BITMAP:
-                    this.value = in.readTypedObject(Bitmap.CREATOR);
+                    this.mValue = in.readTypedObject(Bitmap.CREATOR);
                     break;
                 case BUNDLE:
                     // Because we use Parcel.allowSquashing() when writing, and that affects
@@ -2131,24 +2125,24 @@
                     //  just happens to have that effect on Bundle.readFromParcel().
                     // TODO(b/212731590): build this state tracking into Bundle
                     if (in.hasReadWriteHelper()) {
-                        this.value = in.readBundle();
+                        this.mValue = in.readBundle();
                     } else {
                         in.setReadWriteHelper(ALTERNATIVE_DEFAULT);
-                        this.value = in.readBundle();
+                        this.mValue = in.readBundle();
                         in.setReadWriteHelper(null);
                     }
                     break;
                 case INTENT:
-                    this.value = in.readTypedObject(Intent.CREATOR);
+                    this.mValue = in.readTypedObject(Intent.CREATOR);
                     break;
                 case COLOR_STATE_LIST:
-                    this.value = in.readTypedObject(ColorStateList.CREATOR);
+                    this.mValue = in.readTypedObject(ColorStateList.CREATOR);
                     break;
                 case ICON:
-                    this.value = in.readTypedObject(Icon.CREATOR);
+                    this.mValue = in.readTypedObject(Icon.CREATOR);
                     break;
                 case BLEND_MODE:
-                    this.value = BlendMode.fromValue(in.readInt());
+                    this.mValue = BlendMode.fromValue(in.readInt());
                     break;
                 default:
                     break;
@@ -2159,49 +2153,49 @@
             super.writeToParcel(out, flags);
             // For some values which are null, we record an integer flag to indicate whether
             // we have written a valid value to the parcel.
-            switch (this.type) {
+            switch (this.mType) {
                 case BOOLEAN:
-                    out.writeBoolean((Boolean) this.value);
+                    out.writeBoolean((Boolean) this.mValue);
                     break;
                 case BYTE:
-                    out.writeByte((Byte) this.value);
+                    out.writeByte((Byte) this.mValue);
                     break;
                 case SHORT:
-                    out.writeInt((Short) this.value);
+                    out.writeInt((Short) this.mValue);
                     break;
                 case INT:
-                    out.writeInt((Integer) this.value);
+                    out.writeInt((Integer) this.mValue);
                     break;
                 case LONG:
-                    out.writeLong((Long) this.value);
+                    out.writeLong((Long) this.mValue);
                     break;
                 case FLOAT:
-                    out.writeFloat((Float) this.value);
+                    out.writeFloat((Float) this.mValue);
                     break;
                 case DOUBLE:
-                    out.writeDouble((Double) this.value);
+                    out.writeDouble((Double) this.mValue);
                     break;
                 case CHAR:
-                    out.writeInt((int) ((Character) this.value).charValue());
+                    out.writeInt((int) ((Character) this.mValue).charValue());
                     break;
                 case STRING:
-                    out.writeString8((String) this.value);
+                    out.writeString8((String) this.mValue);
                     break;
                 case CHAR_SEQUENCE:
-                    TextUtils.writeToParcel((CharSequence) this.value, out, flags);
+                    TextUtils.writeToParcel((CharSequence) this.mValue, out, flags);
                     break;
                 case BUNDLE:
-                    out.writeBundle((Bundle) this.value);
+                    out.writeBundle((Bundle) this.mValue);
                     break;
                 case BLEND_MODE:
-                    out.writeInt(BlendMode.toValue((BlendMode) this.value));
+                    out.writeInt(BlendMode.toValue((BlendMode) this.mValue));
                     break;
                 case URI:
                 case BITMAP:
                 case INTENT:
                 case COLOR_STATE_LIST:
                 case ICON:
-                    out.writeTypedObject((Parcelable) this.value, flags);
+                    out.writeTypedObject((Parcelable) this.mValue, flags);
                     break;
                 default:
                     break;
@@ -2211,7 +2205,7 @@
         @Nullable
         @Override
         protected Object getParameterValue(@Nullable View view) throws ActionException {
-            return this.value;
+            return this.mValue;
         }
 
         @Override
@@ -2221,7 +2215,6 @@
     }
 
     private static final class ResourceReflectionAction extends BaseReflectionAction {
-
         static final int DIMEN_RESOURCE = 1;
         static final int COLOR_RESOURCE = 2;
         static final int STRING_RESOURCE = 3;
@@ -2258,7 +2251,7 @@
             try {
                 switch (this.mResourceType) {
                     case DIMEN_RESOURCE:
-                        switch (this.type) {
+                        switch (this.mType) {
                             case BaseReflectionAction.INT:
                                 return mResId == 0 ? 0 : resources.getDimensionPixelSize(mResId);
                             case BaseReflectionAction.FLOAT:
@@ -2266,10 +2259,10 @@
                             default:
                                 throw new ActionException(
                                         "dimen resources must be used as INT or FLOAT, "
-                                                + "not " + this.type);
+                                                + "not " + this.mType);
                         }
                     case COLOR_RESOURCE:
-                        switch (this.type) {
+                        switch (this.mType) {
                             case BaseReflectionAction.INT:
                                 return mResId == 0 ? 0 : view.getContext().getColor(mResId);
                             case BaseReflectionAction.COLOR_STATE_LIST:
@@ -2278,10 +2271,10 @@
                             default:
                                 throw new ActionException(
                                         "color resources must be used as INT or COLOR_STATE_LIST,"
-                                                + " not " + this.type);
+                                                + " not " + this.mType);
                         }
                     case STRING_RESOURCE:
-                        switch (this.type) {
+                        switch (this.mType) {
                             case BaseReflectionAction.CHAR_SEQUENCE:
                                 return mResId == 0 ? null : resources.getText(mResId);
                             case BaseReflectionAction.STRING:
@@ -2289,7 +2282,7 @@
                             default:
                                 throw new ActionException(
                                         "string resources must be used as STRING or CHAR_SEQUENCE,"
-                                                + " not " + this.type);
+                                                + " not " + this.mType);
                         }
                     default:
                         throw new ActionException("unknown resource type: " + this.mResourceType);
@@ -2308,7 +2301,6 @@
     }
 
     private static final class AttributeReflectionAction extends BaseReflectionAction {
-
         static final int DIMEN_RESOURCE = 1;
         static final int COLOR_RESOURCE = 2;
         static final int STRING_RESOURCE = 3;
@@ -2347,7 +2339,7 @@
                 }
                 switch (this.mResourceType) {
                     case DIMEN_RESOURCE:
-                        switch (this.type) {
+                        switch (this.mType) {
                             case BaseReflectionAction.INT:
                                 return typedArray.getDimensionPixelSize(0, 0);
                             case BaseReflectionAction.FLOAT:
@@ -2356,10 +2348,10 @@
                                 throw new ActionException(
                                         "dimen attribute 0x" + Integer.toHexString(this.mAttrId)
                                                 + " must be used as INT or FLOAT,"
-                                                + " not " + this.type);
+                                                + " not " + this.mType);
                         }
                     case COLOR_RESOURCE:
-                        switch (this.type) {
+                        switch (this.mType) {
                             case BaseReflectionAction.INT:
                                 return typedArray.getColor(0, 0);
                             case BaseReflectionAction.COLOR_STATE_LIST:
@@ -2368,10 +2360,10 @@
                                 throw new ActionException(
                                         "color attribute 0x" + Integer.toHexString(this.mAttrId)
                                                 + " must be used as INT or COLOR_STATE_LIST,"
-                                                + " not " + this.type);
+                                                + " not " + this.mType);
                         }
                     case STRING_RESOURCE:
-                        switch (this.type) {
+                        switch (this.mType) {
                             case BaseReflectionAction.CHAR_SEQUENCE:
                                 return typedArray.getText(0);
                             case BaseReflectionAction.STRING:
@@ -2380,7 +2372,7 @@
                                 throw new ActionException(
                                         "string attribute 0x" + Integer.toHexString(this.mAttrId)
                                                 + " must be used as STRING or CHAR_SEQUENCE,"
-                                                + " not " + this.type);
+                                                + " not " + this.mType);
                         }
                     default:
                         // Note: This can only be an implementation error.
@@ -2402,7 +2394,6 @@
         }
     }
     private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
-
         private final float mValue;
         @ComplexDimensionUnit
         private final int mUnit;
@@ -2435,14 +2426,14 @@
             DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
             try {
                 int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
-                switch (this.type) {
+                switch (this.mType) {
                     case ReflectionAction.INT:
                         return TypedValue.complexToDimensionPixelSize(data, dm);
                     case ReflectionAction.FLOAT:
                         return TypedValue.complexToDimension(data, dm);
                     default:
                         throw new ActionException(
-                                "parameter type must be INT or FLOAT, not " + this.type);
+                                "parameter type must be INT or FLOAT, not " + this.mType);
                 }
             } catch (ActionException ex) {
                 throw ex;
@@ -2458,7 +2449,6 @@
     }
 
     private static final class NightModeReflectionAction extends BaseReflectionAction {
-
         private final Object mLightValue;
         private final Object mDarkValue;
 
@@ -2475,7 +2465,7 @@
 
         NightModeReflectionAction(Parcel in) {
             super(in);
-            switch (this.type) {
+            switch (this.mType) {
                 case ICON:
                     mLightValue = in.readTypedObject(Icon.CREATOR);
                     mDarkValue = in.readTypedObject(Icon.CREATOR);
@@ -2489,14 +2479,14 @@
                     mDarkValue = in.readInt();
                     break;
                 default:
-                    throw new ActionException("Unexpected night mode action type: " + this.type);
+                    throw new ActionException("Unexpected night mode action type: " + this.mType);
             }
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
-            switch (this.type) {
+            switch (this.mType) {
                 case ICON:
                 case COLOR_STATE_LIST:
                     out.writeTypedObject((Parcelable) mLightValue, flags);
@@ -2525,7 +2515,7 @@
 
         @Override
         public void visitUris(@NonNull Consumer<Uri> visitor) {
-            if (this.type == ICON) {
+            if (this.mType == ICON) {
                 visitIconUri((Icon) mDarkValue, visitor);
                 visitIconUri((Icon) mLightValue, visitor);
             }
@@ -2617,7 +2607,7 @@
         }
 
         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             mNestedViews = nestedViews;
             mIndex = index;
             mStableId = stableId;
@@ -2625,7 +2615,7 @@
         }
 
         ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mIndex = parcel.readInt();
             mStableId = parcel.readInt();
             mNestedViews = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
@@ -2633,7 +2623,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeInt(mIndex);
             dest.writeInt(mStableId);
             mNestedViews.writeToParcel(dest, flags);
@@ -2658,7 +2648,7 @@
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final Context context = root.getContext();
-            final ViewGroup target = root.findViewById(viewId);
+            final ViewGroup target = root.findViewById(mViewId);
 
             if (target == null) {
                 return;
@@ -2713,7 +2703,7 @@
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
-            ViewTree target = root.findViewTreeById(viewId);
+            ViewTree target = root.findViewTreeById(mViewId);
             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
                 return ACTION_NOOP;
             }
@@ -2845,23 +2835,23 @@
         }
 
         ViewGroupActionRemove(@IdRes int viewId, @IdRes int viewIdToKeep) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             mViewIdToKeep = viewIdToKeep;
         }
 
         ViewGroupActionRemove(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mViewIdToKeep = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeInt(mViewIdToKeep);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final ViewGroup target = root.findViewById(viewId);
+            final ViewGroup target = root.findViewById(mViewId);
 
             if (target == null) {
                 return;
@@ -2888,7 +2878,7 @@
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
-            ViewTree target = root.findViewTreeById(viewId);
+            ViewTree target = root.findViewTreeById(mViewId);
 
             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
                 return ACTION_NOOP;
@@ -2953,22 +2943,21 @@
      * Action to remove a view from its parent.
      */
     private static class RemoveFromParentAction extends Action {
-
         RemoveFromParentAction(@IdRes int viewId) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
         }
 
         RemoveFromParentAction(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
 
             if (target == null || target == root) {
                 return;
@@ -2986,7 +2975,7 @@
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the correct view.
             root.createTree();
-            ViewTree target = root.findViewTreeById(viewId);
+            ViewTree target = root.findViewTreeById(mViewId);
 
             if (target == null || target == root) {
                 return ACTION_NOOP;
@@ -3023,88 +3012,96 @@
      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
      */
     private static class TextViewDrawableAction extends Action {
+        boolean mIsRelative = false;
+        boolean mUseIcons = false;
+        int mD1, mD2, mD3, mD4;
+        Icon mI1, mI2, mI3, mI4;
+
+        boolean mDrawablesLoaded = false;
+        Drawable mId1, mId2, mId3, mId4;
+
         public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, @DrawableRes int d1,
                 @DrawableRes int d2, @DrawableRes int d3, @DrawableRes int d4) {
-            this.viewId = viewId;
-            this.isRelative = isRelative;
-            this.useIcons = false;
-            this.d1 = d1;
-            this.d2 = d2;
-            this.d3 = d3;
-            this.d4 = d4;
+            this.mViewId = viewId;
+            this.mIsRelative = isRelative;
+            this.mUseIcons = false;
+            this.mD1 = d1;
+            this.mD2 = d2;
+            this.mD3 = d3;
+            this.mD4 = d4;
         }
 
         public TextViewDrawableAction(@IdRes int viewId, boolean isRelative,
                 Icon i1, Icon i2, Icon i3, Icon i4) {
-            this.viewId = viewId;
-            this.isRelative = isRelative;
-            this.useIcons = true;
-            this.i1 = i1;
-            this.i2 = i2;
-            this.i3 = i3;
-            this.i4 = i4;
+            this.mViewId = viewId;
+            this.mIsRelative = isRelative;
+            this.mUseIcons = true;
+            this.mI1 = i1;
+            this.mI2 = i2;
+            this.mI3 = i3;
+            this.mI4 = i4;
         }
 
         public TextViewDrawableAction(Parcel parcel) {
-            viewId = parcel.readInt();
-            isRelative = (parcel.readInt() != 0);
-            useIcons = (parcel.readInt() != 0);
-            if (useIcons) {
-                i1 = parcel.readTypedObject(Icon.CREATOR);
-                i2 = parcel.readTypedObject(Icon.CREATOR);
-                i3 = parcel.readTypedObject(Icon.CREATOR);
-                i4 = parcel.readTypedObject(Icon.CREATOR);
+            mViewId = parcel.readInt();
+            mIsRelative = (parcel.readInt() != 0);
+            mUseIcons = (parcel.readInt() != 0);
+            if (mUseIcons) {
+                mI1 = parcel.readTypedObject(Icon.CREATOR);
+                mI2 = parcel.readTypedObject(Icon.CREATOR);
+                mI3 = parcel.readTypedObject(Icon.CREATOR);
+                mI4 = parcel.readTypedObject(Icon.CREATOR);
             } else {
-                d1 = parcel.readInt();
-                d2 = parcel.readInt();
-                d3 = parcel.readInt();
-                d4 = parcel.readInt();
+                mD1 = parcel.readInt();
+                mD2 = parcel.readInt();
+                mD3 = parcel.readInt();
+                mD4 = parcel.readInt();
             }
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeInt(isRelative ? 1 : 0);
-            dest.writeInt(useIcons ? 1 : 0);
-            if (useIcons) {
-                dest.writeTypedObject(i1, 0);
-                dest.writeTypedObject(i2, 0);
-                dest.writeTypedObject(i3, 0);
-                dest.writeTypedObject(i4, 0);
+            dest.writeInt(mViewId);
+            dest.writeInt(mIsRelative ? 1 : 0);
+            dest.writeInt(mUseIcons ? 1 : 0);
+            if (mUseIcons) {
+                dest.writeTypedObject(mI1, 0);
+                dest.writeTypedObject(mI2, 0);
+                dest.writeTypedObject(mI3, 0);
+                dest.writeTypedObject(mI4, 0);
             } else {
-                dest.writeInt(d1);
-                dest.writeInt(d2);
-                dest.writeInt(d3);
-                dest.writeInt(d4);
+                dest.writeInt(mD1);
+                dest.writeInt(mD2);
+                dest.writeInt(mD3);
+                dest.writeInt(mD4);
             }
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final TextView target = root.findViewById(viewId);
+            final TextView target = root.findViewById(mViewId);
             if (target == null) return;
-            if (drawablesLoaded) {
-                if (isRelative) {
-                    target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
+            if (mDrawablesLoaded) {
+                if (mIsRelative) {
+                    target.setCompoundDrawablesRelativeWithIntrinsicBounds(mId1, mId2, mId3, mId4);
                 } else {
-                    target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
+                    target.setCompoundDrawablesWithIntrinsicBounds(mId1, mId2, mId3, mId4);
                 }
-            } else if (useIcons) {
+            } else if (mUseIcons) {
                 final Context ctx = target.getContext();
-                final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
-                final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
-                final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
-                final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
-                if (isRelative) {
+                final Drawable id1 = mI1 == null ? null : mI1.loadDrawable(ctx);
+                final Drawable id2 = mI2 == null ? null : mI2.loadDrawable(ctx);
+                final Drawable id3 = mI3 == null ? null : mI3.loadDrawable(ctx);
+                final Drawable id4 = mI4 == null ? null : mI4.loadDrawable(ctx);
+                if (mIsRelative) {
                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
                 } else {
                     target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
                 }
             } else {
-                if (isRelative) {
-                    target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
+                if (mIsRelative) {
+                    target.setCompoundDrawablesRelativeWithIntrinsicBounds(mD1, mD2, mD3, mD4);
                 } else {
-                    target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
+                    target.setCompoundDrawablesWithIntrinsicBounds(mD1, mD2, mD3, mD4);
                 }
             }
         }
@@ -3112,34 +3109,34 @@
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
                 ActionApplyParams params) {
-            final TextView target = root.findViewById(viewId);
+            final TextView target = root.findViewById(mViewId);
             if (target == null) return ACTION_NOOP;
 
-            TextViewDrawableAction copy = useIcons ?
-                    new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
-                    new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
+            TextViewDrawableAction copy = mUseIcons
+                    ? new TextViewDrawableAction(mViewId, mIsRelative, mI1, mI2, mI3, mI4)
+                    : new TextViewDrawableAction(mViewId, mIsRelative, mD1, mD2, mD3, mD4);
 
             // Load the drawables on the background thread.
-            copy.drawablesLoaded = true;
+            copy.mDrawablesLoaded = true;
             final Context ctx = target.getContext();
 
-            if (useIcons) {
-                copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
-                copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
-                copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
-                copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
+            if (mUseIcons) {
+                copy.mId1 = mI1 == null ? null : mI1.loadDrawable(ctx);
+                copy.mId2 = mI2 == null ? null : mI2.loadDrawable(ctx);
+                copy.mId3 = mI3 == null ? null : mI3.loadDrawable(ctx);
+                copy.mId4 = mI4 == null ? null : mI4.loadDrawable(ctx);
             } else {
-                copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
-                copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
-                copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
-                copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
+                copy.mId1 = mD1 == 0 ? null : ctx.getDrawable(mD1);
+                copy.mId2 = mD2 == 0 ? null : ctx.getDrawable(mD2);
+                copy.mId3 = mD3 == 0 ? null : ctx.getDrawable(mD3);
+                copy.mId4 = mD4 == 0 ? null : ctx.getDrawable(mD4);
             }
             return copy;
         }
 
         @Override
         public boolean prefersAsyncApply() {
-            return useIcons;
+            return mUseIcons;
         }
 
         @Override
@@ -3149,110 +3146,101 @@
 
         @Override
         public void visitUris(@NonNull Consumer<Uri> visitor) {
-            if (useIcons) {
-                visitIconUri(i1, visitor);
-                visitIconUri(i2, visitor);
-                visitIconUri(i3, visitor);
-                visitIconUri(i4, visitor);
+            if (mUseIcons) {
+                visitIconUri(mI1, visitor);
+                visitIconUri(mI2, visitor);
+                visitIconUri(mI3, visitor);
+                visitIconUri(mI4, visitor);
             }
         }
-
-        boolean isRelative = false;
-        boolean useIcons = false;
-        int d1, d2, d3, d4;
-        Icon i1, i2, i3, i4;
-
-        boolean drawablesLoaded = false;
-        Drawable id1, id2, id3, id4;
     }
 
     /**
      * Helper action to set text size on a TextView in any supported units.
      */
     private static class TextViewSizeAction extends Action {
+        int mUnits;
+        float mSize;
+
         TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) {
-            this.viewId = viewId;
-            this.units = units;
-            this.size = size;
+            this.mViewId = viewId;
+            this.mUnits = units;
+            this.mSize = size;
         }
 
         TextViewSizeAction(Parcel parcel) {
-            viewId = parcel.readInt();
-            units = parcel.readInt();
-            size  = parcel.readFloat();
+            mViewId = parcel.readInt();
+            mUnits = parcel.readInt();
+            mSize = parcel.readFloat();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeInt(units);
-            dest.writeFloat(size);
+            dest.writeInt(mViewId);
+            dest.writeInt(mUnits);
+            dest.writeFloat(mSize);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final TextView target = root.findViewById(viewId);
+            final TextView target = root.findViewById(mViewId);
             if (target == null) return;
-            target.setTextSize(units, size);
+            target.setTextSize(mUnits, mSize);
         }
 
         @Override
         public int getActionTag() {
             return TEXT_VIEW_SIZE_ACTION_TAG;
         }
-
-        int units;
-        float size;
     }
 
     /**
      * Helper action to set padding on a View.
      */
     private static class ViewPaddingAction extends Action {
+        @Px int mLeft, mTop, mRight, mBottom;
+
         public ViewPaddingAction(@IdRes int viewId, @Px int left, @Px int top,
                 @Px int right, @Px int bottom) {
-            this.viewId = viewId;
-            this.left = left;
-            this.top = top;
-            this.right = right;
-            this.bottom = bottom;
+            this.mViewId = viewId;
+            this.mLeft = left;
+            this.mTop = top;
+            this.mRight = right;
+            this.mBottom = bottom;
         }
 
         public ViewPaddingAction(Parcel parcel) {
-            viewId = parcel.readInt();
-            left = parcel.readInt();
-            top = parcel.readInt();
-            right = parcel.readInt();
-            bottom = parcel.readInt();
+            mViewId = parcel.readInt();
+            mLeft = parcel.readInt();
+            mTop = parcel.readInt();
+            mRight = parcel.readInt();
+            mBottom = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeInt(left);
-            dest.writeInt(top);
-            dest.writeInt(right);
-            dest.writeInt(bottom);
+            dest.writeInt(mViewId);
+            dest.writeInt(mLeft);
+            dest.writeInt(mTop);
+            dest.writeInt(mRight);
+            dest.writeInt(mBottom);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
-            target.setPadding(left, top, right, bottom);
+            target.setPadding(mLeft, mTop, mRight, mBottom);
         }
 
         @Override
         public int getActionTag() {
             return VIEW_PADDING_ACTION_TAG;
         }
-
-        @Px int left, top, right, bottom;
     }
 
     /**
      * Helper action to set layout params on a View.
      */
     private static class LayoutParamAction extends Action {
-
         static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT;
         static final int LAYOUT_MARGIN_TOP = MARGIN_TOP;
         static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT;
@@ -3274,7 +3262,7 @@
          */
         LayoutParamAction(@IdRes int viewId, int property, float value,
                 @ComplexDimensionUnit int units) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             this.mProperty = property;
             this.mValueType = VALUE_TYPE_COMPLEX_UNIT;
             this.mValue = TypedValue.createComplexDimension(value, units);
@@ -3289,21 +3277,21 @@
          *   {@link #VALUE_TYPE_RAW}.
          */
         LayoutParamAction(@IdRes int viewId, int property, int value, @ValueType int valueType) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             this.mProperty = property;
             this.mValueType = valueType;
             this.mValue = value;
         }
 
         public LayoutParamAction(Parcel parcel) {
-            viewId = parcel.readInt();
+            mViewId = parcel.readInt();
             mProperty = parcel.readInt();
             mValueType = parcel.readInt();
             mValue = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeInt(mProperty);
             dest.writeInt(mValueType);
             dest.writeInt(mValue);
@@ -3311,7 +3299,7 @@
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) {
                 return;
             }
@@ -3438,55 +3426,53 @@
      * Helper action to add a view tag with RemoteInputs.
      */
     private static class SetRemoteInputsAction extends Action {
+        final Parcelable[] mRemoteInputs;
 
         public SetRemoteInputsAction(@IdRes int viewId, RemoteInput[] remoteInputs) {
-            this.viewId = viewId;
-            this.remoteInputs = remoteInputs;
+            this.mViewId = viewId;
+            this.mRemoteInputs = remoteInputs;
         }
 
         public SetRemoteInputsAction(Parcel parcel) {
-            viewId = parcel.readInt();
-            remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
+            mViewId = parcel.readInt();
+            mRemoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeTypedArray(remoteInputs, flags);
+            dest.writeInt(mViewId);
+            dest.writeTypedArray(mRemoteInputs, flags);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
-            target.setTagInternal(R.id.remote_input_tag, remoteInputs);
+            target.setTagInternal(R.id.remote_input_tag, mRemoteInputs);
         }
 
         @Override
         public int getActionTag() {
             return SET_REMOTE_INPUTS_ACTION_TAG;
         }
-
-        final Parcelable[] remoteInputs;
     }
 
     /**
      * Helper action to override all textViewColors
      */
     private static class OverrideTextColorsAction extends Action {
-
-        private final int textColor;
+        private final int mTextColor;
 
         public OverrideTextColorsAction(int textColor) {
-            this.textColor = textColor;
+            this.mTextColor = textColor;
         }
 
         public OverrideTextColorsAction(Parcel parcel) {
-            textColor = parcel.readInt();
+            mTextColor = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(textColor);
+            dest.writeInt(mTextColor);
         }
 
         @Override
@@ -3499,7 +3485,7 @@
                 if (v instanceof TextView) {
                     TextView textView = (TextView) v;
                     textView.setText(ContrastColorUtil.clearColorSpans(textView.getText()));
-                    textView.setTextColor(textColor);
+                    textView.setTextColor(mTextColor);
                 }
                 if (v instanceof ViewGroup) {
                     ViewGroup viewGroup = (ViewGroup) v;
@@ -3554,34 +3540,33 @@
     }
 
     private static class SetCompoundButtonCheckedAction extends Action {
-
         private final boolean mChecked;
 
         SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             mChecked = checked;
         }
 
         SetCompoundButtonCheckedAction(Parcel in) {
-            viewId = in.readInt();
+            mViewId = in.readInt();
             mChecked = in.readBoolean();
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeBoolean(mChecked);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             if (!(target instanceof CompoundButton)) {
                 Log.w(LOG_TAG, "Cannot set checked to view "
-                        + viewId + " because it is not a CompoundButton");
+                        + mViewId + " because it is not a CompoundButton");
                 return;
             }
 
@@ -3605,33 +3590,32 @@
     }
 
     private static class SetRadioGroupCheckedAction extends Action {
-
         @IdRes private final int mCheckedId;
 
         SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             mCheckedId = checkedId;
         }
 
         SetRadioGroupCheckedAction(Parcel in) {
-            viewId = in.readInt();
+            mViewId = in.readInt();
             mCheckedId = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeInt(mCheckedId);
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             if (!(target instanceof RadioGroup)) {
-                Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
+                Log.w(LOG_TAG, "Cannot check " + mViewId + " because it's not a RadioGroup");
                 return;
             }
 
@@ -3670,35 +3654,34 @@
     }
 
     private static class SetViewOutlinePreferredRadiusAction extends Action {
-
         @ValueType
         private final int mValueType;
         private final int mValue;
 
         SetViewOutlinePreferredRadiusAction(@IdRes int viewId, int value,
                 @ValueType int valueType) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             this.mValueType = valueType;
             this.mValue = value;
         }
 
         SetViewOutlinePreferredRadiusAction(
                 @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
-            this.viewId = viewId;
+            this.mViewId = viewId;
             this.mValueType = VALUE_TYPE_COMPLEX_UNIT;
             this.mValue = TypedValue.createComplexDimension(radius, units);
 
         }
 
         SetViewOutlinePreferredRadiusAction(Parcel in) {
-            viewId = in.readInt();
+            mViewId = in.readInt();
             mValueType = in.readInt();
             mValue = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
+            dest.writeInt(mViewId);
             dest.writeInt(mValueType);
             dest.writeInt(mValue);
         }
@@ -3706,7 +3689,7 @@
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
-            final View target = root.findViewById(viewId);
+            final View target = root.findViewById(mViewId);
             if (target == null) return;
 
             try {
@@ -3748,7 +3731,6 @@
      * {@link #setViewOutlinePreferredRadius(int, float, int)}.
      */
     public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
-
         private final float mRadius;
 
         public RemoteViewOutlineProvider(float radius) {
@@ -3821,7 +3803,8 @@
         return mSizedRemoteViews != null;
     }
 
-    private @Nullable SizeF getIdealSize() {
+    @Nullable
+    private SizeF getIdealSize() {
         return mIdealSize;
     }
 
@@ -4174,7 +4157,7 @@
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
-    };
+    }
 
     /**
      * Returns a deep copy of the RemoteViews object. The RemoteView may not be
@@ -5907,7 +5890,7 @@
          * Callback when the RemoteView has finished inflating,
          * but no actions have been applied yet.
          */
-        default void onViewInflated(View v) {};
+        default void onViewInflated(View v) {}
 
         void onViewApplied(View v);
 
@@ -6288,7 +6271,6 @@
      * @hide
      */
     public class ActionApplyParams {
-
         public InteractionHandler handler;
         public ColorResources colorResources;
         public Executor executor;
@@ -6575,15 +6557,17 @@
     /**
      * Parcelable.Creator that instantiates RemoteViews objects
      */
-    public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
-        public RemoteViews createFromParcel(Parcel parcel) {
-            return new RemoteViews(parcel);
-        }
+    @NonNull
+    public static final Parcelable.Creator<RemoteViews> CREATOR =
+            new Parcelable.Creator<RemoteViews>() {
+                public RemoteViews createFromParcel(Parcel parcel) {
+                    return new RemoteViews(parcel);
+                }
 
-        public RemoteViews[] newArray(int size) {
-            return new RemoteViews[size];
-        }
-    };
+                public RemoteViews[] newArray(int size) {
+                    return new RemoteViews[size];
+                }
+            };
 
     /**
      * A representation of the view hierarchy. Only views which have a valid ID are added
@@ -7284,7 +7268,8 @@
      * Get the ID of the top-level view of the XML layout, if set using
      * {@link RemoteViews#RemoteViews(String, int, int)}.
      */
-    public @IdRes int getViewId() {
+    @IdRes
+    public int getViewId() {
         return mViewId;
     }
 
diff --git a/core/java/android/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
new file mode 100644
index 0000000..b2c977b
--- /dev/null
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -0,0 +1,327 @@
+/*
+ * 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.window;
+
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.PerformanceHintManager;
+import android.os.Trace;
+import android.util.Log;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.function.Supplier;
+
+/**
+ * A helper class to manage performance related hints for a process.  This helper is used for both
+ * long-lived and transient hints.
+ *
+ * @hide
+ */
+public class SystemPerformanceHinter {
+    private static final String TAG = "SystemPerformanceHinter";
+
+    // Change app and SF wakeup times to allow sf more time to composite a frame
+    public static final int HINT_SF_EARLY_WAKEUP = 1 << 0;
+    // Force max refresh rate
+    public static final int HINT_SF_FRAME_RATE = 1 << 1;
+    // Boost CPU & GPU clocks
+    public static final int HINT_ADPF = 1 << 2;
+    // Convenience constant for SF only flags
+    public static final int HINT_SF = HINT_SF_EARLY_WAKEUP | HINT_SF_FRAME_RATE;
+    // Convenience constant for all the flags
+    public static final int HINT_ALL = HINT_SF_EARLY_WAKEUP | HINT_SF_FRAME_RATE | HINT_ADPF;
+
+    // Hints that are applied per-display and require a display root surface
+    private static final int HINT_PER_DISPLAY = HINT_SF_FRAME_RATE;
+    // Hints that are global (not per-display)
+    private static final int HINT_GLOBAL = HINT_SF_EARLY_WAKEUP | HINT_ADPF;
+
+    @IntDef(prefix = {"HINT_"}, value = {
+            HINT_SF_EARLY_WAKEUP,
+            HINT_SF_FRAME_RATE,
+            HINT_ADPF,
+    })
+    private @interface HintFlags {}
+
+    /**
+     * A provider for the root to apply SurfaceControl hints which will be inherited by all children
+     * of that root.
+     * @hide
+     */
+    public interface DisplayRootProvider {
+        /**
+         * @return the SurfaceControl to apply hints for the given displayId.
+         */
+        @Nullable SurfaceControl getRootForDisplay(int displayId);
+    }
+
+    /**
+     * A session where high performance is needed.
+     * @hide
+     */
+    public class HighPerfSession implements AutoCloseable {
+        private final @HintFlags int hintFlags;
+        private final String reason;
+        private final int displayId;
+        private final int traceCookie;
+
+        protected HighPerfSession(@HintFlags int hintFlags, int displayId, @NonNull String reason) {
+            this.hintFlags = hintFlags;
+            this.reason = reason;
+            this.displayId = displayId;
+            this.traceCookie = new Random().nextInt();
+            if (hintFlags != 0) {
+                startSession(this);
+            }
+        }
+
+        /**
+         * Closes this session.
+         */
+        public void close() {
+            if (hintFlags != 0) {
+                endSession(this);
+            }
+        }
+
+        public void finalize() {
+            close();
+        }
+    }
+
+    /**
+     * A no-op implementation of a session.
+     */
+    private class NoOpHighPerfSession extends HighPerfSession {
+        public NoOpHighPerfSession() {
+            super(0 /* hintFlags */, -1 /* displayId */, "");
+        }
+
+        public void close() {
+            // Do nothing
+        }
+    }
+
+    // The active sessions
+    private final ArrayList<HighPerfSession> mActiveSessions = new ArrayList<>();
+    private final SurfaceControl.Transaction mTransaction;
+    private final PerformanceHintManager mPerfHintManager;
+    private @Nullable PerformanceHintManager.Session mAdpfSession;
+    private @Nullable DisplayRootProvider mDisplayRootProvider;
+
+
+    /**
+     * Constructor for the hinter.
+     * @hide
+     */
+    public SystemPerformanceHinter(@NonNull Context context,
+            @Nullable DisplayRootProvider displayRootProvider) {
+        this(context, displayRootProvider, null /* transactionSupplier */);
+    }
+
+    /**
+     * Constructor for the hinter.
+     * @hide
+     */
+    @VisibleForTesting
+    public SystemPerformanceHinter(@NonNull Context context,
+            @Nullable DisplayRootProvider displayRootProvider,
+            @Nullable Supplier<SurfaceControl.Transaction> transactionSupplier) {
+        mDisplayRootProvider = displayRootProvider;
+        mPerfHintManager = context.getSystemService(PerformanceHintManager.class);
+        mTransaction = transactionSupplier != null
+                ? transactionSupplier.get()
+                : new SurfaceControl.Transaction();
+    }
+
+    /**
+     * Sets the current ADPF session, required if you are using HINT_ADPF.  It is the responsibility
+     * of the caller to manage up the ADPF session.
+     * @hide
+     */
+    public void setAdpfSession(PerformanceHintManager.Session adpfSession) {
+        mAdpfSession = adpfSession;
+    }
+
+    /**
+     * Starts a session that requires high performance.
+     * @hide
+     */
+    public HighPerfSession startSession(@HintFlags int hintFlags, int displayId,
+            @NonNull String reason) {
+        if (mDisplayRootProvider == null && (hintFlags & HINT_SF_FRAME_RATE) != 0) {
+            throw new IllegalArgumentException(
+                    "Using SF frame rate hints requires a valid display root provider");
+        }
+        if (mAdpfSession == null && (hintFlags & HINT_ADPF) != 0) {
+            throw new IllegalArgumentException("Using ADPF hints requires an ADPF session");
+        }
+        if ((hintFlags & HINT_PER_DISPLAY) != 0) {
+            if (mDisplayRootProvider.getRootForDisplay(displayId) == null) {
+                // Just log an error and return early if there is no root as there could be races
+                // between when a display root is removed and when a hint session is requested
+                Log.v(TAG, "No display root for displayId=" + displayId);
+                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "PerfHint-NoDisplayRoot: " + displayId);
+                return new NoOpHighPerfSession();
+            }
+        }
+        return new HighPerfSession(hintFlags, displayId, reason);
+    }
+
+    /**
+     * Starts a session that requires high performance.
+     */
+    private void startSession(HighPerfSession session) {
+        int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
+        int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
+                session.displayId);
+        mActiveSessions.add(session);
+        int newGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
+        int newPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
+                session.displayId);
+
+        boolean transactionChanged = false;
+        // Per-display flags
+        if (nowEnabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) {
+            mTransaction.setFrameRateSelectionStrategy(
+                    mDisplayRootProvider.getRootForDisplay(session.displayId),
+                    FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+            transactionChanged = true;
+            Trace.beginAsyncSection("PerfHint-framerate-" + session.reason, session.traceCookie);
+        }
+
+        // Global flags
+        if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
+            mTransaction.setEarlyWakeupStart();
+            transactionChanged = true;
+            Trace.beginAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie);
+        }
+        if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
+            mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_UP);
+            Trace.beginAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
+        }
+        if (transactionChanged) {
+            mTransaction.apply();
+        }
+    }
+
+    /**
+     * Ends a session that requires high performance.
+     */
+    private void endSession(HighPerfSession session) {
+        int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
+        int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
+                session.displayId);
+        mActiveSessions.remove(session);
+        int newGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
+        int newPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
+                session.displayId);
+
+        boolean transactionChanged = false;
+        // Per-display flags
+        if (nowDisabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) {
+            mTransaction.setFrameRateSelectionStrategy(
+                    mDisplayRootProvider.getRootForDisplay(session.displayId),
+                    FRAME_RATE_SELECTION_STRATEGY_SELF);
+            transactionChanged = true;
+            Trace.endAsyncSection("PerfHint-framerate-" + session.reason, session.traceCookie);
+        }
+
+        // Global flags
+        if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
+            mTransaction.setEarlyWakeupEnd();
+            transactionChanged = true;
+            Trace.endAsyncSection("PerfHint-early_wakeup" + session.reason, session.traceCookie);
+        }
+        if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
+            mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
+            Trace.endAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
+        }
+        if (transactionChanged) {
+            mTransaction.apply();
+        }
+    }
+
+    /**
+     * Checks if checkFlags was previously not set and is now set.
+     */
+    private boolean nowEnabled(@HintFlags int oldFlags, @HintFlags int newFlags,
+                               @HintFlags int checkFlags) {
+        return (oldFlags & checkFlags) == 0 && (newFlags & checkFlags) != 0;
+    }
+
+    /**
+     * Checks if checkFlags was previously set and is now not set.
+     */
+    private boolean nowDisabled(@HintFlags int oldFlags, @HintFlags int newFlags,
+                                @HintFlags int checkFlags) {
+        return (oldFlags & checkFlags) != 0 && (newFlags & checkFlags) == 0;
+    }
+
+    /**
+     * @return the combined hint flags for all active sessions, filtered by {@param filterFlags}.
+     */
+    private @HintFlags int calculateActiveHintFlags(@HintFlags int filterFlags) {
+        int flags = 0;
+        for (int i = 0; i < mActiveSessions.size(); i++) {
+            flags |= mActiveSessions.get(i).hintFlags & filterFlags;
+        }
+        return flags;
+    }
+
+    /**
+     * @return the combined hint flags for all active sessions for a given display, filtered by
+     *         {@param filterFlags}.
+     */
+    private @HintFlags int calculateActiveHintFlagsForDisplay(@HintFlags int filterFlags,
+            int displayId) {
+        int flags = 0;
+        for (int i = 0; i < mActiveSessions.size(); i++) {
+            final HighPerfSession session = mActiveSessions.get(i);
+            if (session.displayId == displayId) {
+                flags |= mActiveSessions.get(i).hintFlags & filterFlags;
+            }
+        }
+        return flags;
+    }
+
+    /**
+     * Dumps the existing sessions.
+     */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG + ":");
+        pw.println(innerPrefix + "Active sessions (" + mActiveSessions.size() + "):");
+        for (int i = 0; i < mActiveSessions.size(); i++) {
+            final HighPerfSession s = mActiveSessions.get(i);
+            pw.println(innerPrefix + "  reason=" + s.reason
+                    + " flags=" + s.hintFlags
+                    + " display=" + s.displayId);
+        }
+    }
+}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 3323ae5..c2b5196 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -897,6 +897,23 @@
     }
 
     /**
+     * Moves the PiP activity of a parent task to a pinned root task.
+     * @param parentToken the parent task of the PiP activity
+     * @param bounds the entry bounds
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction movePipActivityToPinnedRootTask(
+            @NonNull WindowContainerToken parentToken, @NonNull Rect bounds) {
+        mHierarchyOps.add(new HierarchyOp
+                .Builder(HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK)
+                .setContainer(parentToken.asBinder())
+                .setBounds(bounds)
+                .build());
+        return this;
+    }
+
+    /**
      * Merges another WCT into this one.
      * @param transfer When true, this will transfer everything from other potentially leaving
      *                 other in an unusable state. When false, other is left alone, but
@@ -1327,6 +1344,7 @@
         public static final int HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS = 15;
         public static final int HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH = 16;
         public static final int HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION = 17;
+        public static final int HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK = 18;
 
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1379,6 +1397,9 @@
         @Nullable
         private ShortcutInfo mShortcutInfo;
 
+        @Nullable
+        private Rect mBounds;
+
         private boolean mAlwaysOnTop;
 
         private boolean mReparentLeafTaskIfRelaunch;
@@ -1482,6 +1503,7 @@
         public HierarchyOp(@NonNull HierarchyOp copy) {
             mType = copy.mType;
             mContainer = copy.mContainer;
+            mBounds = copy.mBounds;
             mReparent = copy.mReparent;
             mInsetsFrameProvider = copy.mInsetsFrameProvider;
             mInsetsFrameOwner = copy.mInsetsFrameOwner;
@@ -1501,6 +1523,7 @@
         protected HierarchyOp(Parcel in) {
             mType = in.readInt();
             mContainer = in.readStrongBinder();
+            mBounds = in.readTypedObject(Rect.CREATOR);
             mReparent = in.readStrongBinder();
             mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR);
             mInsetsFrameOwner = in.readStrongBinder();
@@ -1599,6 +1622,11 @@
             return mShortcutInfo;
         }
 
+        @NonNull
+        public Rect getBounds() {
+            return mBounds;
+        }
+
         /** Gets a string representation of a hierarchy-op type. */
         public static String hopToString(int type) {
             switch (type) {
@@ -1709,6 +1737,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mType);
             dest.writeStrongBinder(mContainer);
+            dest.writeTypedObject(mBounds, flags);
             dest.writeStrongBinder(mReparent);
             dest.writeTypedObject(mInsetsFrameProvider, flags);
             dest.writeStrongBinder(mInsetsFrameOwner);
@@ -1783,6 +1812,9 @@
             @Nullable
             private ShortcutInfo mShortcutInfo;
 
+            @Nullable
+            private Rect mBounds;
+
             private boolean mAlwaysOnTop;
 
             private boolean mReparentLeafTaskIfRelaunch;
@@ -1867,6 +1899,11 @@
                 return this;
             }
 
+            Builder setBounds(@NonNull Rect bounds) {
+                mBounds = bounds;
+                return this;
+            }
+
             HierarchyOp build() {
                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
                 hierarchyOp.mContainer = mContainer;
@@ -1887,6 +1924,7 @@
                 hierarchyOp.mAlwaysOnTop = mAlwaysOnTop;
                 hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation;
                 hierarchyOp.mShortcutInfo = mShortcutInfo;
+                hierarchyOp.mBounds = mBounds;
                 hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
 
                 return hierarchyOp;
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
new file mode 100644
index 0000000..1b98806
--- /dev/null
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.window.flags"
+
+# Project link: https://gantry.corp.google.com/projects/android_platform_window_surfaces/changes
+
+flag {
+    namespace: "window_surfaces"
+    name: "surface_trusted_overlay"
+    description: "Whether to add trusted overlay flag on the SurfaceControl or the InputWindow"
+    is_fixed_read_only: true
+    bug: "292032926"
+}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 1bfb51c..6e836e0 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -837,25 +837,41 @@
 
     @WorkerThread
     private void updateProperties(DeviceConfig.Properties properties) {
-        mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
-                DEFAULT_SAMPLING_INTERVAL);
-        mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
-                DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
-        mTraceThresholdFrameTimeMillis = properties.getInt(
-                SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
-                DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
-        // Never allow the debug overlay to be used on user builds
-        boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean(
-                SETTINGS_DEBUG_OVERLAY_ENABLED_KEY,
-                DEFAULT_DEBUG_OVERLAY_ENABLED);
-        if (debugOverlayEnabled && mDebugOverlay == null) {
-            mDebugOverlay = new InteractionMonitorDebugOverlay(mLock, mDebugBgColor, mDebugYOffset);
-        } else if (!debugOverlayEnabled && mDebugOverlay != null) {
-            mDebugOverlay.dispose();
-            mDebugOverlay = null;
+        for (String property : properties.getKeyset()) {
+            switch (property) {
+                case SETTINGS_SAMPLING_INTERVAL_KEY:
+                    mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL);
+                    break;
+                case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY:
+                    mTraceThresholdMissedFrames =
+                            properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
+                    break;
+                case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY:
+                    mTraceThresholdFrameTimeMillis =
+                            properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
+                    break;
+                case SETTINGS_ENABLED_KEY:
+                    mEnabled = properties.getBoolean(property, DEFAULT_ENABLED);
+                    break;
+                case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY:
+                    // Never allow the debug overlay to be used on user builds
+                    boolean debugOverlayEnabled = Build.IS_DEBUGGABLE
+                            && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED);
+                    if (debugOverlayEnabled && mDebugOverlay == null) {
+                        mDebugOverlay = new InteractionMonitorDebugOverlay(
+                                mLock, mDebugBgColor, mDebugYOffset);
+                    } else if (!debugOverlayEnabled && mDebugOverlay != null) {
+                        mDebugOverlay.dispose();
+                        mDebugOverlay = null;
+                    }
+                    break;
+                default:
+                    if (DEBUG) {
+                        Log.d(TAG, "Got a change event for an unknown property: "
+                                + property + " => " + properties.getString(property, ""));
+                    }
+            }
         }
-        // The memory visibility is powered by the volatile field, mEnabled.
-        mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
     }
 
     @VisibleForTesting
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 06e69f2..fd435d0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -222,6 +222,7 @@
     private boolean isContentRectWithinBounds() {
         mContext.getDisplayNoVerify().getRealSize(mDisplaySize);
         mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+        mScreenRect.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]);
 
         return intersectsClosed(mContentRectOnScreen, mScreenRect)
             && intersectsClosed(mContentRectOnScreen, mViewRectOnScreen);
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6c93680..4732702 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -98,6 +98,7 @@
         // Settings for font scaling
         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 Accessibility accessibility = 2;
 
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index 8b6c901..27f8138 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -60,10 +60,11 @@
                     android:gravity="center"
                     android:textAppearance="@style/AutofillSaveUiTitle">
                 </TextView>
-                <LinearLayout
+                <FrameLayout
                     android:id="@+id/autofill_save_custom_subtitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:minHeight="0dp"
                     android:visibility="gone"/>
 
             </LinearLayout>
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
index 63fe471..2c6e0a7 100644
--- a/core/res/res/layout/notification_expand_button.xml
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -52,7 +52,7 @@
             android:id="@+id/expand_button_icon"
             android:layout_width="@dimen/notification_expand_button_pill_height"
             android:layout_height="@dimen/notification_expand_button_pill_height"
-            android:padding="2dp"
+            android:padding="@dimen/notification_expand_button_icon_padding"
             android:scaleType="fitCenter"
             android:importantForAccessibility="no"
             />
diff --git a/core/res/res/values-watch/dimens_material.xml b/core/res/res/values-watch/dimens_material.xml
index 2ab2d91..8becb08 100644
--- a/core/res/res/values-watch/dimens_material.xml
+++ b/core/res/res/values-watch/dimens_material.xml
@@ -47,11 +47,12 @@
     <dimen name="progress_bar_height">24dp</dimen>
 
     <!-- Progress bar message dimens -->
-    <dimen name="message_progress_dialog_text_size">18sp</dimen>
+    <dimen name="message_progress_dialog_text_size">14sp</dimen>
     <dimen name="message_progress_dialog_bottom_padding">80px</dimen>
     <dimen name="message_progress_dialog_top_padding">0dp</dimen>
     <dimen name="message_progress_dialog_start_padding">0dp</dimen>
     <dimen name="message_progress_dialog_end_padding">0dp</dimen>
+    <item name="message_progress_dialog_letter_spacing" format="float" type="dimen">0.021</item>
 
     <!-- fallback for screen percentage widths -->
     <dimen name="screen_percentage_05">0dp</dimen>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index 8698e86..f3e412d 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -100,7 +100,9 @@
 
     <style name="ProgressDialogMessage">
         <item name="android:textAlignment">center</item>
-        <item name="textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:fontFamily">google-sans-text</item>
+        <item name="android:letterSpacing">@dimen/message_progress_dialog_letter_spacing</item>
+        <item name="textColor">?attr/textColorPrimary</item>
         <item name="textSize">@dimen/message_progress_dialog_text_size</item>
         <item name="paddingBottom">@dimen/message_progress_dialog_bottom_padding</item>
         <item name="paddingEnd">@dimen/message_progress_dialog_end_padding</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3c296de..7ef81ab 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1151,6 +1151,14 @@
     <!-- Allows activities to be launched on a long press on power during device setup. -->
     <bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool>
 
+    <!-- Control the behavior when the user short presses the settings button.
+            0 - Nothing
+            1 - Launch notification panel
+         This needs to match the constants in
+         com/android/server/policy/PhoneWindowManager.java
+    -->
+    <integer name="config_shortPressOnSettingsBehavior">0</integer>
+
     <!-- Control the behavior when the user short presses the power button.
             0 - Nothing
             1 - Go to sleep (doze)
@@ -5394,6 +5402,7 @@
         <item>1,1,1.0,.4,1</item>
         <item>1,1,1.0,.15,15</item>
         <item>0,0,0.7,0,1</item>
+        <item>0,0,0.83333,0,1</item>
     </string-array>
 
     <!-- The integer index of the selected option in config_udfps_touch_detection_options -->
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
index 98a5ff9..bc9ca3d 100644
--- a/core/res/res/values/config_device_idle.xml
+++ b/core/res/res/values/config_device_idle.xml
@@ -42,6 +42,15 @@
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR -->
     <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item>
 
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_INCREASE_LINEARLY -->
+    <bool name="device_idle_light_idle_increase_linearly">false</bool>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS -->
+    <integer name="device_idle_light_idle_linear_increase_factor_ms">300000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS -->
+    <integer name="device_idle_light_idle_flex_linear_increase_factor_ms">60000</integer>
+
     <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT -->
     <integer name="device_idle_light_max_idle_to_ms">900000</integer>
 
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 71d696e..3ba150b 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -73,7 +73,7 @@
          CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
          If 0, the device always switch to the higher score SIM.
          If < 0, the network type and signal strength based auto switch is disabled. -->
-    <integer name="auto_data_switch_score_tolerance">-1</integer>
+    <integer name="auto_data_switch_score_tolerance">4000</integer>
     <java-symbol type="integer" name="auto_data_switch_score_tolerance" />
 
     <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
@@ -219,4 +219,8 @@
     <bool name="telephony_analytics_switch">true</bool>
     <java-symbol type="bool" name="telephony_analytics_switch" />
 
+    <!-- Whether to enable modem on boot if behavior is not defined -->
+    <bool name="config_enable_cellular_on_boot_default">true</bool>
+    <java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
+
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 49295fd..96c4bf4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -349,6 +349,9 @@
     <!-- the height of the expand button pill -->
     <dimen name="notification_expand_button_pill_height">24dp</dimen>
 
+    <!-- the padding of the expand icon in the notification header -->
+    <dimen name="notification_expand_button_icon_padding">2dp</dimen>
+
     <!-- Vertical margin for the headerless notification content, when content has 1 line -->
     <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
     <dimen name="notification_headerless_margin_oneline">16dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 193f3ad..643f4b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1816,6 +1816,7 @@
   <java-symbol type="integer" name="config_lidNavigationAccessibility" />
   <java-symbol type="integer" name="config_lidOpenRotation" />
   <java-symbol type="integer" name="config_longPressOnHomeBehavior" />
+  <java-symbol type="integer" name="config_shortPressOnSettingsBehavior" />
   <java-symbol type="layout" name="global_actions" />
   <java-symbol type="layout" name="global_actions_item" />
   <java-symbol type="layout" name="global_actions_silent_mode" />
@@ -4537,7 +4538,10 @@
   <java-symbol type="integer" name="device_idle_light_idle_to_init_flex_ms" />
   <java-symbol type="integer" name="device_idle_light_idle_to_max_flex_ms" />
   <java-symbol type="integer" name="device_idle_light_idle_factor" />
+  <java-symbol type="bool" name="device_idle_light_idle_increase_linearly" />
   <java-symbol type="integer" name="device_idle_light_max_idle_to_ms" />
+  <java-symbol type="integer" name="device_idle_light_idle_linear_increase_factor_ms" />
+  <java-symbol type="integer" name="device_idle_light_idle_flex_linear_increase_factor_ms" />
   <java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" />
   <java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" />
   <java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" />
diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp
index 85d54e0..054d10c 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -38,7 +38,7 @@
     static_libs: [
         "services.core",
         "androidx.test.rules",
-        "truth-prebuilt",
+        "truth",
         "testng",
         "mockito-target-extended",
     ],
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index a195228..824f591 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -16,20 +16,20 @@
 
 package com.android.server.broadcastradio.aidl;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.after;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.compat.CompatChanges;
 import android.graphics.Bitmap;
@@ -81,8 +81,8 @@
 
     private static final int USER_ID_1 = 11;
     private static final int USER_ID_2 = 12;
-    private static final VerificationWithTimeout CALLBACK_TIMEOUT =
-            timeout(/* millis= */ 200);
+    private static final int CALLBACK_TIMEOUT_MS = 200;
+    private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS);
     private static final int SIGNAL_QUALITY = 90;
     private static final long AM_FM_FREQUENCY_SPACING = 500;
     private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100};
@@ -166,12 +166,12 @@
 
     @Before
     public void setup() throws Exception {
-        when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
         doReturn(true).when(() -> CompatChanges.isChangeEnabled(
                 eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+        doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
+        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
         doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
         doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
-        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
 
         mRadioModule = new RadioModule(mBroadcastRadioMock,
                 AidlTestUtils.makeDefaultModuleProperties());
@@ -222,7 +222,7 @@
             return Result.OK;
         }).when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean());
 
-        when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
+        doReturn(null).when(mBroadcastRadioMock).getImage(anyInt());
 
         doAnswer(invocation -> {
             int configFlag = (int) invocation.getArguments()[0];
@@ -275,7 +275,7 @@
 
         mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onConfigurationChanged(FM_BAND_CONFIG);
     }
 
@@ -446,26 +446,11 @@
 
         mTunerSessions[0].tune(initialSel);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(tuneInfo);
     }
 
     @Test
-    public void tune_forSystemUser() throws Exception {
-        when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
-        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
-        ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
-        RadioManager.ProgramInfo tuneInfo =
-                AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
-        openAidlClients(/* numClients= */ 1);
-
-        mTunerSessions[0].tune(initialSel);
-
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
-    }
-
-    @Test
     public void tune_withUnknownErrorFromHal_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
@@ -525,7 +510,7 @@
 
         mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(any());
     }
 
@@ -604,7 +589,7 @@
 
         mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(seekUpInfo);
     }
 
@@ -638,6 +623,7 @@
         openAidlClients(/* numClients= */ 1);
         ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
         mTunerSessions[0].tune(initialSel);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any());
         doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
 
         mTunerSessions[0].cancel();
@@ -686,8 +672,8 @@
     public void getImage_whenHalThrowsException_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         String exceptionMessage = "HAL service died.";
-        when(mBroadcastRadioMock.getImage(anyInt()))
-                .thenThrow(new RemoteException(exceptionMessage));
+        doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock)
+                .getImage(anyInt());
 
         RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
             mTunerSessions[0].getImage(/* id= */ 1);
@@ -713,7 +699,8 @@
 
         mTunerSessions[0].startBackgroundScan();
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete();
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+                .onBackgroundScanComplete();
     }
 
     @Test
@@ -905,7 +892,8 @@
         mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
                 /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+                .onProgramListUpdated(any());
     }
 
     @Test
@@ -1160,8 +1148,8 @@
         Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
                 "mockParam2", "mockValue2");
         String exceptionMessage = "HAL service died.";
-        when(mBroadcastRadioMock.setParameters(any()))
-                .thenThrow(new RemoteException(exceptionMessage));
+        doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock)
+                .setParameters(any());
 
         RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
             mTunerSessions[0].setParameters(parametersSet);
@@ -1186,8 +1174,8 @@
         openAidlClients(/* numClients= */ 1);
         List<String> parameterKeys = List.of("mockKey1", "mockKey2");
         String exceptionMessage = "HAL service died.";
-        when(mBroadcastRadioMock.getParameters(any()))
-                .thenThrow(new RemoteException(exceptionMessage));
+        doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock)
+                .getParameters(any());
 
         RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
             mTunerSessions[0].getParameters(parameterKeys);
@@ -1198,7 +1186,7 @@
     }
 
     @Test
-    public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+    public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
             throws Exception {
         openAidlClients(1);
         doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
@@ -1206,7 +1194,7 @@
         mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo(
                 AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY));
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(any());
     }
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index fac9eaa..3b9d7ba 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -16,22 +16,22 @@
 
 package com.android.server.broadcastradio.hal2;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.after;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 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;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.graphics.Bitmap;
 import android.hardware.broadcastradio.V2_0.Constants;
@@ -78,8 +78,8 @@
 
     private static final int USER_ID_1 = 11;
     private static final int USER_ID_2 = 12;
-    private static final VerificationWithTimeout CALLBACK_TIMEOUT =
-            timeout(/* millis= */ 200);
+    private static final int CALLBACK_TIMEOUT_MS = 200;
+    private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS);
     private static final int SIGNAL_QUALITY = 1;
     private static final long AM_FM_FREQUENCY_SPACING = 500;
     private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100};
@@ -113,7 +113,7 @@
 
     @Before
     public void setup() throws Exception {
-        when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+        doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
         doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
         doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
         doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
@@ -170,7 +170,7 @@
             return Result.OK;
         }).when(mHalTunerSessionMock).scan(anyBoolean(), anyBoolean());
 
-        when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
+        doReturn(new ArrayList<Byte>(0)).when(mBroadcastRadioMock).getImage(anyInt());
 
         doAnswer(invocation -> {
             int configFlag = (int) invocation.getArguments()[0];
@@ -227,7 +227,7 @@
 
         mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onConfigurationChanged(FM_BAND_CONFIG);
     }
 
@@ -379,7 +379,7 @@
 
         mTunerSessions[0].tune(initialSel);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(tuneInfo);
     }
 
@@ -398,20 +398,6 @@
     }
 
     @Test
-    public void tune_forSystemUser() throws Exception {
-        when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
-        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
-        ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
-        RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
-        openAidlClients(/* numClients= */ 1);
-
-        mTunerSessions[0].tune(initialSel);
-
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
-    }
-
-    @Test
     public void step_withDirectionUp() throws Exception {
         long initFreq = AM_FM_FREQUENCY_LIST[1];
         ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -455,7 +441,7 @@
 
         mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(any());
     }
 
@@ -533,7 +519,7 @@
 
         mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(seekUpInfo);
     }
 
@@ -563,18 +549,6 @@
     }
 
     @Test
-    public void cancel_forNonCurrentUser() throws Exception {
-        openAidlClients(/* numClients= */ 1);
-        ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
-        mTunerSessions[0].tune(initialSel);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
-
-        mTunerSessions[0].cancel();
-
-        verify(mHalTunerSessionMock, never()).cancel();
-    }
-
-    @Test
     public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
         openAidlClients(/* numClients= */ 1);
         ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
@@ -627,8 +601,7 @@
     public void getImage_whenHalThrowsException_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         String exceptionMessage = "HAL service died.";
-        when(mBroadcastRadioMock.getImage(anyInt()))
-                .thenThrow(new RemoteException(exceptionMessage));
+        doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock).getImage(anyInt());
 
         RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
             mTunerSessions[0].getImage(/* id= */ 1);
@@ -654,7 +627,8 @@
 
         mTunerSessions[0].startBackgroundScan();
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete();
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+                .onBackgroundScanComplete();
     }
 
     @Test
@@ -845,8 +819,8 @@
         Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
                 "mockParam2", "mockValue2");
         String exceptionMessage = "HAL service died.";
-        when(mHalTunerSessionMock.setParameters(any()))
-                .thenThrow(new RemoteException(exceptionMessage));
+        doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock)
+                .setParameters(any());
 
         RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
             mTunerSessions[0].setParameters(parametersSet);
@@ -871,8 +845,8 @@
         openAidlClients(/* numClients= */ 1);
         List<String> parameterKeys = List.of("mockKey1", "mockKey2");
         String exceptionMessage = "HAL service died.";
-        when(mHalTunerSessionMock.getParameters(any()))
-                .thenThrow(new RemoteException(exceptionMessage));
+        doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock)
+                .getParameters(any());
 
         RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
             mTunerSessions[0].getParameters(parameterKeys);
@@ -883,7 +857,7 @@
     }
 
     @Test
-    public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+    public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
             throws Exception {
         openAidlClients(1);
         doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
@@ -891,7 +865,7 @@
         mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo(
                 TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY));
 
-        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
                 .onCurrentProgramInfoChanged(any());
     }
 
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index 8c5d6d5..0e3bc65 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -30,7 +30,7 @@
         "frameworks-base-testutils",
         "junit",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: ["android.test.runner"],
     platform_apis: true,
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
index 6f2366e..b631df1 100644
--- a/core/tests/PackageInstallerSessions/Android.bp
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -35,7 +35,7 @@
         "frameworks-base-testutils",
         "platform-test-annotations",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
 
     libs: [
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
index 5260835..1fb5f2c 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
@@ -18,7 +18,7 @@
         "platform-test-annotations",
         "platformprotosnano",
         "statsdprotolite",
-        "truth-prebuilt",
+        "truth",
     ],
 
     libs: ["android.test.runner"],
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index 2b34ee2..7c1ac48 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -32,7 +32,7 @@
     static_libs: [
         "androidx.test.rules",
         "androidx.test.uiautomator_uiautomator",
-        "truth-prebuilt",
+        "truth",
     ],
     test_suites: ["general-tests"],
     sdk_version: "test_current",
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 9cde296..81dab08 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -44,19 +44,21 @@
         "frameworks-core-util-lib",
         "mockwebserver",
         "guava",
+        "android.view.accessibility.flags-aconfig-java",
         "androidx.core_core",
         "androidx.core_core-ktx",
         "androidx.test.espresso.core",
         "androidx.test.ext.junit",
         "androidx.test.runner",
         "androidx.test.rules",
+        "flag-junit",
         "junit-params",
         "kotlin-test",
         "mockito-target-minus-junit4",
         "androidx.test.uiautomator_uiautomator",
         "platform-test-annotations",
         "platform-compat-test-rules",
-        "truth-prebuilt",
+        "truth",
         "print-test-util-lib",
         "testng",
         "servicestests-utils",
@@ -147,7 +149,7 @@
         "androidx.test.runner",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
 
     libs: [
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 2afbb47..6a9fc04 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -38,12 +39,18 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.Instrumentation;
+import android.app.UiModeManager;
 import android.content.Context;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
+import android.os.SystemProperties;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.util.Log;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Type;
 
@@ -52,9 +59,13 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -71,6 +82,10 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ViewRootImplTest {
+    private static final String TAG = "ViewRootImplTest";
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private ViewRootImpl mViewRootImpl;
     private volatile boolean mKeyReceived = false;
@@ -101,6 +116,18 @@
                 mViewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify()));
     }
 
+    @After
+    public void teardown() {
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            Settings.Secure.resetToDefaults(sContext.getContentResolver(), TAG);
+
+            var uiModeManager = sContext.getSystemService(UiModeManager.class);
+            uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
+
+            setForceDarkSysProp(false);
+        });
+    }
+
     @Test
     public void adjustLayoutParamsForCompatibility_layoutFullscreen() {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
@@ -400,6 +427,100 @@
         assertThat(result).isFalse();
     }
 
+    @Test
+    public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
+        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            Settings.Secure.putInt(
+                    sContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+                    /* value= */ 0
+            );
+            var uiModeManager = sContext.getSystemService(UiModeManager.class);
+            uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
+        });
+
+        sInstrumentation.runOnMainSync(() ->
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
+        );
+
+        assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
+    }
+
+    @Test
+    public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() {
+        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            Settings.Secure.putInt(
+                    sContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+                    /* value= */ 1
+            );
+            var uiModeManager = sContext.getSystemService(UiModeManager.class);
+            uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
+        });
+
+        sInstrumentation.runOnMainSync(() ->
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
+        );
+
+        assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
+    }
+
+    @Test
+    public void forceInvertOffForceDarkOff_forceDarkModeDisabled() {
+        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            Settings.Secure.putInt(
+                    sContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+                    /* value= */ 0
+            );
+
+            // TODO(b/297556388): figure out how to set this without getting blocked by SELinux
+            assumeTrue(setForceDarkSysProp(true));
+        });
+
+        sInstrumentation.runOnMainSync(() ->
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
+        );
+
+        assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
+    }
+
+    @Test
+    public void forceInvertOffForceDarkOn_forceDarkModeEnabled() {
+        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            Settings.Secure.putInt(
+                    sContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+                    /* value= */ 0
+            );
+
+            assumeTrue(setForceDarkSysProp(true));
+        });
+
+        sInstrumentation.runOnMainSync(() ->
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
+        );
+
+        assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
+    }
+
+    private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
+        try {
+            SystemProperties.set(
+                    ThreadedRenderer.DEBUG_FORCE_DARK,
+                    Boolean.toString(isForceDarkEnabled)
+            );
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to set force_dark sysprop", e);
+            return false;
+        }
+    }
+
     class KeyView extends View {
         KeyView(Context context) {
             super(context);
diff --git a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
new file mode 100644
index 0000000..25f5819
--- /dev/null
+++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
@@ -0,0 +1,389 @@
+/*
+ * 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.window;
+
+import static android.os.PerformanceHintManager.Session.CPU_LOAD_RESET;
+import static android.os.PerformanceHintManager.Session.CPU_LOAD_UP;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
+import static android.window.SystemPerformanceHinter.HINT_ADPF;
+import static android.window.SystemPerformanceHinter.HINT_ALL;
+import static android.window.SystemPerformanceHinter.HINT_SF_EARLY_WAKEUP;
+import static android.window.SystemPerformanceHinter.HINT_SF_FRAME_RATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.os.PerformanceHintManager;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+
+/**
+ * Class for testing {@link android.window.SystemPerformanceHinter}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:android.window.SystemPerformanceHinterTests
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SystemPerformanceHinterTests {
+
+    private static final int DEFAULT_DISPLAY_ID = android.view.Display.DEFAULT_DISPLAY;
+    private static final int SECONDARY_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1;
+    private static final int NO_ROOT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2;
+    private static final String TEST_REASON = "test";
+    private static final String TEST_OTHER_REASON = "test_other";
+
+    private SystemPerformanceHinter mHinter;
+    private SystemPerformanceHinterTests.RootProvider mRootProvider;
+
+    @Mock
+    private PerformanceHintManager.Session mAdpfSession;
+
+    @Mock
+    private SurfaceControl.Transaction mTransaction;
+    private SurfaceControl mDefaultDisplayRoot;
+    private SurfaceControl mSecondaryDisplayRoot;
+
+
+    @Before
+    public void setUpOnce() {
+        MockitoAnnotations.initMocks(this);
+
+        mDefaultDisplayRoot = new SurfaceControl();
+        mSecondaryDisplayRoot = new SurfaceControl();
+        mRootProvider = new SystemPerformanceHinterTests.RootProvider();
+        mRootProvider.put(DEFAULT_DISPLAY_ID, mDefaultDisplayRoot);
+        mRootProvider.put(SECONDARY_DISPLAY_ID, mSecondaryDisplayRoot);
+
+        mHinter = new SystemPerformanceHinter(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                mRootProvider,
+                () -> mTransaction);
+    }
+
+    @Test
+    public void testADPFHintWithoutADPFSession_expectThrows() {
+        assertThrows("Expected exception without ADPF session",
+                IllegalArgumentException.class, () -> {
+                    mHinter.startSession(HINT_ADPF, DEFAULT_DISPLAY_ID, TEST_REASON);
+                });
+    }
+
+    @Test
+    public void testSFVRRHintWithoutDisplayRootProvider_expectThrows() {
+        assertThrows("Expected exception without display root",
+                IllegalArgumentException.class, () -> {
+                    SystemPerformanceHinter hinter = new SystemPerformanceHinter(
+                            InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                            null /* displayRootProvider */,
+                            () -> mTransaction);
+                    hinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+                });
+    }
+
+    @Test
+    public void testGetDefaultDisplayRoot() {
+        mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+        assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId);
+        assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot);
+    }
+
+    @Test
+    public void testGetSecondaryDisplayRoot() {
+        mHinter.startSession(HINT_SF_FRAME_RATE, SECONDARY_DISPLAY_ID, TEST_REASON);
+        assertEquals(SECONDARY_DISPLAY_ID, mRootProvider.lastRequestedDisplayId);
+        assertEquals(mSecondaryDisplayRoot, mRootProvider.lastReturnedRoot);
+    }
+
+    @Test
+    public void testOnlyCacheDisplayRoots() {
+        mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+        mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+        mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+        assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId);
+        assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot);
+    }
+
+    @Test
+    public void testVRRHint() {
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+
+        // Expect it to get a display root
+        assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId);
+        assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot);
+
+        // Verify we call SF
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+        verify(mTransaction).apply();
+    }
+
+    @Test
+    public void testVRRHintCloseSession() {
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON);
+        reset(mTransaction);
+        session.close();
+
+        // Verify we call SF
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+        verify(mTransaction).apply();
+    }
+
+    @Test
+    public void testEarlyWakeupHint() {
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_SF_EARLY_WAKEUP, DEFAULT_DISPLAY_ID, TEST_REASON);
+
+        // Expect that this hint does not require a display root
+        assertEquals(0, mRootProvider.getCount);
+
+        // Verify we call SF
+        verify(mTransaction).setEarlyWakeupStart();
+        verify(mTransaction).apply();
+    }
+
+    @Test
+    public void testEarlyWakeupHintCloseSession() {
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_SF_EARLY_WAKEUP, DEFAULT_DISPLAY_ID, TEST_REASON);
+        reset(mTransaction);
+        session.close();
+
+        // Verify we call SF
+        verify(mTransaction).setEarlyWakeupEnd();
+        verify(mTransaction).apply();
+    }
+
+    @Test
+    public void testADPFHint() {
+        mHinter.setAdpfSession(mAdpfSession);
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_ADPF, DEFAULT_DISPLAY_ID, TEST_REASON);
+
+        // Expect that this hint does not require a display root
+        assertEquals(0, mRootProvider.getCount);
+
+        // Verify we call the perf manager
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
+    }
+
+    @Test
+    public void testADPFHintCloseSession() {
+        mHinter.setAdpfSession(mAdpfSession);
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_ADPF, DEFAULT_DISPLAY_ID, TEST_REASON);
+        reset(mTransaction);
+        session.close();
+
+        // Verify we call the perf manager
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
+    }
+
+    @Test
+    public void testAllHints() {
+        mHinter.setAdpfSession(mAdpfSession);
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON);
+
+        // Expect it to get a display root
+        assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId);
+        assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot);
+
+        // Verify we call SF and perf manager
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+        verify(mTransaction).setEarlyWakeupStart();
+        verify(mTransaction).apply();
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
+    }
+
+    @Test
+    public void testAllHintsCloseSession() {
+        mHinter.setAdpfSession(mAdpfSession);
+        final SystemPerformanceHinter.HighPerfSession session =
+                mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON);
+        reset(mTransaction);
+        session.close();
+
+        // Verify we call SF and perf manager to clean up
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+        verify(mTransaction).setEarlyWakeupEnd();
+        verify(mTransaction).apply();
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
+    }
+
+    @Test
+    public void testAutocloseable() {
+        mHinter.setAdpfSession(mAdpfSession);
+        try (final SystemPerformanceHinter.HighPerfSession session =
+                     mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON)) {
+            reset(mTransaction);
+            reset(mAdpfSession);
+        } finally {
+            // Verify we call SF and perf manager to clean up
+            verify(mTransaction).setFrameRateSelectionStrategy(
+                    eq(mDefaultDisplayRoot),
+                    eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+            verify(mTransaction).setEarlyWakeupEnd();
+            verify(mTransaction).apply();
+            verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
+        }
+    }
+
+    @Test
+    public void testOverlappingHintsOnSameDisplay() {
+        mHinter.setAdpfSession(mAdpfSession);
+        final SystemPerformanceHinter.HighPerfSession session1 =
+                mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON);
+        // Verify we call SF and perf manager
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+        verify(mTransaction).setEarlyWakeupStart();
+        verify(mTransaction).apply();
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
+        reset(mTransaction);
+        reset(mAdpfSession);
+
+        final SystemPerformanceHinter.HighPerfSession session2 =
+                mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_OTHER_REASON);
+        // Verify we never call SF and perf manager since session1 is already running
+        verify(mTransaction, never()).setFrameRateSelectionStrategy(any(), anyInt());
+        verify(mTransaction, never()).setEarlyWakeupEnd();
+        verify(mTransaction, never()).apply();
+        verify(mAdpfSession, never()).sendHint(anyInt());
+
+        session2.close();
+        // Verify we have not cleaned up because session1 is still running
+        verify(mTransaction, never()).setFrameRateSelectionStrategy(any(), anyInt());
+        verify(mTransaction, never()).setEarlyWakeupEnd();
+        verify(mTransaction, never()).apply();
+        verify(mAdpfSession, never()).sendHint(anyInt());
+
+        session1.close();
+        // Verify we call SF and perf manager to clean up
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+        verify(mTransaction).setEarlyWakeupEnd();
+        verify(mTransaction).apply();
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
+    }
+
+    @Test
+    public void testOverlappingHintsOnDifferentDisplays() {
+        mHinter.setAdpfSession(mAdpfSession);
+        final SystemPerformanceHinter.HighPerfSession session1 =
+                mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON);
+
+        // Verify we call SF and perf manager
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+        verify(mTransaction).setEarlyWakeupStart();
+        verify(mTransaction).apply();
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
+        reset(mTransaction);
+        reset(mAdpfSession);
+
+        // Create a new session and ensure only per-display flags are updated and not global flags
+        final SystemPerformanceHinter.HighPerfSession session2 =
+                mHinter.startSession(HINT_ALL, SECONDARY_DISPLAY_ID, TEST_REASON);
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mSecondaryDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+        verify(mTransaction, never()).setEarlyWakeupStart();
+        verify(mTransaction).apply();
+        verify(mAdpfSession, never()).sendHint(anyInt());
+        reset(mTransaction);
+        reset(mAdpfSession);
+
+        // Close the primary display session and ensure it doesn't affect secondary display flags
+        // or any global flags still requested by the secondary display session
+        session1.close();
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+        verify(mTransaction, never()).setFrameRateSelectionStrategy(
+                eq(mSecondaryDisplayRoot),
+                anyInt());
+        verify(mTransaction, never()).setEarlyWakeupEnd();
+        verify(mTransaction).apply();
+        verify(mAdpfSession, never()).sendHint(anyInt());
+        reset(mTransaction);
+        reset(mAdpfSession);
+
+        // Close all sessions, ensure it cleans up all the flags
+        session2.close();
+        verify(mTransaction, never()).setFrameRateSelectionStrategy(
+                eq(mDefaultDisplayRoot),
+                anyInt());
+        verify(mTransaction).setFrameRateSelectionStrategy(
+                eq(mSecondaryDisplayRoot),
+                eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+        verify(mTransaction).setEarlyWakeupEnd();
+        verify(mTransaction).apply();
+        verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
+    }
+
+    private class RootProvider implements SystemPerformanceHinter.DisplayRootProvider {
+        private HashMap<Integer, SurfaceControl> mRoots = new HashMap<>();
+        public int getCount;
+        public int lastRequestedDisplayId = -1;
+        public SurfaceControl lastReturnedRoot;
+
+        void put(int displayId, SurfaceControl root) {
+            mRoots.put(displayId, root);
+        }
+
+        @NonNull
+        @Override
+        public SurfaceControl getRootForDisplay(int displayId) {
+            getCount++;
+            lastRequestedDisplayId = displayId;
+            lastReturnedRoot = mRoots.get(displayId);
+            return lastReturnedRoot;
+        }
+    }
+}
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 3d04937..5f6eaf9 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -30,7 +30,7 @@
         "frameworks-base-testutils",
         "guava-android-testlib",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: ["android.test.runner"],
     platform_apis: true,
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index fde7c08..2d778b1 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -38,7 +38,7 @@
         "androidx.test.ext.junit",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
         "testables",
     ],
 
diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp
index c74600b..f81be49 100644
--- a/core/tests/nfctests/Android.bp
+++ b/core/tests/nfctests/Android.bp
@@ -27,7 +27,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.runner",
diff --git a/core/tests/overlaytests/device_self_targeting/Android.bp b/core/tests/overlaytests/device_self_targeting/Android.bp
index 063c569..931eac5 100644
--- a/core/tests/overlaytests/device_self_targeting/Android.bp
+++ b/core/tests/overlaytests/device_self_targeting/Android.bp
@@ -30,7 +30,7 @@
         "androidx.test.runner",
         "androidx.test.ext.junit",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
 
     optimize: {
diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp
index 7b5d7df..b08850e 100644
--- a/core/tests/packagemonitortests/Android.bp
+++ b/core/tests/packagemonitortests/Android.bp
@@ -32,7 +32,7 @@
         "compatibility-device-util-axt",
         "frameworks-base-testutils",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: ["android.test.runner"],
     platform_apis: true,
diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp
index bc3dd81..4e24cd5 100644
--- a/core/tests/privacytests/Android.bp
+++ b/core/tests/privacytests/Android.bp
@@ -14,7 +14,7 @@
         "junit",
         "rappor-tests",
         "androidx.test.rules",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: ["android.test.runner"],
     platform_apis: true,
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 3798da5..580e73c 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -33,7 +33,7 @@
         "frameworks-base-testutils",
         "mockito-target-minus-junit4",
         "androidx.test.ext.junit",
-        "truth-prebuilt",
+        "truth",
         "servicestests-utils",
     ],
 
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
index 829409a..09608e9 100644
--- a/core/tests/vibrator/Android.bp
+++ b/core/tests/vibrator/Android.bp
@@ -18,7 +18,7 @@
         "androidx.test.runner",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
         "testng",
     ],
 
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index ad08026..c1d2235 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -41,7 +41,7 @@
     java_resource_dirs: ["tests/res"],
     java_resources: [":error_prone_android_framework_testdata"],
     static_libs: [
-        "truth-prebuilt",
+        "truth",
         "kxml2-2.3.0",
         "compile-testing-prebuilt",
         "error_prone_android_framework_lib",
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index c55a781..11278e8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -43,6 +43,7 @@
 import java.security.interfaces.RSAPublicKey;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 import javax.crypto.Mac;
 import javax.crypto.SecretKey;
 
@@ -181,6 +182,8 @@
             spi = ((Mac) cryptoPrimitive).getCurrentSpi();
         } else if (cryptoPrimitive instanceof Cipher) {
             spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
+        } else if (cryptoPrimitive instanceof KeyAgreement) {
+            spi = ((KeyAgreement) cryptoPrimitive).getCurrentSpi();
         } else {
             throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
                     + ". Supported: Signature, Mac, Cipher");
diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
index b6e743a..ed2ff2d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -37,7 +37,7 @@
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "mockito-target-extended-minus-junit4",
-        "truth-prebuilt",
+        "truth",
         "testables",
         "platform-test-annotations",
     ],
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
new file mode 100644
index 0000000..ee9f070
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="@dimen/desktop_mode_handle_menu_width"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        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"
+        android:layout_marginStart="1dp"
+        android:elevation="1dp"
+        android:orientation="horizontal"
+        android:background="@drawable/desktop_mode_decor_handle_menu_background"
+        android:gravity="center_vertical">
+
+        <ImageView
+            android:id="@+id/application_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_marginStart="14dp"
+            android:layout_marginEnd="14dp"
+            android:contentDescription="@string/app_icon_text"/>
+
+        <TextView
+            android:id="@+id/application_name"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            tools:text="Gmail"
+            android:textColor="?androidprv:attr/materialColorOnSurface"
+            android:textSize="14sp"
+            android:textFontWeight="500"
+            android:lineHeight="20dp"
+            android:textStyle="normal"
+            android:layout_weight="1"/>
+
+        <ImageButton
+            android:id="@+id/collapse_menu_button"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:padding="4dp"
+            android:layout_marginEnd="14dp"
+            android:layout_marginStart="14dp"
+            android:contentDescription="@string/collapse_menu_text"
+            android:src="@drawable/ic_baseline_expand_more_24"
+            android:rotation="180"
+            android:tint="?androidprv:attr/materialColorOnSurface"
+            android:background="?android:selectableItemBackgroundBorderless"/>
+    </LinearLayout>
+
+    <LinearLayout
+        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"
+        android:layout_marginStart="1dp"
+        android:orientation="horizontal"
+        android:elevation="1dp"
+        android:background="@drawable/desktop_mode_decor_handle_menu_background"
+        android:gravity="center_vertical">
+
+        <ImageButton
+            android:id="@+id/fullscreen_button"
+            android:layout_marginEnd="4dp"
+            android:contentDescription="@string/fullscreen_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
+            android:tint="?androidprv:attr/materialColorOnSurface"
+            android:layout_weight="1"
+            style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+        <ImageButton
+            android:id="@+id/split_screen_button"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp"
+            android:contentDescription="@string/split_screen_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
+            android:tint="?androidprv:attr/materialColorOnSurface"
+            android:layout_weight="1"
+            style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+        <ImageButton
+            android:id="@+id/floating_button"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp"
+            android:contentDescription="@string/float_button_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_floating"
+            android:tint="?androidprv:attr/materialColorOnSurface"
+            android:layout_weight="1"
+            style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+        <ImageButton
+            android:id="@+id/desktop_button"
+            android:layout_marginStart="4dp"
+            android:contentDescription="@string/desktop_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
+            android:tint="?androidprv:attr/materialColorOnSurface"
+            android:layout_weight="1"
+            style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        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"
+        android:layout_marginStart="1dp"
+        android:orientation="vertical"
+        android:elevation="1dp"
+        android:background="@drawable/desktop_mode_decor_handle_menu_background">
+
+        <Button
+            android:id="@+id/screenshot_button"
+            android:contentDescription="@string/screenshot_text"
+            android:text="@string/screenshot_text"
+            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
+            android:drawableTint="?androidprv:attr/materialColorOnSurface"
+            style="@style/DesktopModeHandleMenuActionButton"/>
+
+        <Button
+            android:id="@+id/select_button"
+            android:contentDescription="@string/select_text"
+            android:text="@string/select_text"
+            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
+            android:drawableTint="?androidprv:attr/materialColorOnSurface"
+            style="@style/DesktopModeHandleMenuActionButton"/>
+
+    </LinearLayout>
+</LinearLayout>
+
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
deleted file mode 100644
index c2ee306..0000000
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="@dimen/desktop_mode_handle_menu_width"
-    android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
-    android:orientation="horizontal"
-    android:background="@drawable/desktop_mode_decor_handle_menu_background"
-    android:gravity="center_vertical">
-
-    <ImageView
-        android:id="@+id/application_icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_marginStart="14dp"
-        android:layout_marginEnd="14dp"
-        android:contentDescription="@string/app_icon_text"/>
-
-    <TextView
-        android:id="@+id/application_name"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        tools:text="Gmail"
-        android:textColor="?androidprv:attr/materialColorOnSurface"
-        android:textSize="14sp"
-        android:textFontWeight="500"
-        android:lineHeight="20dp"
-        android:textStyle="normal"
-        android:layout_weight="1"/>
-
-    <ImageButton
-        android:id="@+id/collapse_menu_button"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:padding="4dp"
-        android:layout_marginEnd="14dp"
-        android:layout_marginStart="14dp"
-        android:contentDescription="@string/collapse_menu_text"
-        android:src="@drawable/ic_baseline_expand_more_24"
-        android:rotation="180"
-        android:tint="?androidprv:attr/materialColorOnSurface"
-        android:background="?android:selectableItemBackgroundBorderless"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
deleted file mode 100644
index e637671..0000000
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:layout_width="@dimen/desktop_mode_handle_menu_width"
-    android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
-    android:orientation="vertical"
-    android:background="@drawable/desktop_mode_decor_handle_menu_background">
-
-    <Button
-        android:id="@+id/screenshot_button"
-        android:contentDescription="@string/screenshot_text"
-        android:text="@string/screenshot_text"
-        android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
-        android:drawableTint="?androidprv:attr/materialColorOnSurface"
-        style="@style/DesktopModeHandleMenuActionButton"/>
-
-    <Button
-        android:id="@+id/select_button"
-        android:contentDescription="@string/select_text"
-        android:text="@string/select_text"
-        android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
-        android:drawableTint="?androidprv:attr/materialColorOnSurface"
-        style="@style/DesktopModeHandleMenuActionButton"/>
-    <Button
-        android:id="@+id/close_button"
-        android:contentDescription="@string/close_text"
-        android:text="@string/close_text"
-        android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close"
-        android:drawableTint="?androidprv:attr/materialColorOnSurface"
-        style="@style/DesktopModeHandleMenuActionButton"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
deleted file mode 100644
index c4b688d..0000000
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:layout_width="@dimen/desktop_mode_handle_menu_width"
-    android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
-    android:orientation="horizontal"
-    android:background="@drawable/desktop_mode_decor_handle_menu_background"
-    android:gravity="center_vertical">
-
-    <ImageButton
-        android:id="@+id/fullscreen_button"
-        android:layout_marginEnd="4dp"
-        android:contentDescription="@string/fullscreen_text"
-        android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
-        android:tint="?androidprv:attr/materialColorOnSurface"
-        android:layout_weight="1"
-        style="@style/DesktopModeHandleMenuWindowingButton"/>
-
-    <ImageButton
-        android:id="@+id/split_screen_button"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:contentDescription="@string/split_screen_text"
-        android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
-        android:tint="?androidprv:attr/materialColorOnSurface"
-        android:layout_weight="1"
-        style="@style/DesktopModeHandleMenuWindowingButton"/>
-
-    <ImageButton
-        android:id="@+id/floating_button"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:contentDescription="@string/float_button_text"
-        android:src="@drawable/desktop_mode_ic_handle_menu_floating"
-        android:tint="?androidprv:attr/materialColorOnSurface"
-        android:layout_weight="1"
-        style="@style/DesktopModeHandleMenuWindowingButton"/>
-
-    <ImageButton
-        android:id="@+id/desktop_button"
-        android:layout_marginStart="4dp"
-        android:contentDescription="@string/desktop_text"
-        android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
-        android:tint="?androidprv:attr/materialColorOnSurface"
-        android:layout_weight="1"
-        style="@style/DesktopModeHandleMenuWindowingButton"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 705e387..7a309f5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -434,11 +434,11 @@
     <!-- The height of the handle menu's "Windowing" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
 
-    <!-- The height of the handle menu's "More Actions" pill in desktop mode, but not freeform. -->
-    <dimen name="desktop_mode_handle_menu_more_actions_pill_height">104dp</dimen>
+    <!-- The height of the handle menu's "More Actions" pill in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_more_actions_pill_height">52dp</dimen>
 
-    <!-- The height of the handle menu's "More Actions" pill in freeform desktop windowing mode. -->
-    <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">52dp</dimen>
+    <!-- The height of the handle menu in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_height">328dp</dimen>
 
     <!-- The top margin of the handle menu in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 410ae78d..38550b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -32,6 +32,7 @@
 import android.window.DisplayAreaAppearedInfo;
 import android.window.DisplayAreaInfo;
 import android.window.DisplayAreaOrganizer;
+import android.window.SystemPerformanceHinter;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -58,6 +59,14 @@
     /** {@link DisplayAreaContext} list, which is mapped by display IDs. */
     private final SparseArray<DisplayAreaContext> mDisplayAreaContexts = new SparseArray<>();
 
+    private final SystemPerformanceHinter.DisplayRootProvider mPerfRootProvider =
+            new SystemPerformanceHinter.DisplayRootProvider() {
+                @Override
+                public SurfaceControl getRootForDisplay(int displayId) {
+                    return mLeashes.get(displayId);
+                }
+            };
+
     private final Context mContext;
 
     public RootTaskDisplayAreaOrganizer(Executor executor, Context context) {
@@ -229,6 +238,11 @@
         return mDisplayAreaContexts.get(displayId);
     }
 
+    @NonNull
+    public SystemPerformanceHinter.DisplayRootProvider getPerformanceRootProvider() {
+        return mPerfRootProvider;
+    }
+
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         final String childPrefix = innerPrefix + "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index f1ee8fa..a67821b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -318,7 +318,7 @@
     /**
      * Animates the dot to the given scale, running the optional callback when the animation ends.
      */
-    private void animateDotScale(float toScale, @Nullable Runnable after) {
+    public void animateDotScale(float toScale, @Nullable Runnable after) {
         mDotIsAnimating = true;
 
         // Don't restart the animation if we're already animating to the given value.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index df19757..dc099d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -27,6 +27,7 @@
 import android.graphics.drawable.InsetDrawable
 import android.util.PathParser
 import android.view.LayoutInflater
+import android.view.View.VISIBLE
 import android.widget.FrameLayout
 import com.android.launcher3.icons.BubbleIconFactory
 import com.android.wm.shell.R
@@ -156,7 +157,9 @@
 
     fun setShowDot(show: Boolean) {
         showDot = show
-        overflowBtn?.updateDotVisibility(true /* animate */)
+        if (overflowBtn?.visibility == VISIBLE) {
+            overflowBtn?.updateDotVisibility(true /* animate */)
+        }
     }
 
     /** Creates the expanded view for bubbles showing in the stack view. */
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 c124b53..2241c34 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
@@ -1864,6 +1864,14 @@
                 : GONE);
     }
 
+    private void updateOverflowDotVisibility(boolean expanding) {
+        if (mBubbleOverflow.showDot()) {
+            mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> {
+                mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE);
+            });
+        }
+    }
+
     // via BubbleData.Listener
     void updateBubble(Bubble bubble) {
         animateInFlyoutForBubble(bubble);
@@ -2274,6 +2282,7 @@
             if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
                 maybeShowManageEdu();
             }
+            updateOverflowDotVisibility(true /* expanding */);
         } /* after */);
         int index;
         if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
@@ -2405,11 +2414,15 @@
         // since we're about to animate collapsed.
         mExpandedAnimationController.notifyPreparingToCollapse();
 
+        updateOverflowDotVisibility(false /* expanding */);
         final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack(
                 mStackAnimationController
                         .getStackPositionAlongNearestHorizontalEdge()
                 /* collapseTo */,
-                () -> mBubbleContainer.setActiveController(mStackAnimationController));
+                () -> {
+                    mBubbleContainer.setActiveController(mStackAnimationController);
+                    updateOverflowVisibility();
+                });
 
         final Runnable after = () -> {
             final BubbleViewProvider previouslySelected = mExpandedBubble;
@@ -2424,7 +2437,6 @@
                 Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
                         mExpandedBubble));
             }
-            updateOverflowVisibility();
             updateZOrder();
             updateBadges(true /* setBadgeForCollapsedStack */);
             afterExpandedViewAnimation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 4d7042b..738c94e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -34,6 +34,8 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BadgedImageView;
+import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleStackView;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -63,6 +65,12 @@
     /** Damping ratio for expand/collapse spring. */
     private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f;
 
+    /**
+     * Damping ratio for the overflow bubble spring; this is less bouncy so it doesn't bounce behind
+     * the top bubble when it goes to disappear.
+     */
+    private static final float DAMPING_RATIO_OVERFLOW_BOUNCY = 0.90f;
+
     /** Stiffness for the expand/collapse path-following animation. */
     private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400;
 
@@ -274,9 +282,14 @@
                 // of the screen where the bubble will be stacked.
                 path.lineTo(stackedX, p.y);
 
+                // The overflow should animate to the collapse point, so 0 offset.
+                final boolean isOverflow = bubble instanceof BadgedImageView
+                        && BubbleOverflow.KEY.equals(((BadgedImageView) bubble).getKey());
+                final float offsetY = isOverflow
+                        ? 0
+                        : Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx;
                 // Then, draw a line down to the stack position.
-                path.lineTo(stackedX, mCollapsePoint.y
-                        + Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx);
+                path.lineTo(stackedX, mCollapsePoint.y + offsetY);
             }
 
             // The lead bubble should be the bubble with the longest distance to travel when we're
@@ -505,8 +518,12 @@
 
     @Override
     SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
+        boolean isOverflow = (view instanceof BadgedImageView)
+                && BubbleOverflow.KEY.equals(((BadgedImageView) view).getKey());
         return new SpringForce()
-                .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
+                .setDampingRatio(isOverflow
+                        ? DAMPING_RATIO_OVERFLOW_BOUNCY
+                        : DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
                 .setStiffness(SpringForce.STIFFNESS_LOW);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 5e42782..e9344ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -203,7 +203,7 @@
                     + "SystemWindow:" + view);
             return null;
         }
-        return root.getFocusGrantToken();
+        return root.getInputTransferToken();
     }
 
     private class PerDisplay {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 998cd5d..e6d3abc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -25,6 +25,7 @@
 import android.os.SystemProperties;
 import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.window.SystemPerformanceHinter;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.launcher3.icons.IconProvider;
@@ -85,6 +86,7 @@
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.performance.PerfHintController;
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
@@ -296,6 +298,17 @@
         return new LaunchAdjacentController(syncQueue);
     }
 
+    @WMSingleton
+    @Provides
+    static SystemPerformanceHinter provideSystemPerformanceHinter(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
+            RootTaskDisplayAreaOrganizer rootTdaOrganizer) {
+        final PerfHintController perfHintController =
+                new PerfHintController(context, shellInit, shellCommandHandler, rootTdaOrganizer);
+        return perfHintController.getHinter();
+    }
+
     //
     // Back animation
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 11aa0546..5dfba5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -494,13 +494,14 @@
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
             @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
             LaunchAdjacentController launchAdjacentController,
+            RecentsTransitionHandler recentsTransitionHandler,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
                 displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
                 transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
                 toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
-                launchAdjacentController, mainExecutor);
+                launchAdjacentController, recentsTransitionHandler, mainExecutor);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index af97cf6..8a64037 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -23,7 +23,7 @@
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.dagger.WMShellBaseModule;
 import com.android.wm.shell.dagger.WMSingleton;
-import com.android.wm.shell.pip2.PipTransition;
+import com.android.wm.shell.pip2.phone.PipTransition;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 570f0a3..f2631ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -19,6 +19,7 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.dagger.WMSingleton;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.phone.PipTransition;
 
 import dagger.Module;
 import dagger.Provides;
@@ -36,7 +37,7 @@
     @Provides
     static PipTransitionController providePipTransitionController(
             com.android.wm.shell.pip.PipTransition legacyPipTransition,
-            com.android.wm.shell.pip2.PipTransition newPipTransition) {
+            PipTransition newPipTransition) {
         if (PipUtils.isPip2ExperimentEnabled()) {
             return newPipTransition;
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 09ba4f7..412a5b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -60,6 +60,8 @@
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
 import com.android.wm.shell.sysui.ShellCommandHandler
@@ -68,7 +70,6 @@
 import com.android.wm.shell.sysui.ShellSharedConstants
 import com.android.wm.shell.transition.OneShotRemoteHandler
 import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TransitionHandler
 import com.android.wm.shell.util.KtProtoLog
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -93,6 +94,7 @@
         ToggleResizeDesktopTaskTransitionHandler,
         private val desktopModeTaskRepository: DesktopModeTaskRepository,
         private val launchAdjacentController: LaunchAdjacentController,
+        private val recentsTransitionHandler: RecentsTransitionHandler,
         @ShellMainThread private val mainExecutor: ShellExecutor
 ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
 
@@ -119,6 +121,8 @@
             com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
         )
 
+    private var recentsAnimationRunning = false
+
     // This is public to avoid cyclic dependency; it is set by SplitScreenController
     lateinit var splitScreenController: SplitScreenController
 
@@ -139,6 +143,19 @@
         )
         transitions.addHandler(this)
         desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
+
+        recentsTransitionHandler.addTransitionStateListener(
+            object : RecentsTransitionStateListener {
+                override fun onAnimationStateChanged(running: Boolean) {
+                    KtProtoLog.v(
+                        WM_SHELL_DESKTOP_MODE,
+                        "DesktopTasksController: recents animation state changed running=%b",
+                        running
+                    )
+                    recentsAnimationRunning = running
+                }
+            }
+        )
     }
 
     /** Show all tasks, that are part of the desktop, on top of launcher */
@@ -644,6 +661,10 @@
         val triggerTask = request.triggerTask
         val shouldHandleRequest =
             when {
+                recentsAnimationRunning -> {
+                    reason = "recents animation is running"
+                    false
+                }
                 // Only handle open or to front transitions
                 request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
                     reason = "transition type not handled (${request.type})"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt
new file mode 100644
index 0000000..f7977f8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.performance
+
+import android.content.Context
+import android.os.PerformanceHintManager
+import android.os.Process
+import android.window.SystemPerformanceHinter
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
+import java.io.PrintWriter
+import java.util.concurrent.TimeUnit
+
+/**
+ * Manages the performance hints to the system.
+ */
+class PerfHintController(private val mContext: Context,
+                         shellInit: ShellInit,
+                         private val mShellCommandHandler: ShellCommandHandler,
+                         rootTdaOrganizer: RootTaskDisplayAreaOrganizer) {
+
+    // The system perf hinter
+    val hinter: SystemPerformanceHinter
+
+    init {
+        hinter = SystemPerformanceHinter(mContext,
+                rootTdaOrganizer.performanceRootProvider)
+        shellInit.addInitCallback(this::onInit, this)
+    }
+
+    private fun onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this)
+        val perfHintMgr = mContext.getSystemService(PerformanceHintManager::class.java)
+        val adpfSession = perfHintMgr!!.createHintSession(intArrayOf(Process.myTid()),
+                TimeUnit.SECONDS.toNanos(1))
+        hinter.setAdpfSession(adpfSession)
+    }
+
+    fun dump(pw: PrintWriter, prefix: String?) {
+        hinter.dump(pw, prefix)
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
deleted file mode 100644
index b8e4c04..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
+++ /dev/null
@@ -1,81 +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.wm.shell.pip2;
-
-import android.annotation.NonNull;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.pip.PipMenuController;
-import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-/** Placeholder, for demonstrate purpose only. */
-public class PipTransition extends PipTransitionController {
-    public PipTransition(
-            @NonNull ShellInit shellInit,
-            @NonNull ShellTaskOrganizer shellTaskOrganizer,
-            @NonNull Transitions transitions,
-            PipBoundsState pipBoundsState,
-            PipMenuController pipMenuController,
-            PipBoundsAlgorithm pipBoundsAlgorithm) {
-        super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
-                pipBoundsAlgorithm);
-    }
-
-    @Override
-    protected void onInit() {
-        if (PipUtils.isPip2ExperimentEnabled()) {
-            mTransitions.addHandler(this);
-        }
-    }
-
-    @Nullable
-    @Override
-    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
-            @NonNull TransitionRequestInfo request) {
-        return null;
-    }
-
-    @Override
-    public boolean startAnimation(@NonNull IBinder transition,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        return false;
-    }
-
-    @Override
-    public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {}
-
-    @Override
-    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
-            @Nullable SurfaceControl.Transaction finishT) {}
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
new file mode 100644
index 0000000..d704b09
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.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.wm.shell.pip2.phone;
+
+import static android.view.WindowManager.TRANSIT_OPEN;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * Implementation of transitions for PiP on phone.
+ */
+public class PipTransition extends PipTransitionController {
+    @Nullable
+    private IBinder mAutoEnterButtonNavTransition;
+
+    public PipTransition(
+            @NonNull ShellInit shellInit,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer,
+            @NonNull Transitions transitions,
+            PipBoundsState pipBoundsState,
+            PipMenuController pipMenuController,
+            PipBoundsAlgorithm pipBoundsAlgorithm) {
+        super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+                pipBoundsAlgorithm);
+    }
+
+    @Override
+    protected void onInit() {
+        if (PipUtils.isPip2ExperimentEnabled()) {
+            mTransitions.addHandler(this);
+        }
+    }
+
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        if (isAutoEnterInButtonNavigation(request)) {
+            mAutoEnterButtonNavTransition = transition;
+            return getEnterPipTransaction(transition, request);
+        }
+        return null;
+    }
+
+    @Override
+    public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request,
+            @NonNull WindowContainerTransaction outWct) {
+        if (isAutoEnterInButtonNavigation(request)) {
+            outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */);
+            mAutoEnterButtonNavTransition = transition;
+        }
+    }
+
+    private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
+        PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
+        mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
+                pipParams, mPipBoundsAlgorithm);
+
+        // calculate the entry bounds and notify core to move task to pinned with final bounds
+        final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
+        return wct;
+    }
+
+    private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) {
+        final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask();
+        if (pipTask == null) {
+            return false;
+        }
+        if (pipTask.pictureInPictureParams == null) {
+            return false;
+        }
+
+        // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type
+        // implies that we are entering PiP in button navigation mode. This is guaranteed by
+        // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav.
+        return requestInfo.getType() == TRANSIT_OPEN
+                && pipTask.pictureInPictureParams.isAutoEnterEnabled();
+    }
+
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition,
+            @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        if (transition == mAutoEnterButtonNavTransition) {
+            startTransaction.apply();
+            finishCallback.onTransitionFinished(null);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {}
+
+    @Override
+    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishT) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index ead2f9c..d31476c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -54,6 +54,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -279,7 +280,7 @@
             mDeathHandler = () -> {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                         "[%d] RecentsController.DeathRecipient: binder died", mInstanceId);
-                finish(mWillFinishToHome, false /* leaveHint */);
+                finish(mWillFinishToHome, false /* leaveHint */, null /* finishCb */);
             };
             try {
                 mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
@@ -313,7 +314,7 @@
                 }
             }
             if (mFinishCB != null) {
-                finishInner(toHome, false /* userLeave */);
+                finishInner(toHome, false /* userLeave */, null /* finishCb */);
             } else {
                 cleanUp();
             }
@@ -670,7 +671,8 @@
                 // now and let it do its animation (since recents is going to be occluded).
                 sendCancelWithSnapshots();
                 mExecutor.executeDelayed(
-                        () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0);
+                        () -> finishInner(true /* toHome */, false /* userLeaveHint */,
+                                null /* finishCb */), 0);
                 return;
             }
             if (recentsOpening != null) {
@@ -899,11 +901,12 @@
 
         @Override
         @SuppressLint("NewApi")
-        public void finish(boolean toHome, boolean sendUserLeaveHint) {
-            mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint));
+        public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) {
+            mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint, finishCb));
         }
 
-        private void finishInner(boolean toHome, boolean sendUserLeaveHint) {
+        private void finishInner(boolean toHome, boolean sendUserLeaveHint,
+                IResultReceiver runnerFinishCb) {
             if (mFinishCB == null) {
                 Slog.e(TAG, "Duplicate call to finish");
                 return;
@@ -993,6 +996,16 @@
             }
             cleanUp();
             finishCB.onTransitionFinished(wct.isEmpty() ? null : wct);
+            if (runnerFinishCb != null) {
+                try {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                            "[%d] RecentsController.finishInner: calling finish callback",
+                            mInstanceId);
+                    runnerFinishCb.send(0, null);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to report transition finished", e);
+                }
+            }
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 83dc7fa..e828eed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -693,9 +694,19 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        Transitions.TransitionFinishCallback finishCB = wct -> {
+            mixed.mInFlightSubAnimations--;
+            if (mixed.mInFlightSubAnimations == 0) {
+                mActiveTransitions.remove(mixed);
+                finishCallback.onTransitionFinished(wct);
+            }
+        };
+
+        mixed.mInFlightSubAnimations++;
         boolean consumed = mRecentsHandler.startAnimation(
-                mixed.mTransition, info, startTransaction, finishTransaction, finishCallback);
+                mixed.mTransition, info, startTransaction, finishTransaction, finishCB);
         if (!consumed) {
+            mixed.mInFlightSubAnimations--;
             return false;
         }
         if (mDesktopTasksController != null) {
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 e0635ac..de03f58 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
@@ -322,7 +322,6 @@
         final Runnable onAnimFinish = () -> {
             if (!animations.isEmpty()) return;
             mAnimations.remove(transition);
-            info.releaseAllSurfaces();
             finishCallback.onTransitionFinished(null /* wct */);
         };
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 780bbb5..bf99ab3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -376,7 +376,7 @@
         public void onClick(View v) {
             final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
             final int id = v.getId();
-            if (id == R.id.close_window || id == R.id.close_button) {
+            if (id == R.id.close_window) {
                 mTaskOperations.closeTask(mTaskToken);
                 if (isTaskInSplitScreen(mTaskId)) {
                     RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
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 a7a11de..15f8f1c 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
@@ -42,7 +42,6 @@
 import android.window.SurfaceSyncGroup;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
 
 /**
  * Handle menu opened when the appropriate button is clicked on.
@@ -56,12 +55,8 @@
     private static final String TAG = "HandleMenu";
     private final Context mContext;
     private final WindowDecoration mParentDecor;
-    private WindowDecoration.AdditionalWindow mAppInfoPill;
-    private WindowDecoration.AdditionalWindow mWindowingPill;
-    private WindowDecoration.AdditionalWindow mMoreActionsPill;
-    private final PointF mAppInfoPillPosition = new PointF();
-    private final PointF mWindowingPillPosition = new PointF();
-    private final PointF mMoreActionsPillPosition = new PointF();
+    private WindowDecoration.AdditionalWindow mHandleMenuWindow;
+    private final PointF mHandleMenuPosition = new PointF();
     private final boolean mShouldShowWindowingPill;
     private final Drawable mAppIcon;
     private final CharSequence mAppName;
@@ -73,13 +68,8 @@
     private final int mCaptionY;
     private int mMarginMenuTop;
     private int mMarginMenuStart;
-    private int mMarginMenuSpacing;
+    private int mMenuHeight;
     private int mMenuWidth;
-    private int mAppInfoPillHeight;
-    private int mWindowingPillHeight;
-    private int mMoreActionsPillHeight;
-    private int mShadowRadius;
-    private int mCornerRadius;
 
 
     HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
@@ -104,102 +94,86 @@
         final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG);
         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
 
-        createAppInfoPill(t, ssg);
-        if (mShouldShowWindowingPill) {
-            createWindowingPill(t, ssg);
-        }
-        createMoreActionsPill(t, ssg);
+        createHandleMenuWindow(t, ssg);
         ssg.addTransaction(t);
         ssg.markSyncReady();
         setupHandleMenu();
     }
 
-    private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
-        final int x = (int) mAppInfoPillPosition.x;
-        final int y = (int) mAppInfoPillPosition.y;
-        mAppInfoPill = mParentDecor.addWindow(
-                R.layout.desktop_mode_window_decor_handle_menu_app_info_pill,
-                "Menu's app info pill",
-                t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius);
-    }
-
-    private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
-        final int x = (int) mWindowingPillPosition.x;
-        final int y = (int) mWindowingPillPosition.y;
-        mWindowingPill = mParentDecor.addWindow(
-                R.layout.desktop_mode_window_decor_handle_menu_windowing_pill,
-                "Menu's windowing pill",
-                t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius);
-    }
-
-    private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
-        final int x = (int) mMoreActionsPillPosition.x;
-        final int y = (int) mMoreActionsPillPosition.y;
-        mMoreActionsPill = mParentDecor.addWindow(
-                R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill,
-                "Menu's more actions pill",
-                t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius);
+    private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
+        final int x = (int) mHandleMenuPosition.x;
+        final int y = (int) mHandleMenuPosition.y;
+        mHandleMenuWindow = mParentDecor.addWindow(
+                R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
+                t, ssg, x, y, mMenuWidth, mMenuHeight);
     }
 
     /**
-     * Set up interactive elements and color of this handle menu
+     * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
+     * pill.
      */
     private void setupHandleMenu() {
-        // App Info pill setup.
-        final View appInfoPillView = mAppInfoPill.mWindowViewHost.getView();
-        final ImageButton collapseBtn = appInfoPillView.findViewById(R.id.collapse_menu_button);
-        final ImageView appIcon = appInfoPillView.findViewById(R.id.application_icon);
-        final TextView appName = appInfoPillView.findViewById(R.id.application_name);
+        final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView();
+        handleMenu.setOnTouchListener(mOnTouchListener);
+        setupAppInfoPill(handleMenu);
+        if (mShouldShowWindowingPill) {
+            setupWindowingPill(handleMenu);
+        }
+        setupMoreActionsPill(handleMenu);
+    }
+
+    /**
+     * Set up interactive elements of handle menu's app info pill.
+     */
+    private void setupAppInfoPill(View handleMenu) {
+        final ImageButton collapseBtn = handleMenu.findViewById(R.id.collapse_menu_button);
+        final ImageView appIcon = handleMenu.findViewById(R.id.application_icon);
+        final TextView appName = handleMenu.findViewById(R.id.application_name);
         collapseBtn.setOnClickListener(mOnClickListener);
-        appInfoPillView.setOnTouchListener(mOnTouchListener);
         appIcon.setImageDrawable(mAppIcon);
         appName.setText(mAppName);
+    }
 
-        // Windowing pill setup.
-        if (mShouldShowWindowingPill) {
-            final View windowingPillView = mWindowingPill.mWindowViewHost.getView();
-            final ImageButton fullscreenBtn = windowingPillView.findViewById(
-                    R.id.fullscreen_button);
-            final ImageButton splitscreenBtn = windowingPillView.findViewById(
-                    R.id.split_screen_button);
-            final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button);
-            // TODO: Remove once implemented.
-            floatingBtn.setVisibility(View.GONE);
+    /**
+     * Set up interactive elements and color of handle menu's windowing pill.
+     */
+    private void setupWindowingPill(View handleMenu) {
+        final ImageButton fullscreenBtn = handleMenu.findViewById(
+                R.id.fullscreen_button);
+        final ImageButton splitscreenBtn = handleMenu.findViewById(
+                R.id.split_screen_button);
+        final ImageButton floatingBtn = handleMenu.findViewById(R.id.floating_button);
+        // TODO: Remove once implemented.
+        floatingBtn.setVisibility(View.GONE);
 
-            final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button);
-            fullscreenBtn.setOnClickListener(mOnClickListener);
-            splitscreenBtn.setOnClickListener(mOnClickListener);
-            floatingBtn.setOnClickListener(mOnClickListener);
-            desktopBtn.setOnClickListener(mOnClickListener);
-            // The button corresponding to the windowing mode that the task is currently in uses a
-            // different color than the others.
-            final int[] iconColors = getWindowingIconColor();
-            final ColorStateList inActiveColorStateList = ColorStateList.valueOf(iconColors[0]);
-            final ColorStateList activeColorStateList = ColorStateList.valueOf(iconColors[1]);
-            fullscreenBtn.setImageTintList(
-                    mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                            ? activeColorStateList : inActiveColorStateList);
-            splitscreenBtn.setImageTintList(
-                    mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
-                            ? activeColorStateList : inActiveColorStateList);
-            floatingBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED
-                    ? activeColorStateList : inActiveColorStateList);
-            desktopBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
-                    ? activeColorStateList : inActiveColorStateList);
-        }
+        final ImageButton desktopBtn = handleMenu.findViewById(R.id.desktop_button);
+        fullscreenBtn.setOnClickListener(mOnClickListener);
+        splitscreenBtn.setOnClickListener(mOnClickListener);
+        floatingBtn.setOnClickListener(mOnClickListener);
+        desktopBtn.setOnClickListener(mOnClickListener);
+        // The button corresponding to the windowing mode that the task is currently in uses a
+        // different color than the others.
+        final ColorStateList[] iconColors = getWindowingIconColor();
+        final ColorStateList inActiveColorStateList = iconColors[0];
+        final ColorStateList activeColorStateList = iconColors[1];
+        final int windowingMode = mTaskInfo.getWindowingMode();
+        fullscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_FULLSCREEN
+                        ? activeColorStateList : inActiveColorStateList);
+        splitscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_MULTI_WINDOW
+                        ? activeColorStateList : inActiveColorStateList);
+        floatingBtn.setImageTintList(windowingMode == WINDOWING_MODE_PINNED
+                ? activeColorStateList : inActiveColorStateList);
+        desktopBtn.setImageTintList(windowingMode == WINDOWING_MODE_FREEFORM
+                ? activeColorStateList : inActiveColorStateList);
+    }
 
-        // More Actions pill setup.
-        final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView();
-        final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button);
-        if (shouldShowCloseButton()) {
-            closeBtn.setVisibility(View.GONE);
-        } else {
-            closeBtn.setVisibility(View.VISIBLE);
-            closeBtn.setOnClickListener(mOnClickListener);
-        }
-        final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button);
+    /**
+     * Set up interactive elements & height of handle menu's more actions pill
+     */
+    private void setupMoreActionsPill(View handleMenu) {
+        final Button selectBtn = handleMenu.findViewById(R.id.select_button);
         selectBtn.setOnClickListener(mOnClickListener);
-        final Button screenshotBtn = moreActionsPillView.findViewById(R.id.screenshot_button);
+        final Button screenshotBtn = handleMenu.findViewById(R.id.screenshot_button);
         // TODO: Remove once implemented.
         screenshotBtn.setVisibility(View.GONE);
     }
@@ -208,7 +182,7 @@
      * Returns array of windowing icon color based on current UI theme. First element of the
      * array is for inactive icons and the second is for active icons.
      */
-    private int[] getWindowingIconColor() {
+    private ColorStateList[] getWindowingIconColor() {
         final int mode = mContext.getResources().getConfiguration().uiMode
                 & Configuration.UI_MODE_NIGHT_MASK;
         final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES);
@@ -218,11 +192,12 @@
         final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK);
         final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
         typedArray.recycle();
-        return new int[] {inActiveColor, activeColor};
+        return new ColorStateList[]{ColorStateList.valueOf(inActiveColor),
+                ColorStateList.valueOf(activeColor)};
     }
 
     /**
-     * Updates the handle menu pills' position variables to reflect their next positions
+     * Updates handle menu's position variables to reflect its next position.
      */
     private void updateHandleMenuPillPositions() {
         final int menuX, menuY;
@@ -239,39 +214,19 @@
             menuY = mCaptionY + mMarginMenuStart;
         }
 
-        // App Info pill setup.
-        final int appInfoPillY = menuY;
-        mAppInfoPillPosition.set(menuX, appInfoPillY);
+        // Handle Menu position setup.
+        mHandleMenuPosition.set(menuX, menuY);
 
-        final int windowingPillY, moreActionsPillY;
-        if (mShouldShowWindowingPill) {
-            windowingPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
-            mWindowingPillPosition.set(menuX, windowingPillY);
-            moreActionsPillY = windowingPillY + mWindowingPillHeight + mMarginMenuSpacing;
-            mMoreActionsPillPosition.set(menuX, moreActionsPillY);
-        } else {
-            // Just start after the end of the app info pill + margins.
-            moreActionsPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
-            mMoreActionsPillPosition.set(menuX, moreActionsPillY);
-        }
     }
 
     /**
      * Update pill layout, in case task changes have caused positioning to change.
      */
     void relayout(SurfaceControl.Transaction t) {
-        if (mAppInfoPill != null) {
+        if (mHandleMenuWindow != null) {
             updateHandleMenuPillPositions();
-            t.setPosition(mAppInfoPill.mWindowSurface,
-                    mAppInfoPillPosition.x, mAppInfoPillPosition.y);
-            // Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
-            final boolean shouldShowWindowingPill = DesktopModeStatus.isEnabled();
-            if (shouldShowWindowingPill) {
-                t.setPosition(mWindowingPill.mWindowSurface,
-                        mWindowingPillPosition.x, mWindowingPillPosition.y);
-            }
-            t.setPosition(mMoreActionsPill.mWindowSurface,
-                    mMoreActionsPillPosition.x, mMoreActionsPillPosition.y);
+            t.setPosition(mHandleMenuWindow.mWindowSurface,
+                    mHandleMenuPosition.x, mHandleMenuPosition.y);
         }
     }
 
@@ -283,12 +238,12 @@
      * @param ev the MotionEvent to compare against.
      */
     void checkClickEvent(MotionEvent ev) {
-        final View appInfoPill = mAppInfoPill.mWindowViewHost.getView();
-        final ImageButton collapse = appInfoPill.findViewById(R.id.collapse_menu_button);
+        final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView();
+        final ImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
         // Translate the input point from display coordinates to the same space as the collapse
         // button, meaning its parent (app info pill view).
-        final PointF inputPoint = new PointF(ev.getX() - mAppInfoPillPosition.x,
-                ev.getY() - mAppInfoPillPosition.y);
+        final PointF inputPoint = new PointF(ev.getX() - mHandleMenuPosition.x,
+                ev.getY() - mHandleMenuPosition.y);
         if (pointInView(collapse, inputPoint.x, inputPoint.y)) {
             mOnClickListener.onClick(collapse);
         }
@@ -303,23 +258,10 @@
      */
     boolean isValidMenuInput(PointF inputPoint) {
         if (!viewsLaidOut()) return true;
-        final boolean pointInAppInfoPill = pointInView(
-                mAppInfoPill.mWindowViewHost.getView(),
-                inputPoint.x - mAppInfoPillPosition.x,
-                inputPoint.y - mAppInfoPillPosition.y);
-        boolean pointInWindowingPill = false;
-        if (mWindowingPill != null) {
-            pointInWindowingPill = pointInView(
-                    mWindowingPill.mWindowViewHost.getView(),
-                    inputPoint.x - mWindowingPillPosition.x,
-                    inputPoint.y - mWindowingPillPosition.y);
-        }
-        final boolean pointInMoreActionsPill = pointInView(
-                mMoreActionsPill.mWindowViewHost.getView(),
-                inputPoint.x - mMoreActionsPillPosition.x,
-                inputPoint.y - mMoreActionsPillPosition.y);
-
-        return pointInAppInfoPill || pointInWindowingPill || pointInMoreActionsPill;
+        return pointInView(
+                mHandleMenuWindow.mWindowViewHost.getView(),
+                inputPoint.x - mHandleMenuPosition.x,
+                inputPoint.y - mHandleMenuPosition.y);
     }
 
     private boolean pointInView(View v, float x, float y) {
@@ -331,33 +273,31 @@
      * Check if the views for handle menu can be seen.
      */
     private boolean viewsLaidOut() {
-        return mAppInfoPill.mWindowViewHost.getView().isLaidOut();
+        return mHandleMenuWindow.mWindowViewHost.getView().isLaidOut();
     }
 
-
     private void loadHandleMenuDimensions() {
         final Resources resources = mContext.getResources();
         mMenuWidth = loadDimensionPixelSize(resources,
                 R.dimen.desktop_mode_handle_menu_width);
+        mMenuHeight = getHandleMenuHeight(resources);
         mMarginMenuTop = loadDimensionPixelSize(resources,
                 R.dimen.desktop_mode_handle_menu_margin_top);
         mMarginMenuStart = loadDimensionPixelSize(resources,
                 R.dimen.desktop_mode_handle_menu_margin_start);
-        mMarginMenuSpacing = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_pill_spacing_margin);
-        mAppInfoPillHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_app_info_pill_height);
-        mWindowingPillHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_windowing_pill_height);
-        mMoreActionsPillHeight = shouldShowCloseButton()
-                ? loadDimensionPixelSize(resources,
-                        R.dimen.desktop_mode_handle_menu_more_actions_pill_freeform_height)
-                : loadDimensionPixelSize(resources,
-                        R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
-        mShadowRadius = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_shadow_radius);
-        mCornerRadius = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_corner_radius);
+    }
+
+    /**
+     * Determines handle menu height based on if windowing pill should be shown.
+     */
+    private int getHandleMenuHeight(Resources resources) {
+        int menuHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_height);
+        if (!mShouldShowWindowingPill) {
+            menuHeight -= loadDimensionPixelSize(resources,
+                    R.dimen.desktop_mode_handle_menu_windowing_pill_height);
+        }
+        return menuHeight;
     }
 
     private int loadDimensionPixelSize(Resources resources, int resourceId) {
@@ -367,19 +307,9 @@
         return resources.getDimensionPixelSize(resourceId);
     }
 
-    private boolean shouldShowCloseButton() {
-        return mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
-    }
-
     void close() {
-        mAppInfoPill.releaseView();
-        mAppInfoPill = null;
-        if (mWindowingPill != null) {
-            mWindowingPill.releaseView();
-            mWindowingPill = null;
-        }
-        mMoreActionsPill.releaseView();
-        mMoreActionsPill = null;
+        mHandleMenuWindow.releaseView();
+        mHandleMenuWindow = null;
     }
 
     static final class Builder {
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 07fee43..6062e34 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
@@ -425,13 +425,10 @@
      * @param yPos         y position of new window
      * @param width        width of new window
      * @param height       height of new window
-     * @param shadowRadius radius of the shadow of the new window
-     * @param cornerRadius radius of the corners of the new window
      * @return the {@link AdditionalWindow} that was added.
      */
     AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t,
-            SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height, int shadowRadius,
-            int cornerRadius) {
+            SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height) {
         final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
         SurfaceControl windowSurfaceControl = builder
                 .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
@@ -442,8 +439,6 @@
 
         t.setPosition(windowSurfaceControl, xPos, yPos)
                 .setWindowCrop(windowSurfaceControl, width, height)
-                .setShadowRadius(windowSurfaceControl, shadowRadius)
-                .setCornerRadius(windowSurfaceControl, cornerRadius)
                 .show(windowSurfaceControl);
         final WindowManager.LayoutParams lp =
                 new WindowManager.LayoutParams(width, height,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
new file mode 100644
index 0000000..4f27ced
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.flicker.pip
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import com.android.wm.shell.flicker.pip.common.PipTransition
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/** Test changing aspect ratio of pip. */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+    override val thisTransition: FlickerBuilder.() -> Unit = {
+        transitions {
+            pipApp.changeAspectRatio()
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun pipAspectRatioChangesProperly() {
+        flicker.assertLayersStart { this.visibleRegion(pipApp).isSameAspectRatio(16, 9) }
+        flicker.assertLayersEnd { this.visibleRegion(pipApp).isSameAspectRatio(1, 2) }
+    }
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() =
+            LegacyFlickerTestFactory.nonRotationTests(
+                supportedRotations = listOf(Rotation.ROTATION_0)
+            )
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 54f9498..d09a90c 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -45,7 +45,7 @@
         "kotlinx-coroutines-core",
         "mockito-kotlin2",
         "mockito-target-extended-minus-junit4",
-        "truth-prebuilt",
+        "truth",
         "testables",
         "platform-test-annotations",
         "servicestests-utils",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index dea1617..ebcb640 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -54,6 +54,8 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -101,11 +103,13 @@
     @Mock lateinit var launchAdjacentController: LaunchAdjacentController
     @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
     @Mock lateinit var splitScreenController: SplitScreenController
+    @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var controller: DesktopTasksController
     private lateinit var shellInit: ShellInit
     private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+    private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
 
     private val shellExecutor = TestShellExecutor()
     // Mock running tasks are registered here so we can get the list from mock shell task organizer
@@ -126,6 +130,10 @@
         controller.splitScreenController = splitScreenController
 
         shellInit.init()
+
+        val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
+        verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
+        recentsTransitionStateListener = captor.value
     }
 
     private fun createController(): DesktopTasksController {
@@ -144,6 +152,7 @@
             mToggleResizeDesktopTaskTransitionHandler,
             desktopModeTaskRepository,
             launchAdjacentController,
+            recentsTransitionHandler,
             shellExecutor
         )
     }
@@ -355,7 +364,7 @@
 
     @Test
     fun moveToDesktop_splitTaskExitsSplit() {
-        var task = setUpSplitScreenTask()
+        val task = setUpSplitScreenTask()
         controller.moveToDesktop(desktopModeWindowDecoration, task)
         val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -367,7 +376,7 @@
 
     @Test
     fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
-        var task = setUpFullscreenTask()
+        val task = setUpFullscreenTask()
         controller.moveToDesktop(desktopModeWindowDecoration, task)
         val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -666,6 +675,20 @@
     }
 
     @Test
+    fun handleRequest_recentsAnimationRunning_returnNull() {
+        // Set up a visible freeform task so a fullscreen task should be converted to freeform
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        // Mark recents animation running
+        recentsTransitionStateListener.onAnimationStateChanged(true)
+
+        // Open a fullscreen task, check that it does not result in a WCT with changes to it
+        val fullscreenTask = createFullscreenTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
     fun stashDesktopApps_stateUpdates() {
         whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 76bc25a..966a99ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -119,8 +119,6 @@
     private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
     private Configuration mWindowConfiguration = new Configuration();
     private int mCaptionMenuWidthId;
-    private int mCaptionMenuShadowRadiusId;
-    private int mCaptionMenuCornerRadiusId;
 
     @Before
     public void setUp() {
@@ -131,8 +129,6 @@
         mRelayoutParams.mLayoutResId = 0;
         mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
         mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width;
-        mCaptionMenuShadowRadiusId = R.dimen.test_caption_menu_shadow_radius;
-        mCaptionMenuCornerRadiusId = R.dimen.test_caption_menu_corner_radius;
         mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
         mRelayoutParams.mCornerRadius = CORNER_RADIUS;
 
@@ -417,16 +413,6 @@
         final int height = WindowDecoration.loadDimensionPixelSize(
                 windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
         verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height);
-        final int shadowRadius = WindowDecoration.loadDimensionPixelSize(
-                windowDecor.mDecorWindowContext.getResources(),
-                mCaptionMenuShadowRadiusId);
-        verify(mMockSurfaceControlAddWindowT)
-                .setShadowRadius(additionalWindowSurface, shadowRadius);
-        final int cornerRadius = WindowDecoration.loadDimensionPixelSize(
-                windowDecor.mDecorWindowContext.getResources(),
-                mCaptionMenuCornerRadiusId);
-        verify(mMockSurfaceControlAddWindowT)
-                .setCornerRadius(additionalWindowSurface, cornerRadius);
         verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
         verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
                 .create(any(), eq(defaultDisplay), any());
@@ -588,13 +574,11 @@
             int y = mRelayoutParams.mCaptionY;
             int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
             int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
-            int shadowRadius = loadDimensionPixelSize(resources, mCaptionMenuShadowRadiusId);
-            int cornerRadius = loadDimensionPixelSize(resources, mCaptionMenuCornerRadiusId);
             String name = "Test Window";
             WindowDecoration.AdditionalWindow additionalWindow =
-                    addWindow(R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, name,
+                    addWindow(R.layout.desktop_mode_window_decor_handle_menu, name,
                             mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y,
-                            width, height, shadowRadius, cornerRadius);
+                            width, height);
             return additionalWindow;
         }
     }
diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp
index 64b53cb..4dafd0a 100644
--- a/libs/dream/lowlight/tests/Android.bp
+++ b/libs/dream/lowlight/tests/Android.bp
@@ -34,7 +34,7 @@
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
         "testables",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.mock",
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index 6ca991d..bc17459 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -4,9 +4,8 @@
 djsollen@google.com
 jreck@google.com
 njawad@google.com
-reed@google.com
 scroggo@google.com
-stani@google.com
+sumir@google.com
 
 # For text, e.g. Typeface, Font, Minikin, etc.
 nona@google.com
diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp
index 7df546a..80b501d 100644
--- a/libs/securebox/tests/Android.bp
+++ b/libs/securebox/tests/Android.bp
@@ -32,7 +32,7 @@
         "platform-test-annotations",
         "testables",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.mock",
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 0319f32..8800dc8 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -29,7 +29,6 @@
 import android.media.audiofx.HapticGenerator;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -767,15 +766,4 @@
         @RingtoneMedia int getEnabledMedia();
         VibrationEffect getVibrationEffect();
     }
-
-    /**
-     * Switch for using the new ringtone implementation (RingtoneV1 vs RingtoneV2). This may be
-     * called from both system server and app-side sdk.
-     *
-     * @hide
-     */
-    public static boolean useRingtoneV2() {
-        // TODO(b/293846645): chang eto new flagging infra
-        return SystemProperties.getBoolean("persist.audio.ringtone.use_v2", false);
-    }
 }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 12db8c0..b5a9ae2 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -42,6 +42,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.vibrator.Flags;
 import android.os.vibrator.persistence.VibrationXmlParser;
 import android.provider.BaseColumns;
 import android.provider.MediaStore;
@@ -607,7 +608,7 @@
 
         Ringtone ringtone;
         Uri positionUri = getRingtoneUri(position);
-        if (Ringtone.useRingtoneV2()) {
+        if (Flags.hapticsCustomizationRingtoneV2Enabled()) {
             mPreviousRingtone = new Ringtone.Builder(
                     mContext, mMediaType, getDefaultAudioAttributes(mType))
                     .setUri(positionUri)
@@ -953,7 +954,7 @@
      * @return A {@link Ringtone} for the given URI, or null.
      */
     public static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
-        if (Ringtone.useRingtoneV2()) {
+        if (Flags.hapticsCustomizationRingtoneV2Enabled()) {
             return new Ringtone.Builder(
                     context, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(-1))
                     .setUri(ringtoneUri)
@@ -970,7 +971,7 @@
             @Nullable VolumeShaper.Configuration volumeShaperConfig,
             AudioAttributes audioAttributes) {
         // TODO: move caller(s) away from this method: inline the builder call.
-        if (Ringtone.useRingtoneV2()) {
+        if (Flags.hapticsCustomizationRingtoneV2Enabled()) {
             return new Ringtone.Builder(context, Ringtone.MEDIA_SOUND, audioAttributes)
                     .setUri(ringtoneUri)
                     .setVolumeShaperConfig(volumeShaperConfig)
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
index 74f7276..b74b6a3 100644
--- a/media/java/android/media/RingtoneSelection.java
+++ b/media/java/android/media/RingtoneSelection.java
@@ -18,16 +18,23 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.net.Uri;
+import android.os.UserHandle;
+import android.os.vibrator.Flags;
 import android.provider.MediaStore;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Immutable representation a desired ringtone, usually originating from a user preference.
@@ -46,7 +53,7 @@
  * to be internally consistent and reflect effective values - with the exception of not verifying
  * the actual URI content. For example, loading a selection Uri that sets a sound source to
  * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class
- * instead returning {@link #SOUND_SOURCE_DEFAULT} from {@link #getSoundSource}.
+ * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}.
  *
  * <h2>Storing preferences</h2>
  *
@@ -57,6 +64,7 @@
  * @hide
  */
 @TestApi
+@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
 public final class RingtoneSelection {
 
     /**
@@ -70,7 +78,7 @@
      * The sound source is not explicitly specified, so it can follow default behavior for its
      * context.
      */
-    public static final int SOUND_SOURCE_DEFAULT = 0;
+    public static final int SOUND_SOURCE_UNSPECIFIED = 0;
 
     /**
      * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker.
@@ -83,15 +91,25 @@
     public static final int SOUND_SOURCE_URI = 2;
 
     /**
+     * The sound should explicitly use the system default.
+     *
+     * <p>This value isn't valid within the system default itself.
+     */
+    public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3;
+
+    // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT.
+
+    /**
      * Directive for how to make sound.
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "SOUND_SOURCE_", value = {
             SOUND_SOURCE_UNKNOWN,
-            SOUND_SOURCE_DEFAULT,
+            SOUND_SOURCE_UNSPECIFIED,
             SOUND_SOURCE_OFF,
             SOUND_SOURCE_URI,
+            SOUND_SOURCE_SYSTEM_DEFAULT,
     })
     public @interface SoundSource {}
 
@@ -106,9 +124,9 @@
     /**
      * Vibration source is not explicitly specified. If vibration is enabled, this will use the
      * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL},
-     * {@link #VIBRATION_SOURCE_APPLICATION_PROVIDED}, or system default vibration.
+     * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      */
-    public static final int VIBRATION_SOURCE_DEFAULT = 0;
+    public static final int VIBRATION_SOURCE_UNSPECIFIED = 0;
 
     /** Specifies that vibration is explicitly disabled for this ringtone. */
     public static final int VIBRATION_SOURCE_OFF = 1;
@@ -117,22 +135,31 @@
     public static final int VIBRATION_SOURCE_URI = 2;
 
     /**
+     * The vibration should explicitly use the system default.
+     *
+     * <p>This value isn't valid within the system default itself.
+     */
+    public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3;
+
+    /**
      * Specifies that vibration should use the vibration provided by the application. This is
      * typically the application's own default for the use-case, provided via
      * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration
      * effect saved on the notification channel.
      *
      * <p>If no vibration is specified by the application, this value behaves if the source was
-     * {@link #VIBRATION_SOURCE_DEFAULT}.
+     * {@link #VIBRATION_SOURCE_UNSPECIFIED}.
+     *
+     * <p>This value isn't valid within the system default.
      */
-    public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3;
+    public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4;
 
     /**
      * Specifies that vibration should use haptic audio channels from the
      * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified
-     * by {@link #VIBRATION_SOURCE_DEFAULT}.
+     * by {@link #VIBRATION_SOURCE_UNSPECIFIED}.
      */
-    // Numeric gap from VIBRATION_SOURCE_APPLICATION_PROVIDED in case we want other common elements.
+    // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements.
     public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10;
 
     /**
@@ -151,10 +178,10 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "VIBRATION_SOURCE_", value = {
             VIBRATION_SOURCE_UNKNOWN,
-            VIBRATION_SOURCE_DEFAULT,
+            VIBRATION_SOURCE_UNSPECIFIED,
             VIBRATION_SOURCE_OFF,
             VIBRATION_SOURCE_URI,
-            VIBRATION_SOURCE_APPLICATION_PROVIDED,
+            VIBRATION_SOURCE_APPLICATION_DEFAULT,
             VIBRATION_SOURCE_AUDIO_CHANNEL,
             VIBRATION_SOURCE_HAPTIC_GENERATOR,
     })
@@ -162,9 +189,12 @@
 
     /**
      * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri
-     * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}.
-     * This behavior is particularly suited to loading values from older settings that may contain
-     * a raw sound Uri or null for silent.
+     * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF},
+     * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning
+     * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}.
+     *
+     * <p>This behavior is particularly suited to loading values from older settings that may
+     * contain a raw sound Uri or null for silent.
      *
      * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
      */
@@ -173,7 +203,8 @@
     /**
      * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration
      * Uri for the returned {@link RingtoneSelection}, with null meaning
-     * {@link #VIBRATION_SOURCE_OFF}.
+     * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs
+     * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      *
      * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
      */
@@ -182,7 +213,9 @@
     /**
      * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid
      * value. Null or an invalid values will revert to default behavior correspnoding to
-     * {@link #DEFAULT_SELECTION_URI_STRING}.
+     * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs
+     * ({@link RingtoneManager#getDefaultUri}) will set both
+     * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      *
      * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false,
      * which include {@code null}.
@@ -218,10 +251,11 @@
 
     /* Common param values */
     private static final String SOURCE_OFF_STRING = "off";
+    private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys";
 
     /* Vibration source param values. */
     private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac";
-    private static final String VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING = "app";
+    private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app";
     private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg";
 
     @Nullable
@@ -236,53 +270,45 @@
 
     private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource,
             @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) {
-        // Enforce guarantees on the source values: revert to unset if they depend on something
-        // that's not set.
-        switch (soundSource) {
-            case SOUND_SOURCE_URI:
-            case SOUND_SOURCE_UNKNOWN:  // Allow unknown to revert to URI before default.
-                mSoundSource = soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_DEFAULT;
-                break;
-            default:
-                mSoundSource = soundSource;
-                break;
-        }
-        switch (vibrationSource) {
-            case VIBRATION_SOURCE_AUDIO_CHANNEL:
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR:
-                mVibrationSource = soundUri != null ? vibrationSource : VIBRATION_SOURCE_DEFAULT;
-                break;
-            case VIBRATION_SOURCE_URI:
-            case VIBRATION_SOURCE_UNKNOWN:  // Allow unknown to revert to URI.
-                mVibrationSource =
-                        vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_DEFAULT;
-                break;
-            default:
-                mVibrationSource = vibrationSource;
-                break;
-        }
+        // Enforce guarantees on the source values: revert to unspecified if they depend on
+        // something that's not set.
+        //
+        // The non-public "unknown" value can't appear in a getter result, it's just a reserved
+        // "null" value and should be treated the same as an unrecognized value. This can be seen
+        // in Uri parsing. For this and other unrecognized values, we either revert them to the URI
+        // source, if a Uri was included, or the "unspecified" source otherwise. This can be
+        // seen in action in the Uri parsing.
+        //
+        // The "unspecified" source is a public value meaning that there is no specific
+        // behavior indicated, and the defaults and fallbacks should be applied. For example, an
+        // vibration source value of "system default" means to explicitly use the system default
+        // vibration. However, an "unspecified" vibration source will first see if audio coupled
+        // or application-default vibrations are available.
+        mSoundSource = switch (soundSource) {
+            // Supported explicit values that don't have a Uri.
+            case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT ->
+                    soundSource;
+            // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
+            // unspecified.
+            default ->
+                soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED;
+        };
+        mVibrationSource = switch (vibrationSource) {
+            // Enforce vibration sources that require a sound Uri.
+            case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR ->
+                    soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED;
+            // Supported explicit values that don't rely on any Uri.
+            case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED,
+                    VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT ->
+                    vibrationSource;
+            // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
+            // unspecified.
+            default ->
+                    vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED;
+        };
         // Clear Uri values if they're un-used by the source.
-        switch (mSoundSource) {
-            case SOUND_SOURCE_OFF:
-                mSoundUri = null;
-                break;
-            default:
-                // Unset case isn't handled here: the defaulting behavior is left to the player.
-                mSoundUri = soundUri;
-                break;
-        }
-        switch (mVibrationSource) {
-            case VIBRATION_SOURCE_OFF:
-            case VIBRATION_SOURCE_APPLICATION_PROVIDED:
-            case VIBRATION_SOURCE_AUDIO_CHANNEL:
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR:
-                mVibrationUri = null;
-                break;
-            default:
-                // Unset case isn't handled here: the defaulting behavior is left to the player.
-                mVibrationUri = vibrationUri;
-                break;
-        }
+        mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null;
+        mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null;
     }
 
     /**
@@ -360,18 +386,83 @@
         }
         // Any URI content://media/ringtone
         return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
-                && MediaStore.AUTHORITY.equals(uri.getAuthority())
+                && MediaStore.AUTHORITY.equals(
+                        ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()))
                 && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath());
     }
 
+    /**
+     * Strip the specified userId from internal Uris. Non-stripped userIds will typically be
+     * for work profiles referencing system ringtones from the host user.
+     *
+     * This is only for use in RingtoneManager.
+     * @hide
+     */
+    @VisibleForTesting
+    public RingtoneSelection getWithoutUserId(int userIdToStrip) {
+        if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) {
+            return this;
+        }
 
+        // Ok if uri is null. We only replace explicit references to the specified (current) userId.
+        int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL);
+        int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL);
+        boolean needToChangeSound =
+                soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip;
+        boolean needToChangeVibration =
+                vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip;
+
+        // Anything to do?
+        if (!needToChangeSound && !needToChangeVibration) {
+            return this;
+        }
+
+        RingtoneSelection.Builder updated = new Builder(this);
+        // The relevant uris can't be null, because they contain userIdToStrip.
+        if (needToChangeSound) {
+            // mSoundUri is not null, so the result of getUriWithoutUserId won't be null.
+            updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri));
+        }
+        if (needToChangeVibration) {
+            updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri));
+        }
+        return updated.build();
+    }
+
+    @Override
+    public String toString() {
+        return toUri().toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof RingtoneSelection other)) {
+            return false;
+        }
+        return this.mSoundSource == other.mSoundSource
+                && this.mVibrationSource == other.mVibrationSource
+                && Objects.equals(this.mSoundUri, other.mSoundUri)
+                && Objects.equals(this.mVibrationUri, other.mVibrationUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri);
+    }
 
     /**
      * Converts a Uri into a RingtoneSelection.
      *
-     * <p>Null values and Uris that {@link #isRingtoneSelectionUri(Uri)} returns false will be
-     * treated according to the behaviour specified by the {@code unrecognizedValueBehavior}
-     * parameter.
+     * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for
+     * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated
+     * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter.
+     *
+     * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1,
+     * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and
+     * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      *
      * @param uri The Uri to convert, potentially null.
      * @param unrecognizedValueBehavior indicates how to treat values for which
@@ -384,21 +475,35 @@
         if (isRingtoneSelectionUri(uri)) {
             return parseRingtoneSelectionUri(uri);
         }
+        // Old symbolic default URIs map to the sources suggested by the unrecognized behavior.
+        // It doesn't always map to both sources because the app may have its own default behavior
+        // to apply, so non-primary sources are left as unspecified, which will revert to the
+        // system default in the absence of an app default.
+        boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0;
         RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
         switch (unrecognizedValueBehavior) {
             case FROM_URI_RINGTONE_SELECTION_ONLY:
-                // Always return use-defaults for unrecognized ringtone selection Uris.
+                if (isDefaultUri) {
+                    builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
+                    builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT);
+                }
+                // Always return unspecified (defaults) for unrecognized ringtone selection Uris.
                 return builder.build();
             case FROM_URI_RINGTONE_SELECTION_OR_SOUND:
                 if (uri == null) {
                     return builder.setSoundSource(SOUND_SOURCE_OFF).build();
+                } else if (isDefaultUri) {
+                    return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build();
                 } else {
                     return builder.setSoundSource(uri).build();
                 }
             case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
                 if (uri == null) {
                     return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build();
+                } else if (isDefaultUri) {
+                    return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build();
                 } else {
+                    // Unlike sound, there's no legacy settings alias uri for the default.
                     return builder.setVibrationSource(uri).build();
                 }
             default:
@@ -407,7 +512,12 @@
         }
     }
 
-    /** Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. */
+    /**
+     * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}.
+     * As a special case to be more compatible, if the RingtoneSelection has a userId specified in
+     * the authority, then this is pushed down into the sound and vibration Uri, as the selection
+     * itself is agnostic.
+     */
     @NonNull
     private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) {
         RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
@@ -416,19 +526,39 @@
                 uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE));
         Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI);
         Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI);
+
+        // Add userId if necessary. This won't override an existing one in the sound/vib Uris.
+        int userIdFromAuthority = ContentProvider.getUserIdFromAuthority(
+                uri.getAuthority(), UserHandle.USER_NULL);
+        if (userIdFromAuthority != UserHandle.USER_NULL) {
+            // Won't override existing user id.
+            if (soundUri != null) {
+                soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority);
+            }
+            if (vibrationUri != null) {
+                vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority);
+            }
+        }
+
+        // Set sound uri if present, but map system default Uris to the system default source.
         if (soundUri != null) {
-            builder.setSoundSource(soundUri);
+            if (RingtoneManager.getDefaultType(uri) >= 0) {
+                builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
+            } else {
+                builder.setSoundSource(soundUri);
+            }
         }
         if (vibrationUri != null) {
             builder.setVibrationSource(vibrationUri);
         }
+
         // Don't set the source if there's a URI and the source is default, because that will
         // override the Uri source set above. In effect, we prioritise "explicit" sources over
         // an implicit Uri source - except for "default", which isn't really explicit.
-        if (soundSource != SOUND_SOURCE_DEFAULT || soundUri == null) {
+        if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) {
             builder.setSoundSource(soundSource);
         }
-        if (vibrationSource != VIBRATION_SOURCE_DEFAULT || vibrationUri == null) {
+        if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) {
             builder.setVibrationSource(vibrationSource);
         }
         return builder.build();
@@ -450,26 +580,28 @@
      */
     @Nullable
     private static String soundSourceToString(@SoundSource int soundSource) {
-        switch (soundSource) {
-            case SOUND_SOURCE_OFF: return SOURCE_OFF_STRING;
-            default: return null;
-        }
+        return switch (soundSource) {
+            case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING;
+            case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
+            default -> null;
+        };
     }
 
     /**
      * Returns the sound source int corresponding to the query string value. Returns
      * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and
-     * {@link #SOUND_SOURCE_DEFAULT} if the value is {@code null} (not in the Uri).
+     * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri).
      */
     @SoundSource
     private static int stringToSoundSource(@Nullable String soundSource) {
         if (soundSource == null) {
-            return SOUND_SOURCE_DEFAULT;
+            return SOUND_SOURCE_UNSPECIFIED;
         }
-        switch (soundSource) {
-            case SOURCE_OFF_STRING: return SOUND_SOURCE_OFF;
-            default: return SOUND_SOURCE_UNKNOWN;
-        }
+        return switch (soundSource) {
+            case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF;
+            case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT;
+            default -> SOUND_SOURCE_UNKNOWN;
+        };
     }
 
     /**
@@ -478,30 +610,31 @@
      */
     @Nullable
     private static String vibrationSourceToString(@VibrationSource int vibrationSource) {
-        switch (vibrationSource) {
-            case VIBRATION_SOURCE_OFF: return SOURCE_OFF_STRING;
-            case VIBRATION_SOURCE_AUDIO_CHANNEL: return VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR:
-                return VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
-            case VIBRATION_SOURCE_APPLICATION_PROVIDED:
-                return VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING;
-            default: return null;
-        }
+        return switch (vibrationSource) {
+            case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING;
+            case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
+            case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
+            case VIBRATION_SOURCE_APPLICATION_DEFAULT ->
+                    VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING;
+            case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
+            default -> null;
+        };
     }
 
     @VibrationSource
     private static int stringToVibrationSource(@Nullable String vibrationSource) {
         if (vibrationSource == null) {
-            return VIBRATION_SOURCE_DEFAULT;
+            return VIBRATION_SOURCE_UNSPECIFIED;
         }
-        switch (vibrationSource) {
-            case SOURCE_OFF_STRING: return VIBRATION_SOURCE_OFF;
-            case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING: return VIBRATION_SOURCE_AUDIO_CHANNEL;
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING: return VIBRATION_SOURCE_HAPTIC_GENERATOR;
-            case VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING:
-                return VIBRATION_SOURCE_APPLICATION_PROVIDED;
-            default: return VIBRATION_SOURCE_UNKNOWN;
-        }
+        return switch (vibrationSource) {
+            case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF;
+            case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT;
+            case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL;
+            case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR;
+            case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING ->
+                    VIBRATION_SOURCE_APPLICATION_DEFAULT;
+            default -> VIBRATION_SOURCE_UNKNOWN;
+        };
     }
 
     /**
@@ -512,12 +645,13 @@
     public static final class Builder {
         private Uri mSoundUri;
         private Uri mVibrationUri;
-        @SoundSource private int mSoundSource = SOUND_SOURCE_DEFAULT;
-        @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_DEFAULT;
+        @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED;
+        @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED;
 
         /**
          * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its
-         * sound and vibration source unset, which means they would fall back to system defaults.
+         * sound and vibration source unspecified, which means they would fall back to app/system
+         * defaults.
          */
         public Builder() {}
 
@@ -559,7 +693,9 @@
          */
         @NonNull
         public Builder setSoundSource(@NonNull Uri soundUri) {
-            mSoundUri = requireNonNull(soundUri);
+            // getCanonicalUri shouldn't return null. If it somehow did, then the
+            // RingtoneSelection constructor will revert this to unspecified.
+            mSoundUri = requireNonNull(soundUri).getCanonicalUri();
             mSoundSource = SOUND_SOURCE_URI;
             return this;
         }
@@ -587,7 +723,9 @@
          */
         @NonNull
         public Builder setVibrationSource(@NonNull Uri vibrationUri) {
-            mVibrationUri = requireNonNull(vibrationUri);
+            // getCanonicalUri shouldn't return null. If it somehow did, then the
+            // RingtoneSelection constructor will revert this to unspecified.
+            mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri();
             mVibrationSource = VIBRATION_SOURCE_URI;
             return this;
         }
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index ca20225e..bdd7afe 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -22,7 +22,7 @@
         "android-ex-camera2",
         "testables",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
     jni_libs: [
         "libdexmakerjvmtiagent",
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 4cccf89..61b18c8 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -24,7 +24,7 @@
         "compatibility-device-util-axt",
         "mockito-target-minus-junit4",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
     test_suites: ["general-tests"],
     platform_apis: true,
diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp
index e313c46..48cd8b6 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -30,7 +30,7 @@
         "platform-test-annotations",
         "testng",
         "testables",
-        "truth-prebuilt",
+        "truth",
         "platform-compat-test-rules",
     ],
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index ba88484..2318bb9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -18,21 +18,23 @@
 
 import android.app.assist.AssistStructure
 import android.content.Context
-import android.credentials.GetCredentialRequest
 import android.credentials.CredentialManager
-import android.credentials.GetCandidateCredentialsResponse
 import android.credentials.CredentialOption
 import android.credentials.GetCandidateCredentialsException
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCredentialRequest
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
-import android.service.autofill.FillRequest
 import android.service.autofill.AutofillService
-import android.service.autofill.FillResponse
 import android.service.autofill.FillCallback
-import android.service.autofill.SaveRequest
+import android.service.autofill.FillRequest
+import android.service.autofill.FillResponse
 import android.service.autofill.SaveCallback
+import android.service.autofill.SaveRequest
+import android.service.credentials.CredentialProviderService
 import android.util.Log
+import android.view.autofill.AutofillId
 import org.json.JSONObject
 import java.util.concurrent.Executors
 
@@ -129,27 +131,31 @@
     }
 
     private fun traverseNode(
-            viewNode: AssistStructure.ViewNode?,
+            viewNode: AssistStructure.ViewNode,
             cmRequests: MutableList<CredentialOption>
     ) {
-        val options = getCredentialOptionsFromViewNode(viewNode)
-        cmRequests.addAll(options)
+        viewNode.autofillId?.let {
+            val options = getCredentialOptionsFromViewNode(viewNode, it)
+            cmRequests.addAll(options)
+        }
 
-        val children: List<AssistStructure.ViewNode>? =
-                viewNode?.run {
+        val children: List<AssistStructure.ViewNode> =
+                viewNode.run {
                     (0 until childCount).map { getChildAt(it) }
                 }
 
-        children?.forEach { childNode: AssistStructure.ViewNode ->
+        children.forEach { childNode: AssistStructure.ViewNode ->
             traverseNode(childNode, cmRequests)
         }
     }
 
-    private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
-            List<CredentialOption> {
+    private fun getCredentialOptionsFromViewNode(
+            viewNode: AssistStructure.ViewNode,
+            autofillId: AutofillId
+    ): List<CredentialOption> {
         // TODO(b/293945193) Replace with isCredential check from viewNode
         val credentialHints: MutableList<String> = mutableListOf()
-        if (viewNode != null && viewNode.autofillHints != null) {
+        if (viewNode.autofillHints != null) {
             for (hint in viewNode.autofillHints!!) {
                 if (hint.startsWith(CRED_HINT_PREFIX)) {
                     credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
@@ -159,12 +165,14 @@
 
         val credentialOptions: MutableList<CredentialOption> = mutableListOf()
         for (credentialHint in credentialHints) {
-            convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) }
+            convertJsonToCredentialOption(credentialHint, autofillId)
+                    .let { credentialOptions.addAll(it) }
         }
         return credentialOptions
     }
 
-    private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
+    private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId):
+            List<CredentialOption> {
         // TODO(b/302000646) Move this logic to jetpack so that is consistent
         //  with building the json
         val credentialOptions: MutableList<CredentialOption> = mutableListOf()
@@ -173,11 +181,14 @@
         val options = json.getJSONArray(CRED_OPTIONS_KEY)
         for (i in 0 until options.length()) {
             val option = options.getJSONObject(i)
-
+            val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
+            candidateBundle.putParcelable(
+                    CredentialProviderService.EXTRA_AUTOFILL_ID,
+                    autofillId)
             credentialOptions.add(CredentialOption(
                     option.getString(TYPE_KEY),
                     convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
-                    convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
+                    candidateBundle,
                     option.getBoolean(SYS_PROVIDER_REQ_KEY),
             ))
         }
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 633f186..86c62ef 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -25,7 +25,7 @@
     static_libs: [
         "androidx.test.rules",
         "mockito-target",
-        "truth-prebuilt",
+        "truth",
     ],
 
     certificate: "platform",
diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp
index 64b4c54..61a8270 100644
--- a/packages/FusedLocation/Android.bp
+++ b/packages/FusedLocation/Android.bp
@@ -47,7 +47,7 @@
     test_config: "test/AndroidTest.xml",
     srcs: [
         "test/src/**/*.java",
-        "src/**/*.java",  // include real sources because we're forced to test this directly
+        "src/**/*.java", // include real sources because we're forced to test this directly
     ],
     libs: [
         "android.test.base",
@@ -60,9 +60,9 @@
         "androidx.test.ext.junit",
         "androidx.test.ext.truth",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     platform_apis: true,
     certificate: "platform",
-    test_suites: ["device-tests"]
+    test_suites: ["device-tests"],
 }
diff --git a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
index ad3199f..d9338b1 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
@@ -337,7 +337,7 @@
     label:                              'M'
     base:                               'm'
     shift, capslock:                    'M'
-    shift+capslock:                     'n'
+    shift+capslock:                     'm'
 }
 
 key COMMA {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
index 6744922..fc53cba 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
@@ -12,9 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-
-type FULL
+type OVERLAY
 
 ### Basic QWERTY keys ###
 
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 88bb30b..7f23f74 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -227,13 +227,6 @@
         android:label="@string/keyboard_layout_turkish"
         android:keyboardLayout="@raw/keyboard_layout_turkish"
         android:keyboardLocale="tr-Latn"
-        android:keyboardLayoutType="qwerty" />
-
-    <keyboard-layout
-        android:name="keyboard_layout_turkish"
-        android:label="@string/keyboard_layout_turkish"
-        android:keyboardLayout="@raw/keyboard_layout_turkish"
-        android:keyboardLocale="tr-Latn"
         android:keyboardLayoutType="turkish_q" />
 
     <keyboard-layout
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
index d0a6188..0757df3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
@@ -29,8 +29,8 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
@@ -46,6 +46,7 @@
 fun SettingsTextFieldPassword(
     value: String,
     label: String,
+    enabled: Boolean = true,
     onTextChange: (String) -> Unit,
 ) {
     var visibility by remember { mutableStateOf(false) }
@@ -60,6 +61,7 @@
             keyboardType = KeyboardType.Password,
             imeAction = ImeAction.Send
         ),
+        enabled = enabled,
         trailingIcon = {
             Icon(
                 imageVector = if (visibility) Icons.Outlined.VisibilityOff
diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp
index 4031cd7..639d1a7 100644
--- a/packages/SettingsLib/Spa/testutils/Android.bp
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -31,7 +31,7 @@
         "androidx.compose.ui_ui-test-manifest",
         "androidx.lifecycle_lifecycle-runtime-testing",
         "mockito-kotlin2",
-        "truth-prebuilt",
+        "truth",
     ],
     kotlincflags: [
         "-Xjvm-default=all",
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java
index 815293e9..09f34b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java
@@ -16,17 +16,20 @@
 
 package com.android.settingslib.enterprise;
 
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Settings;
-import android.util.Log;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.settingslib.RestrictedLockUtils;
 
-import org.jetbrains.annotations.Nullable;
 
 final class SupervisedDeviceActionDisabledByAdminController
         extends BaseActionDisabledByAdminController {
@@ -57,8 +60,13 @@
 
     @Nullable
     @Override
-    public DialogInterface.OnClickListener getPositiveButtonListener(Context context,
-            RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+    public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+            @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+        if (enforcedAdmin.component == null
+                || TextUtils.isEmpty(enforcedAdmin.component.getPackageName())) {
+            return null;
+        }
+
         final Intent intent = new Intent(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING)
                 .setData(new Uri.Builder()
                         .scheme("policy")
@@ -72,7 +80,6 @@
             return null;
         }
         return (dialog, which) -> {
-            Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
             context.startActivity(intent);
         };
     }
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index b03c43c..4b4caf5 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -52,7 +52,7 @@
         "flag-junit",
         "mockito-target-minus-junit4",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
         "SettingsLibDeviceStateRotationLock",
         "SettingsLibSettingsSpinner",
         "SettingsLibUsageProgressBarPreference",
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index dd9cb9c..2d875cf 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -96,6 +96,6 @@
     libs: [
         "Robolectric_all-target_upstream",
         "mockito-robolectric-prebuilt",
-        "truth-prebuilt",
+        "truth",
     ],
 }
diff --git a/packages/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp
index 19ab1c6..6d6e2ff 100644
--- a/packages/SettingsLib/tests/unit/Android.bp
+++ b/packages/SettingsLib/tests/unit/Android.bp
@@ -31,6 +31,6 @@
         "SettingsLib",
         "androidx.test.ext.junit",
         "androidx.test.runner",
-        "truth-prebuilt",
+        "truth",
     ],
 }
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 92ebe09..f4ca260 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -64,7 +64,7 @@
         "SettingsLibDeviceStateRotationLock",
         "SettingsLibDisplayUtils",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.base",
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 91d2d1b..ba4ad36 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -218,6 +218,7 @@
         Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
+        Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
         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 bec1447..19fde75 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -308,6 +308,10 @@
         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_GESTURE,
+                new InclusiveIntegerRangeValidator(
+                        Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE,
+                        Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL));
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 17ce7c7..29f27f7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -120,6 +120,11 @@
         VALIDATORS.put(System.DISPLAY_COLOR_MODE_VENDOR_HINT, ANY_STRING_VALIDATOR);
         VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                System.SCREEN_BRIGHTNESS_FOR_ALS,
+                new InclusiveIntegerRangeValidator(
+                        System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT,
+                        System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM));
         VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3c8d4bc..f06b31c 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_GESTURE,
+                SecureSettingsProto.Accessibility
+                        .ACCESSIBILITY_MAGNIFICATION_GESTURE);
+        dumpSetting(s, p,
                 Settings.Secure.HEARING_AID_RINGTONE_ROUTING,
                 SecureSettingsProto.Accessibility.HEARING_AID_RINGTONE_ROUTING);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 40f7ba6..34d3d44 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2054,12 +2054,14 @@
             final Uri ringtoneUri = Uri.parse(value);
             // Stream selected ringtone into cache, so it's available for playback
             // when CE storage is still locked
-            try (InputStream in = openRingtone(getContext(), ringtoneUri);
-                 OutputStream out = new FileOutputStream(cacheFile)) {
-                FileUtils.copy(in, out);
-            } catch (IOException e) {
-                Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
-            }
+            Binder.withCleanCallingIdentity(() -> {
+                try (InputStream in = openRingtone(getContext(), ringtoneUri);
+                         OutputStream out = new FileOutputStream(cacheFile)) {
+                    FileUtils.copy(in, out);
+                } catch (IOException e) {
+                    Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
+                }
+            });
         }
         return true;
     }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c0f6231..9ddc976a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -101,6 +101,7 @@
                     Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
                     Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
                     Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
                     Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
                     Settings.System.WEAR_TTS_PREWARM_ENABLED,
                     Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ee05f2d..e40fcb2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -319,6 +319,8 @@
         "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
         "tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt",
         "tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt",
+        "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt",
+        "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt",
 
         /* Bouncer UI tests */
         "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt",
@@ -365,6 +367,42 @@
         "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
         "tests/src/com/android/systemui/qs/tiles/base/**/*.kt",
         "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt",
+
+        /* Authentication */
+        "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt",
+        "tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt",
+
+        /* Device entry */
+        "tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt",
+        "tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt",
+
+        /* Bouncer scene */
+        "tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt",
+        "tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt",
+        "tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt",
+        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt",
+        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt",
+        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt",
+        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt",
+
+        /* Lockscreen scene */
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt",
+
+        /* Shade scene */
+        "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt",
+        "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt",
+
+        /* Quick Settings scene */
+        "tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt",
+
+        /* Flexiglass / Scene framework tests */
+        "tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt",
+        "tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt",
+        "tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt",
+        "tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt",
+        "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt",
+        "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt",
+        "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt",
     ],
     path: "tests/src",
 }
@@ -428,7 +466,7 @@
         "hamcrest-library",
         "androidx.test.rules",
         "testables",
-        "truth-prebuilt",
+        "truth",
         "monet",
         "libmonet",
         "dagger2",
@@ -545,7 +583,7 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
-        "truth-prebuilt",
+        "truth",
     ],
 
     upstream: true,
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 008732e..96e1e3f 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -63,6 +63,7 @@
 
     private static final String TAG = "A11yMenuService";
     private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
+    private static final long TAKE_SCREENSHOT_DELAY_MS = 100L;
 
     private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
             (int) Math.ceil(BrightnessUtils.GAMMA_SPACE_MAX * 0.11f);
@@ -301,7 +302,14 @@
         } else if (viewTag == ShortcutId.ID_NOTIFICATION_VALUE.ordinal()) {
             performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS);
         } else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) {
-            performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
+            if (Flags.a11yMenuHideBeforeTakingAction()) {
+                // Delay before taking a screenshot to give time for the UI to close.
+                mHandler.postDelayed(
+                        () -> performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT),
+                        TAKE_SCREENSHOT_DELAY_MS);
+            } else {
+                performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
+            }
         }
 
         if (!Flags.a11yMenuHideBeforeTakingAction()) {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
index 538ecb3..3fc351c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -32,7 +32,7 @@
         "androidx.test.ext.junit",
         "compatibility-device-util-axt",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
     ],
     srcs: [
         "src/**/*.java",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 437f8af..18117a8 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -13,3 +13,11 @@
     description: "Enables all the sysui classic flags that are marked as being in teamfood"
     bug: "302578396"
 }
+
+flag {
+    name: "notifications_footer_view_refactor"
+    namespace: "systemui"
+    description: "Enables the refactored version of the footer view in the notification shade "
+        "(containing the \"Clear all\" button). Should not bring any behavior changes"
+    bug: "293167744"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 0f2e4ba..4aac279 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -39,6 +39,7 @@
 import android.view.WindowManager
 import android.view.animation.Interpolator
 import android.view.animation.PathInterpolator
+import androidx.annotation.AnyThread
 import androidx.annotation.BinderThread
 import androidx.annotation.UiThread
 import com.android.app.animation.Interpolators
@@ -149,6 +150,10 @@
             override fun onLaunchAnimationProgress(linearProgress: Float) {
                 listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
             }
+
+            override fun onLaunchAnimationCancelled() {
+                listeners.forEach { it.onLaunchAnimationCancelled() }
+            }
         }
 
     /**
@@ -191,6 +196,7 @@
                     "ActivityLaunchAnimator.callback must be set before using this animator"
                 )
         val runner = createRunner(controller)
+        val runnerDelegate = runner.delegate!!
         val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
 
         // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the
@@ -241,12 +247,15 @@
         // If we expect an animation, post a timeout to cancel it in case the remote animation is
         // never started.
         if (willAnimate) {
-            runner.delegate.postTimeout()
+            runnerDelegate.postTimeout()
 
             // Hide the keyguard using the launch animation instead of the default unlock animation.
             if (hideKeyguardWithAnimation) {
                 callback.hideKeyguardWithAnimation(runner)
             }
+        } else {
+            // We need to make sure delegate references are dropped to avoid memory leaks.
+            runner.dispose()
         }
     }
 
@@ -344,6 +353,13 @@
          */
         fun onLaunchAnimationEnd() {}
 
+        /**
+         * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
+         * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
+         * before the cancellation.
+         */
+        fun onLaunchAnimationCancelled() {}
+
         /** Called when an activity launch animation made progress. */
         fun onLaunchAnimationProgress(linearProgress: Float) {}
     }
@@ -426,6 +442,39 @@
         fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
     }
 
+    /**
+     * Invokes [onAnimationComplete] when animation is either cancelled or completed. Delegates all
+     * events to the passed [delegate].
+     */
+    @VisibleForTesting
+    inner class DelegatingAnimationCompletionListener(
+        private val delegate: Listener?,
+        private val onAnimationComplete: () -> Unit
+    ) : Listener {
+        var cancelled = false
+
+        override fun onLaunchAnimationStart() {
+            delegate?.onLaunchAnimationStart()
+        }
+
+        override fun onLaunchAnimationProgress(linearProgress: Float) {
+            delegate?.onLaunchAnimationProgress(linearProgress)
+        }
+
+        override fun onLaunchAnimationEnd() {
+            delegate?.onLaunchAnimationEnd()
+            if (!cancelled) {
+                onAnimationComplete.invoke()
+            }
+        }
+
+        override fun onLaunchAnimationCancelled() {
+            cancelled = true
+            delegate?.onLaunchAnimationCancelled()
+            onAnimationComplete.invoke()
+        }
+    }
+
     @VisibleForTesting
     inner class Runner(
         controller: Controller,
@@ -436,11 +485,21 @@
         listener: Listener? = null
     ) : IRemoteAnimationRunner.Stub() {
         private val context = controller.launchContainer.context
-        internal val delegate: AnimationDelegate
+
+        // This is being passed across IPC boundaries and cycles (through PendingIntentRecords,
+        // etc.) are possible. So we need to make sure we drop any references that might
+        // transitively cause leaks when we're done with animation.
+        @VisibleForTesting var delegate: AnimationDelegate?
 
         init {
             delegate =
-                AnimationDelegate(controller, callback, listener, launchAnimator, disableWmTimeout)
+                AnimationDelegate(
+                    controller,
+                    callback,
+                    DelegatingAnimationCompletionListener(listener, this::dispose),
+                    launchAnimator,
+                    disableWmTimeout
+                )
         }
 
         @BinderThread
@@ -451,14 +510,33 @@
             nonApps: Array<out RemoteAnimationTarget>?,
             finishedCallback: IRemoteAnimationFinishedCallback?
         ) {
+            val delegate = delegate
             context.mainExecutor.execute {
-                delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback)
+                if (delegate == null) {
+                    Log.i(TAG, "onAnimationStart called after completion")
+                    // Animation started too late and timed out already. We need to still
+                    // signal back that we're done with it.
+                    finishedCallback?.onAnimationFinished()
+                } else {
+                    delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback)
+                }
             }
         }
 
         @BinderThread
         override fun onAnimationCancelled() {
-            context.mainExecutor.execute { delegate.onAnimationCancelled() }
+            val delegate = delegate
+            context.mainExecutor.execute {
+                delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion")
+                delegate?.onAnimationCancelled()
+            }
+        }
+
+        @AnyThread
+        fun dispose() {
+            // Drop references to animation controller once we're done with the animation
+            // to avoid leaking.
+            context.mainExecutor.execute { delegate = null }
         }
     }
 
@@ -584,6 +662,7 @@
                     )
                 }
                 controller.onLaunchAnimationCancelled()
+                listener?.onLaunchAnimationCancelled()
                 return
             }
 
@@ -821,6 +900,7 @@
                 Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
             }
             controller.onLaunchAnimationCancelled()
+            listener?.onLaunchAnimationCancelled()
         }
 
         @UiThread
@@ -842,6 +922,7 @@
                 )
             }
             controller.onLaunchAnimationCancelled()
+            listener?.onLaunchAnimationCancelled()
         }
 
         private fun IRemoteAnimationFinishedCallback.invoke() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index bd3706e..00d9056 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -70,8 +70,10 @@
          * If a new layout change happens while an animation is already in progress, the animation
          * is updated to continue from the current values to the new end state.
          *
-         * A set of [excludedViews] can be passed. If any dependent view from [rootView] matches an
-         * entry in this set, changes to that view will not be animated.
+         * By default, child views whole layout changes are animated as well. However, this can be
+         * controlled by [animateChildren]. If children are included, a set of [excludedViews] can
+         * be passed. If any dependent view from [rootView] matches an entry in this set, changes to
+         * that view will not be animated.
          *
          * The animator continues to respond to layout changes until [stopAnimating] is called.
          *
@@ -86,6 +88,7 @@
             rootView: View,
             interpolator: Interpolator = DEFAULT_INTERPOLATOR,
             duration: Long = DEFAULT_DURATION,
+            animateChildren: Boolean = true,
             excludedViews: Set<View> = emptySet()
         ): Boolean {
             return animate(
@@ -93,6 +96,7 @@
                 interpolator,
                 duration,
                 ephemeral = false,
+                animateChildren = animateChildren,
                 excludedViews = excludedViews
             )
         }
@@ -106,6 +110,7 @@
             rootView: View,
             interpolator: Interpolator = DEFAULT_INTERPOLATOR,
             duration: Long = DEFAULT_DURATION,
+            animateChildren: Boolean = true,
             excludedViews: Set<View> = emptySet()
         ): Boolean {
             return animate(
@@ -113,6 +118,7 @@
                 interpolator,
                 duration,
                 ephemeral = true,
+                animateChildren = animateChildren,
                 excludedViews = excludedViews
             )
         }
@@ -122,6 +128,7 @@
             interpolator: Interpolator,
             duration: Long,
             ephemeral: Boolean,
+            animateChildren: Boolean,
             excludedViews: Set<View> = emptySet()
         ): Boolean {
             if (
@@ -137,7 +144,13 @@
             }
 
             val listener = createUpdateListener(interpolator, duration, ephemeral)
-            addListener(rootView, listener, recursive = true, excludedViews = excludedViews)
+            addListener(
+                rootView,
+                listener,
+                recursive = true,
+                animateChildren = animateChildren,
+                excludedViews = excludedViews
+            )
             return true
         }
 
@@ -940,6 +953,7 @@
             view: View,
             listener: View.OnLayoutChangeListener,
             recursive: Boolean = false,
+            animateChildren: Boolean = true,
             excludedViews: Set<View> = emptySet()
         ) {
             if (excludedViews.contains(view)) return
@@ -952,12 +966,13 @@
 
             view.addOnLayoutChangeListener(listener)
             view.setTag(R.id.tag_layout_listener, listener)
-            if (view is ViewGroup && recursive) {
+            if (animateChildren && view is ViewGroup && recursive) {
                 for (i in 0 until view.childCount) {
                     addListener(
                         view.getChildAt(i),
                         listener,
                         recursive = true,
+                        animateChildren = animateChildren,
                         excludedViews = excludedViews
                     )
                 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
index 88944f10..60c3fd3 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -108,7 +108,7 @@
 ) {
     val fromScene = layoutImpl.state.transitionState.currentScene
     val isUserInput =
-        (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
+        (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
             ?: false
 
     val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
@@ -119,9 +119,23 @@
     val targetProgress = if (reversed) 0f else 1f
     val transition =
         if (reversed) {
-            OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
+            OneOffTransition(
+                fromScene = target,
+                toScene = fromScene,
+                currentScene = target,
+                isUserInput,
+                isUserInputOngoing = false,
+                animatable,
+            )
         } else {
-            OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
+            OneOffTransition(
+                fromScene = fromScene,
+                toScene = target,
+                currentScene = target,
+                isUserInput,
+                isUserInputOngoing = false,
+                animatable,
+            )
         }
 
     // Change the current layout state to use this new transition.
@@ -142,7 +156,8 @@
     override val fromScene: SceneKey,
     override val toScene: SceneKey,
     override val currentScene: SceneKey,
-    override val isUserInputDriven: Boolean,
+    override val isInitiatedByUserInput: Boolean,
+    override val isUserInputOngoing: Boolean,
     private val animatable: Animatable<Float, AnimationVector1D>,
 ) : TransitionState.Transition {
     override val progress: Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
index a62c984..3bcd920f 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
@@ -48,28 +48,40 @@
 /** An element on screen, that can be composed in one or more scenes. */
 internal class Element(val key: ElementKey) {
     /**
-     * The last offset assigned to this element, relative to the SceneTransitionLayout containing
-     * it.
+     * The last values of this element, coming from any scene. Note that this value will be unstable
+     * if this element is present in multiple scenes but the shared element animation is disabled,
+     * given that multiple instances of the element with different states will write to these
+     * values. You should prefer using [TargetValues.lastValues] in the current scene if it is
+     * defined.
      */
-    var lastOffset = Offset.Unspecified
-
-    /** The last size assigned to this element. */
-    var lastSize = SizeUnspecified
-
-    /** The last alpha assigned to this element. */
-    var lastAlpha = 1f
+    val lastSharedValues = Values()
 
     /** The mapping between a scene and the values/state this element has in that scene, if any. */
-    val sceneValues = SnapshotStateMap<SceneKey, SceneValues>()
+    val sceneValues = SnapshotStateMap<SceneKey, TargetValues>()
 
     override fun toString(): String {
         return "Element(key=$key)"
     }
 
+    /** The current values of this element, either in a specific scene or in a shared context. */
+    class Values {
+        /** The offset of the element, relative to the SceneTransitionLayout containing it. */
+        var offset = Offset.Unspecified
+
+        /** The size of this element. */
+        var size = SizeUnspecified
+
+        /** The alpha of this element. */
+        var alpha = AlphaUnspecified
+    }
+
     /** The target values of this element in a given scene. */
-    class SceneValues {
-        var size by mutableStateOf(SizeUnspecified)
-        var offset by mutableStateOf(Offset.Unspecified)
+    class TargetValues {
+        val lastValues = Values()
+
+        var targetSize by mutableStateOf(SizeUnspecified)
+        var targetOffset by mutableStateOf(Offset.Unspecified)
+
         val sharedValues = SnapshotStateMap<ValueKey, SharedValue<*>>()
     }
 
@@ -80,6 +92,7 @@
 
     companion object {
         val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
+        val AlphaUnspecified = Float.MIN_VALUE
     }
 }
 
@@ -91,7 +104,7 @@
     scene: Scene,
     key: ElementKey,
 ): Modifier {
-    val sceneValues = remember(scene, key) { Element.SceneValues() }
+    val sceneValues = remember(scene, key) { Element.TargetValues() }
     val element =
         // Get the element associated to [key] if it was already composed in another scene,
         // otherwise create it and add it to our Map<ElementKey, Element>. This is done inside a
@@ -108,6 +121,8 @@
 
             element
         }
+    val lastSharedValues = element.lastSharedValues
+    val lastSceneValues = sceneValues.lastValues
 
     DisposableEffect(scene, sceneValues, element) {
         onDispose {
@@ -121,13 +136,14 @@
     }
 
     val alpha =
-        remember(layoutImpl, element, scene) {
-            derivedStateOf { elementAlpha(layoutImpl, element, scene) }
+        remember(layoutImpl, element, scene, sceneValues) {
+            derivedStateOf { elementAlpha(layoutImpl, element, scene, sceneValues) }
         }
     val isOpaque by remember(alpha) { derivedStateOf { alpha.value == 1f } }
     SideEffect {
-        if (isOpaque && element.lastAlpha != 1f) {
-            element.lastAlpha = 1f
+        if (isOpaque) {
+            lastSharedValues.alpha = 1f
+            lastSceneValues.alpha = 1f
         }
     }
 
@@ -148,7 +164,8 @@
             Modifier.graphicsLayer {
                 val alpha = alpha.value
                 this.alpha = alpha
-                element.lastAlpha = alpha
+                lastSharedValues.alpha = alpha
+                lastSceneValues.alpha = alpha
             }
         }
         .testTag(key.testTag)
@@ -220,7 +237,7 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValues: Element.SceneValues,
+    sceneValues: Element.TargetValues,
 ): Modifier {
     when (val state = layoutImpl.state.transitionState) {
         is TransitionState.Idle -> return this
@@ -248,7 +265,8 @@
 private fun elementAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    scene: Scene
+    scene: Scene,
+    sceneValues: Element.TargetValues,
 ): Float {
     return computeValue(
             layoutImpl,
@@ -258,7 +276,11 @@
             transformation = { it.alpha },
             idleValue = 1f,
             currentValue = { 1f },
-            lastValue = { element.lastAlpha },
+            lastValue = {
+                sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified }
+                    ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified }
+                        ?: 1f
+            },
             ::lerp,
         )
         .coerceIn(0f, 1f)
@@ -269,15 +291,15 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValues: Element.SceneValues,
+    sceneValues: Element.TargetValues,
     measurable: Measurable,
     constraints: Constraints,
 ): Placeable {
     // Update the size this element has in this scene when idle.
     val targetSizeInScene = lookaheadSize
-    if (targetSizeInScene != sceneValues.size) {
+    if (targetSizeInScene != sceneValues.targetSize) {
         // TODO(b/290930950): Better handle when this changes to avoid instant size jumps.
-        sceneValues.size = targetSizeInScene
+        sceneValues.targetSize = targetSizeInScene
     }
 
     // Some lambdas called (max once) by computeValue() will need to measure [measurable], in which
@@ -292,17 +314,14 @@
             layoutImpl,
             scene,
             element,
-            sceneValue = { it.size },
+            sceneValue = { it.targetSize },
             transformation = { it.size },
             idleValue = lookaheadSize,
             currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() },
             lastValue = {
-                val lastSize = element.lastSize
-                if (lastSize != Element.SizeUnspecified) {
-                    lastSize
-                } else {
-                    measurable.measure(constraints).also { maybePlaceable = it }.size()
-                }
+                sceneValues.lastValues.size.takeIf { it != Element.SizeUnspecified }
+                    ?: element.lastSharedValues.size.takeIf { it != Element.SizeUnspecified }
+                        ?: measurable.measure(constraints).also { maybePlaceable = it }.size()
             },
             ::lerp,
         )
@@ -316,7 +335,9 @@
                 )
             )
 
-    element.lastSize = placeable.size()
+    val size = placeable.size()
+    element.lastSharedValues.size = size
+    sceneValues.lastValues.size = size
     return placeable
 }
 
@@ -325,7 +346,7 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValues: Element.SceneValues,
+    sceneValues: Element.TargetValues,
     placeable: Placeable,
     placementScope: Placeable.PlacementScope,
 ) {
@@ -334,9 +355,9 @@
         // when idle.
         val coords = coordinates!!
         val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)
-        if (targetOffsetInScene != sceneValues.offset) {
+        if (targetOffsetInScene != sceneValues.targetOffset) {
             // TODO(b/290930950): Better handle when this changes to avoid instant offset jumps.
-            sceneValues.offset = targetOffsetInScene
+            sceneValues.targetOffset = targetOffsetInScene
         }
 
         val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
@@ -345,22 +366,20 @@
                 layoutImpl,
                 scene,
                 element,
-                sceneValue = { it.offset },
+                sceneValue = { it.targetOffset },
                 transformation = { it.offset },
                 idleValue = targetOffsetInScene,
                 currentValue = { currentOffset },
                 lastValue = {
-                    val lastValue = element.lastOffset
-                    if (lastValue.isSpecified) {
-                        lastValue
-                    } else {
-                        currentOffset
-                    }
+                    sceneValues.lastValues.offset.takeIf { it.isSpecified }
+                        ?: element.lastSharedValues.offset.takeIf { it.isSpecified }
+                            ?: currentOffset
                 },
                 ::lerp,
             )
 
-        element.lastOffset = targetOffset
+        element.lastSharedValues.offset = targetOffset
+        sceneValues.lastValues.offset = targetOffset
         placeable.place((targetOffset - currentOffset).round())
     }
 }
@@ -391,7 +410,7 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValue: (Element.SceneValues) -> T,
+    sceneValue: (Element.TargetValues) -> T,
     transformation: (ElementTransformations) -> PropertyTransformation<T>?,
     idleValue: T,
     currentValue: () -> T,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index ccdec6e..1b79dbd 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -52,7 +52,14 @@
          * scene, this value will remain true after the pointer is no longer touching the screen and
          * will be true in any transition created to animate back to the original position.
          */
-        val isUserInputDriven: Boolean,
+        val isInitiatedByUserInput: Boolean,
+
+        /**
+         * Whether user input is currently driving the transition. For example, if a user is
+         * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+         * the transition completes/settles.
+         */
+        val isUserInputOngoing: Flow<Boolean>,
     ) : ObservableTransitionState()
 }
 
@@ -73,7 +80,8 @@
                             fromScene = state.fromScene,
                             toScene = state.toScene,
                             progress = snapshotFlow { state.progress },
-                            isUserInputDriven = state.isUserInputDriven,
+                            isInitiatedByUserInput = state.isInitiatedByUserInput,
+                            isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
                         )
                     }
                 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 39173d9..58c7bdb 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -130,10 +130,23 @@
 sealed interface UserAction
 
 /** The user navigated back, either using a gesture or by triggering a KEYCODE_BACK event. */
-object Back : UserAction
+data object Back : UserAction
 
 /** The user swiped on the container. */
-enum class Swipe : UserAction {
+data class Swipe(
+    val direction: SwipeDirection,
+    val pointerCount: Int = 1,
+    val fromEdge: Edge? = null,
+) : UserAction {
+    companion object {
+        val Left = Swipe(SwipeDirection.Left)
+        val Up = Swipe(SwipeDirection.Up)
+        val Right = Swipe(SwipeDirection.Right)
+        val Down = Swipe(SwipeDirection.Down)
+    }
+}
+
+enum class SwipeDirection {
     Up,
     Down,
     Left,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 7a21211..b9f83c5 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -70,6 +70,9 @@
         val progress: Float
 
         /** Whether the transition was triggered by user input rather than being programmatic. */
-        val isUserInputDriven: Boolean
+        val isInitiatedByUserInput: Boolean
+
+        /** Whether user input is currently driving the transition. */
+        val isUserInputOngoing: Boolean
     }
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
index 1cbfe30..e275fca 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -66,7 +66,7 @@
     // swipe in the other direction.
     val startDragImmediately =
         state == transition &&
-            transition.isAnimatingOffset &&
+            !transition.isUserInputOngoing &&
             !currentScene.shouldEnableSwipes(orientation.opposite())
 
     // The velocity threshold at which the intent of the user is to swipe up or down. It is the same
@@ -126,7 +126,7 @@
 
     override val progress: Float
         get() {
-            val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
+            val offset = if (isUserInputOngoing) dragOffset else offsetAnimatable.value
             if (distance == 0f) {
                 // This can happen only if fromScene == toScene.
                 error(
@@ -137,16 +137,15 @@
             return offset / distance
         }
 
-    override val isUserInputDriven = true
+    override val isInitiatedByUserInput = true
+
+    var _isUserInputOngoing by mutableStateOf(false)
+    override val isUserInputOngoing: Boolean
+        get() = _isUserInputOngoing
 
     /** The current offset caused by the drag gesture. */
     var dragOffset by mutableFloatStateOf(0f)
 
-    /**
-     * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture.
-     */
-    var isAnimatingOffset by mutableStateOf(false)
-
     /** The animatable used to animate the offset once the user lifted its finger. */
     val offsetAnimatable = Animatable(0f, visibilityThreshold = OffsetVisibilityThreshold)
 
@@ -209,9 +208,11 @@
     transition: SwipeTransition,
     orientation: Orientation,
 ) {
+    transition._isUserInputOngoing = true
+
     if (layoutImpl.state.transitionState == transition) {
         // This [transition] was already driving the animation: simply take over it.
-        if (transition.isAnimatingOffset) {
+        if (!transition.isUserInputOngoing) {
             // Stop animating and start from where the current offset. Setting the animation job to
             // `null` will effectively cancel the animation.
             transition.stopOffsetAnimation()
@@ -456,30 +457,29 @@
 ) {
     transition.startOffsetAnimation {
         launch {
-                if (!transition.isAnimatingOffset) {
-                    transition.offsetAnimatable.snapTo(transition.dragOffset)
-                }
-                transition.isAnimatingOffset = true
-
-                transition.offsetAnimatable.animateTo(
-                    targetOffset,
-                    // TODO(b/290184746): Make this spring spec configurable.
-                    spring(
-                        stiffness = Spring.StiffnessMediumLow,
-                        visibilityThreshold = OffsetVisibilityThreshold
-                    ),
-                    initialVelocity = initialVelocity,
-                )
-
-                // Now that the animation is done, the state should be idle. Note that if the state
-                // was changed since this animation started, some external code changed it and we
-                // shouldn't do anything here. Note also that this job will be cancelled in the case
-                // where the user intercepts this swipe.
-                if (layoutImpl.state.transitionState == transition) {
-                    layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
-                }
+            if (transition.isUserInputOngoing) {
+                transition.offsetAnimatable.snapTo(transition.dragOffset)
             }
-            .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } }
+            transition._isUserInputOngoing = false
+
+            transition.offsetAnimatable.animateTo(
+                targetOffset,
+                // TODO(b/290184746): Make this spring spec configurable.
+                spring(
+                    stiffness = Spring.StiffnessMediumLow,
+                    visibilityThreshold = OffsetVisibilityThreshold
+                ),
+                initialVelocity = initialVelocity,
+            )
+
+            // Now that the animation is done, the state should be idle. Note that if the state
+            // was changed since this animation started, some external code changed it and we
+            // shouldn't do anything here. Note also that this job will be cancelled in the case
+            // where the user intercepts this swipe.
+            if (layoutImpl.state.transitionState == transition) {
+                layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
+            }
+        }
     }
 }
 
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index d4ed697..95385d5 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -34,12 +34,12 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
         fun anchorSizeIn(scene: SceneKey): IntSize {
-            val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.size
+            val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.targetSize
             return if (size != null && size != Element.SizeUnspecified) {
                 size
             } else {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 8a5bd74..a1d6319 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -35,13 +35,13 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
         val anchor = layoutImpl.elements[anchor] ?: return value
         fun anchorOffsetIn(scene: SceneKey): Offset? {
-            return anchor.sceneValues[scene]?.offset?.takeIf { it.isSpecified }
+            return anchor.sceneValues[scene]?.targetOffset?.takeIf { it.isSpecified }
         }
 
         // [element] will move the same amount as [anchor] does.
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 5cdce94..840800d 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -34,12 +34,12 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: Offset
     ): Offset {
         val sceneSize = scene.size
-        val elementSize = sceneValues.size
+        val elementSize = sceneValues.targetSize
         if (elementSize == Element.SizeUnspecified) {
             return value
         }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt
index 0a5ac54..17032dc 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -30,7 +30,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: Float
     ): Float {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt
index 73b366e..62d67f0 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt
@@ -47,14 +47,14 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
     ): Modifier {
         return drawWithContent {
             val bounds = layoutImpl.elements[bounds]
             if (
                 bounds == null ||
-                    bounds.lastSize == Element.SizeUnspecified ||
-                    bounds.lastOffset == Offset.Unspecified
+                    bounds.lastSharedValues.size == Element.SizeUnspecified ||
+                    bounds.lastSharedValues.offset == Offset.Unspecified
             ) {
                 drawContent()
                 return@drawWithContent
@@ -64,7 +64,7 @@
                 canvas.withSaveLayer(size.toRect(), Paint()) {
                     drawContent()
 
-                    val offset = bounds.lastOffset - element.lastOffset
+                    val offset = bounds.lastSharedValues.offset - element.lastSharedValues.offset
                     translate(offset.x, offset.y) { drawHole(bounds) }
                 }
             }
@@ -72,7 +72,7 @@
     }
 
     private fun DrawScope.drawHole(bounds: Element) {
-        val boundsSize = bounds.lastSize.toSize()
+        val boundsSize = bounds.lastSharedValues.size.toSize()
         if (shape == RectangleShape) {
             drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut)
             return
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index ce754dc..233ae59 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -37,7 +37,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
index a650254..2ef8d56 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -59,7 +59,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
     ): Modifier
 }
 
@@ -74,7 +74,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: T,
     ): T
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt
index 8abca61..864b937 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -35,7 +35,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.SceneValues,
+        sceneValues: Element.TargetValues,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 52c6385..8e9c586 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -43,7 +43,7 @@
         "androidx.compose.ui_ui-test-junit4",
         "androidx.compose.ui_ui-test-manifest",
 
-        "truth-prebuilt",
+        "truth",
     ],
 
     kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 2232370..53ed2b5 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -122,7 +122,8 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
+        assertThat(transition.isUserInputOngoing).isTrue()
 
         // Release the finger. We should now be animating back to A (currentScene = SceneA) given
         // that 55dp < positional threshold.
@@ -134,7 +135,8 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
+        assertThat(transition.isUserInputOngoing).isFalse()
 
         // Wait for the animation to finish. We should now be in scene A.
         rule.waitForIdle()
@@ -156,7 +158,8 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
+        assertThat(transition.isUserInputOngoing).isTrue()
 
         // Release the finger. We should now be animating to C (currentScene = SceneC) given
         // that 56dp >= positional threshold.
@@ -168,7 +171,8 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
+        assertThat(transition.isUserInputOngoing).isFalse()
 
         // Wait for the animation to finish. We should now be in scene C.
         rule.waitForIdle()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index a9944f7..f2b7b32 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -144,15 +144,17 @@
         }
 
         val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()
+        val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
 
-        when (windowSizeClass.widthSizeClass) {
-            WindowWidthSizeClass.Expanded ->
+        when {
+            windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded ->
                 SideBySide(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
                     modifier = childModifier,
                 )
-            WindowWidthSizeClass.Medium ->
+            isFullScreenUserSwitcherEnabled &&
+                windowSizeClass.widthSizeClass == WindowWidthSizeClass.Medium ->
                 Stacked(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
@@ -442,14 +444,22 @@
                 label = "offset",
             )
 
-        UserSwitcher(
-            viewModel = viewModel,
-            modifier =
-                Modifier.fillMaxHeight().weight(1f).graphicsLayer {
-                    translationX = size.width * animatedOffset
-                    alpha = animatedAlpha(animatedOffset)
-                },
-        )
+        val userSwitcherModifier =
+            Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+                translationX = size.width * animatedOffset
+                alpha = animatedAlpha(animatedOffset)
+            }
+        if (viewModel.isUserSwitcherVisible) {
+            UserSwitcher(
+                viewModel = viewModel,
+                modifier = userSwitcherModifier,
+            )
+        } else {
+            Box(
+                modifier = userSwitcherModifier,
+            )
+        }
+
         Box(
             modifier =
                 Modifier.fillMaxHeight().weight(1f).graphicsLayer {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index c3a3752..ee310ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -36,13 +36,14 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.isVisible
 import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.qualifiers.KeyguardRootView
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
@@ -99,7 +100,9 @@
         return buildMap {
             up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) }
             left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) }
-            this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade)
+            this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] =
+                SceneModel(SceneKey.QuickSettings)
+            this[UserAction.Swipe(direction = Direction.DOWN)] = SceneModel(SceneKey.Shade)
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 1f9c3e6..a33eac5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -23,11 +23,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.footer.ui.compose.QuickSettings
@@ -37,6 +39,7 @@
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
 import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -98,12 +101,22 @@
                 .clickable(onClick = { viewModel.onContentClicked() })
                 .padding(start = 16.dp, end = 16.dp, bottom = 48.dp)
     ) {
-        ExpandedShadeHeader(
-            viewModel = viewModel.shadeHeaderViewModel,
-            createTintedIconManager = createTintedIconManager,
-            createBatteryMeterViewController = createBatteryMeterViewController,
-            statusBarIconController = statusBarIconController,
-        )
+        when (LocalWindowSizeClass.current.widthSizeClass) {
+            WindowWidthSizeClass.Compact ->
+                ExpandedShadeHeader(
+                    viewModel = viewModel.shadeHeaderViewModel,
+                    createTintedIconManager = createTintedIconManager,
+                    createBatteryMeterViewController = createBatteryMeterViewController,
+                    statusBarIconController = statusBarIconController,
+                )
+            else ->
+                CollapsedShadeHeader(
+                    viewModel = viewModel.shadeHeaderViewModel,
+                    createTintedIconManager = createTintedIconManager,
+                    createBatteryMeterViewController = createBatteryMeterViewController,
+                    statusBarIconController = statusBarIconController,
+                )
+        }
         Spacer(modifier = Modifier.height(16.dp))
         QuickSettings()
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 2ee461f..f35ea83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -22,6 +22,7 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
@@ -41,7 +42,12 @@
     override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
-                    UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
+                    UserAction.Swipe(
+                        pointerCount = 2,
+                        fromEdge = Edge.TOP,
+                        direction = Direction.DOWN,
+                    ) to SceneModel(SceneKey.QuickSettings),
+                    UserAction.Swipe(direction = Direction.DOWN) to SceneModel(SceneKey.Shade),
                 )
             )
             .asStateFlow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index ef01266..0da562b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -35,15 +35,18 @@
 import androidx.compose.ui.input.pointer.motionEventSpy
 import androidx.compose.ui.input.pointer.pointerInput
 import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Edge as SceneTransitionEdge
 import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
 import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
@@ -158,7 +161,8 @@
                 fromScene = fromScene.toModel().key,
                 toScene = toScene.toModel().key,
                 progress = progress,
-                isUserInputDriven = isUserInputDriven,
+                isInitiatedByUserInput = isInitiatedByUserInput,
+                isUserInputOngoing = isUserInputOngoing,
             )
     }
 }
@@ -180,12 +184,24 @@
 private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction {
     return when (this) {
         is UserAction.Swipe ->
-            when (this.direction) {
-                Direction.LEFT -> Swipe.Left
-                Direction.UP -> Swipe.Up
-                Direction.RIGHT -> Swipe.Right
-                Direction.DOWN -> Swipe.Down
-            }
+            Swipe(
+                pointerCount = pointerCount,
+                fromEdge =
+                    when (this.fromEdge) {
+                        null -> null
+                        Edge.LEFT -> SceneTransitionEdge.Left
+                        Edge.TOP -> SceneTransitionEdge.Top
+                        Edge.RIGHT -> SceneTransitionEdge.Right
+                        Edge.BOTTOM -> SceneTransitionEdge.Bottom
+                    },
+                direction =
+                    when (this.direction) {
+                        Direction.LEFT -> SwipeDirection.Left
+                        Direction.UP -> SwipeDirection.Up
+                        Direction.RIGHT -> SwipeDirection.Right
+                        Direction.DOWN -> SwipeDirection.Down
+                    }
+            )
         is UserAction.Back -> Back
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 6629a25..591fa76 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -30,6 +30,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.derivedStateOf
@@ -50,6 +51,7 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.animation.scene.animateSharedFloatAsState
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.settingslib.Utils
 import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.battery.BatteryMeterViewController
@@ -98,12 +100,12 @@
             ShadeHeader.Keys.transitionProgress,
             ShadeHeader.Elements.FormatPlaceholder
         )
-    val useExpandedFormat by
-        remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
 
     val cutoutWidth = LocalDisplayCutout.current.width()
     val cutoutLocation = LocalDisplayCutout.current.location
 
+    val useExpandedFormat = formatProgress.value > 0.5f || cutoutLocation != CutoutLocation.CENTER
+
     // This layout assumes it is globally positioned at (0, 0) and is the
     // same size as the screen.
     Layout(
@@ -131,6 +133,14 @@
                 {
                     Row(horizontalArrangement = Arrangement.End) {
                         SystemIconContainer {
+                            when (LocalWindowSizeClass.current.widthSizeClass) {
+                                WindowWidthSizeClass.Medium,
+                                WindowWidthSizeClass.Expanded ->
+                                    ShadeCarrierGroup(
+                                        viewModel = viewModel,
+                                        modifier = Modifier.align(Alignment.CenterVertically),
+                                    )
+                            }
                             StatusIcons(
                                 viewModel = viewModel,
                                 createTintedIconManager = createTintedIconManager,
diff --git a/packages/SystemUI/customization/res/values-af/strings.xml b/packages/SystemUI/customization/res/values-af/strings.xml
new file mode 100644
index 0000000..4c2c627
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-af/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Verstek vir digitaal"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-am/strings.xml b/packages/SystemUI/customization/res/values-am/strings.xml
new file mode 100644
index 0000000..847d7a5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-am/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ዲጂታል ነባሪ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ar/strings.xml b/packages/SystemUI/customization/res/values-ar/strings.xml
new file mode 100644
index 0000000..57d1612
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ar/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"رقمية تلقائية"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-as/strings.xml b/packages/SystemUI/customization/res/values-as/strings.xml
new file mode 100644
index 0000000..2f3b64b
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-as/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটেল ডিফ’ল্ট"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-az/strings.xml b/packages/SystemUI/customization/res/values-az/strings.xml
new file mode 100644
index 0000000..eb52f95
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-az/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Rəqəmsal defolt"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..90e6678
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitalni podrazumevani"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-be/strings.xml b/packages/SystemUI/customization/res/values-be/strings.xml
new file mode 100644
index 0000000..f327da2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-be/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"электронны, стандартны шрыфт"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-bg/strings.xml b/packages/SystemUI/customization/res/values-bg/strings.xml
new file mode 100644
index 0000000..6e3754a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-bg/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Стандартно дигитално"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-bn/strings.xml b/packages/SystemUI/customization/res/values-bn/strings.xml
new file mode 100644
index 0000000..adf1256
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-bn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটাল ডিফল্ট"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-bs/strings.xml b/packages/SystemUI/customization/res/values-bs/strings.xml
new file mode 100644
index 0000000..8de04ab
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-bs/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitalno zadano"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ca/strings.xml b/packages/SystemUI/customization/res/values-ca/strings.xml
new file mode 100644
index 0000000..967bb3f
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ca/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminat"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-cs/strings.xml b/packages/SystemUI/customization/res/values-cs/strings.xml
new file mode 100644
index 0000000..45da4d7
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-cs/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitální výchozí"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-da/strings.xml b/packages/SystemUI/customization/res/values-da/strings.xml
new file mode 100644
index 0000000..3ffaa8b
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-da/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Standard (digital)"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-de/strings.xml b/packages/SystemUI/customization/res/values-de/strings.xml
new file mode 100644
index 0000000..64f95e0
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-de/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital (Standard)"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-el/strings.xml b/packages/SystemUI/customization/res/values-el/strings.xml
new file mode 100644
index 0000000..57134b5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-el/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Ψηφιακή προεπιλογή"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-en-rAU/strings.xml b/packages/SystemUI/customization/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..a6110d5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-en-rAU/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-en-rCA/strings.xml b/packages/SystemUI/customization/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..79919c0
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-en-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for clock_default_description (5309401440896597541) -->
+    <skip />
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-en-rGB/strings.xml b/packages/SystemUI/customization/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..a6110d5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-en-rGB/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-en-rIN/strings.xml b/packages/SystemUI/customization/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..a6110d5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-en-rIN/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-en-rXC/strings.xml b/packages/SystemUI/customization/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..7c540da
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-en-rXC/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‏‎Digital default‎‏‎‎‏‎"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-es-rUS/strings.xml b/packages/SystemUI/customization/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..59be786
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-es-rUS/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Configuración predeterminada digital"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-es/strings.xml b/packages/SystemUI/customization/res/values-es/strings.xml
new file mode 100644
index 0000000..03ef0e5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-es/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminada"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-et/strings.xml b/packages/SystemUI/customization/res/values-et/strings.xml
new file mode 100644
index 0000000..fec793e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-et/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitaalne vaikimisi"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-eu/strings.xml b/packages/SystemUI/customization/res/values-eu/strings.xml
new file mode 100644
index 0000000..a70c808
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-eu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital lehenetsia"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-fa/strings.xml b/packages/SystemUI/customization/res/values-fa/strings.xml
new file mode 100644
index 0000000..6426d51
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-fa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"دیجیتال پیش‌فرض"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-fi/strings.xml b/packages/SystemUI/customization/res/values-fi/strings.xml
new file mode 100644
index 0000000..9b19373
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-fi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitaalinen (oletus)"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-fr-rCA/strings.xml b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..bbd1208
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Numérique, par défaut"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-fr/strings.xml b/packages/SystemUI/customization/res/values-fr/strings.xml
new file mode 100644
index 0000000..5579a42
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-fr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Numérique par défaut"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-gl/strings.xml b/packages/SystemUI/customization/res/values-gl/strings.xml
new file mode 100644
index 0000000..2da93c6
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-gl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Predeterminada dixital"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-gu/strings.xml b/packages/SystemUI/customization/res/values-gu/strings.xml
new file mode 100644
index 0000000..c578a2e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-gu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ડિજિટલ ડિફૉલ્ટ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hi/strings.xml b/packages/SystemUI/customization/res/values-hi/strings.xml
new file mode 100644
index 0000000..6080f80
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफ़ॉल्ट"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hr/strings.xml b/packages/SystemUI/customization/res/values-hr/strings.xml
new file mode 100644
index 0000000..0a1440f
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitalni zadani"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hu/strings.xml b/packages/SystemUI/customization/res/values-hu/strings.xml
new file mode 100644
index 0000000..32618a8
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitális, alapértelmezett"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hy/strings.xml b/packages/SystemUI/customization/res/values-hy/strings.xml
new file mode 100644
index 0000000..d45afbf
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hy/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Թվային, կանխադրված"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-in/strings.xml b/packages/SystemUI/customization/res/values-in/strings.xml
new file mode 100644
index 0000000..a6110d5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-in/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-is/strings.xml b/packages/SystemUI/customization/res/values-is/strings.xml
new file mode 100644
index 0000000..5a370d6
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-is/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Stafræn, sjálfgefið"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-it/strings.xml b/packages/SystemUI/customization/res/values-it/strings.xml
new file mode 100644
index 0000000..2a5087d
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-it/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitale - predefinito"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-iw/strings.xml b/packages/SystemUI/customization/res/values-iw/strings.xml
new file mode 100644
index 0000000..ddd28f2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-iw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"דיגיטלי ברירת מחדל"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ja/strings.xml b/packages/SystemUI/customization/res/values-ja/strings.xml
new file mode 100644
index 0000000..744604a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ja/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"デジタル デフォルト"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ka/strings.xml b/packages/SystemUI/customization/res/values-ka/strings.xml
new file mode 100644
index 0000000..88ba1df
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ka/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ციფრული ნაგულისხმევი"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-kk/strings.xml b/packages/SystemUI/customization/res/values-kk/strings.xml
new file mode 100644
index 0000000..9ee6522
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-kk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Цифрлық әдепкі"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-km/strings.xml b/packages/SystemUI/customization/res/values-km/strings.xml
new file mode 100644
index 0000000..bbc438a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-km/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"លំនាំដើមឌីជីថល"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-kn/strings.xml b/packages/SystemUI/customization/res/values-kn/strings.xml
new file mode 100644
index 0000000..e67319d
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-kn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ಡಿಜಿಟಲ್ ಡೀಫಾಲ್ಟ್"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ko/strings.xml b/packages/SystemUI/customization/res/values-ko/strings.xml
new file mode 100644
index 0000000..fa9103b
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ko/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"디지털 기본"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ky/strings.xml b/packages/SystemUI/customization/res/values-ky/strings.xml
new file mode 100644
index 0000000..76cc5e2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ky/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Демейки санариптик"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-lo/strings.xml b/packages/SystemUI/customization/res/values-lo/strings.xml
new file mode 100644
index 0000000..28f5000
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-lo/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ດິຈິຕອນຕາມຄ່າເລີ່ມຕົ້ນ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-lt/strings.xml b/packages/SystemUI/customization/res/values-lt/strings.xml
new file mode 100644
index 0000000..2fe7315
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-lt/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Skaitmeninis numatytasis"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-lv/strings.xml b/packages/SystemUI/customization/res/values-lv/strings.xml
new file mode 100644
index 0000000..e0b904a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-lv/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitālais pulkstenis — noklusējums"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-mk/strings.xml b/packages/SystemUI/customization/res/values-mk/strings.xml
new file mode 100644
index 0000000..9b95a6e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-mk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Дигитален стандарден приказ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ml/strings.xml b/packages/SystemUI/customization/res/values-ml/strings.xml
new file mode 100644
index 0000000..7f6be8a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ml/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ഡിജിറ്റൽ ഡിഫോൾട്ട്"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-mn/strings.xml b/packages/SystemUI/customization/res/values-mn/strings.xml
new file mode 100644
index 0000000..38369b6
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-mn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Дижитал өгөгдмөл"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-mr/strings.xml b/packages/SystemUI/customization/res/values-mr/strings.xml
new file mode 100644
index 0000000..821ff10
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-mr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डीफॉल्टसह क्लॉक फेस"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ms/strings.xml b/packages/SystemUI/customization/res/values-ms/strings.xml
new file mode 100644
index 0000000..2f61b47
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ms/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital lalai"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-my/strings.xml b/packages/SystemUI/customization/res/values-my/strings.xml
new file mode 100644
index 0000000..3d137eb
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-my/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ဒစ်ဂျစ်တယ်နာရီ မူရင်း"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-nb/strings.xml b/packages/SystemUI/customization/res/values-nb/strings.xml
new file mode 100644
index 0000000..6eb4373
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-nb/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital – standard"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ne/strings.xml b/packages/SystemUI/customization/res/values-ne/strings.xml
new file mode 100644
index 0000000..c5b0877
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ne/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफल्ट"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-nl/strings.xml b/packages/SystemUI/customization/res/values-nl/strings.xml
new file mode 100644
index 0000000..4f46ab8
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-nl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Standaard digitaal"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-or/strings.xml b/packages/SystemUI/customization/res/values-or/strings.xml
new file mode 100644
index 0000000..a74017f
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-or/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ଡିଜିଟାଲ ଡିଫଲ୍ଟ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-pa/strings.xml b/packages/SystemUI/customization/res/values-pa/strings.xml
new file mode 100644
index 0000000..a77661a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-pa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ਡਿਜੀਟਲ ਡਿਫਾਲਟ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-pl/strings.xml b/packages/SystemUI/customization/res/values-pl/strings.xml
new file mode 100644
index 0000000..6f5b6f2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-pl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Cyfrowa domyślna"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-pt-rPT/strings.xml b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..c6c3cc0
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Predefinição digital"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-pt/strings.xml b/packages/SystemUI/customization/res/values-pt/strings.xml
new file mode 100644
index 0000000..bbe4355
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-pt/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital padrão"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ro/strings.xml b/packages/SystemUI/customization/res/values-ro/strings.xml
new file mode 100644
index 0000000..ef163e9
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ro/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Implicit digital"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ru/strings.xml b/packages/SystemUI/customization/res/values-ru/strings.xml
new file mode 100644
index 0000000..5ee928e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ru/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Цифровые часы, стандартный"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-si/strings.xml b/packages/SystemUI/customization/res/values-si/strings.xml
new file mode 100644
index 0000000..caf9610
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-si/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ඩිජිටල් පෙරනිමිය"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sk/strings.xml b/packages/SystemUI/customization/res/values-sk/strings.xml
new file mode 100644
index 0000000..1843f97
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitálne predvolené"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sl/strings.xml b/packages/SystemUI/customization/res/values-sl/strings.xml
new file mode 100644
index 0000000..12df66f
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digitalna (privzeta)"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sq/strings.xml b/packages/SystemUI/customization/res/values-sq/strings.xml
new file mode 100644
index 0000000..1fc9f25
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sq/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Dixhitale e parazgjedhur"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sr/strings.xml b/packages/SystemUI/customization/res/values-sr/strings.xml
new file mode 100644
index 0000000..6b127c9
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Дигитални подразумевани"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sv/strings.xml b/packages/SystemUI/customization/res/values-sv/strings.xml
new file mode 100644
index 0000000..84ad25c
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sv/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital standard"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sw/strings.xml b/packages/SystemUI/customization/res/values-sw/strings.xml
new file mode 100644
index 0000000..e2ec3de
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Dijitali chaguomsingi"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sw400dp/dimens.xml b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml
new file mode 100644
index 0000000..3c9e078
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+*/
+-->
+<resources>
+    <dimen name="presentation_clock_text_size">170dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ta/strings.xml b/packages/SystemUI/customization/res/values-ta/strings.xml
new file mode 100644
index 0000000..f4eea07
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ta/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"டிஜிட்டல் இயல்பு"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-te/strings.xml b/packages/SystemUI/customization/res/values-te/strings.xml
new file mode 100644
index 0000000..c7c77d5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-te/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"డిజిటల్ ఆటోమేటిక్ సెట్టింగ్"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-th/strings.xml b/packages/SystemUI/customization/res/values-th/strings.xml
new file mode 100644
index 0000000..61d880e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-th/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ดิจิทัลแบบเริ่มต้น"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-tl/strings.xml b/packages/SystemUI/customization/res/values-tl/strings.xml
new file mode 100644
index 0000000..a3484a7
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-tl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Digital na default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-tr/strings.xml b/packages/SystemUI/customization/res/values-tr/strings.xml
new file mode 100644
index 0000000..a90e985
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-tr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Dijital varsayılan"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-uk/strings.xml b/packages/SystemUI/customization/res/values-uk/strings.xml
new file mode 100644
index 0000000..ee9b77b
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-uk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Цифровий, стандартний"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ur/strings.xml b/packages/SystemUI/customization/res/values-ur/strings.xml
new file mode 100644
index 0000000..06a6a7c
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ur/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"ڈیجیٹل ڈیفالٹ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-uz/strings.xml b/packages/SystemUI/customization/res/values-uz/strings.xml
new file mode 100644
index 0000000..6b31b04
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-uz/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Raqamli soat, standart"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-vi/strings.xml b/packages/SystemUI/customization/res/values-vi/strings.xml
new file mode 100644
index 0000000..830b6e2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-vi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Mặt số mặc định"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-zh-rCN/strings.xml b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..747567e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"默认数字"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-zh-rHK/strings.xml b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..c19cc68
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"數碼時鐘 (預設)"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-zh-rTW/strings.xml b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..6fcd313
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"數位預設"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-zu/strings.xml b/packages/SystemUI/customization/res/values-zu/strings.xml
new file mode 100644
index 0000000..c87c250a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-zu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+        <string name="clock_default_description" msgid="5309401440896597541">"Okuzenzakalelayo kwedijithali"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
index 8eb8132..c574d1f 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/res/values/dimens.xml
@@ -17,6 +17,7 @@
 -->
 <resources>
     <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
+    <dimen name="presentation_clock_text_size">50dp</dimen>
     <dimen name="large_clock_text_size">150dp</dimen>
     <dimen name="small_clock_text_size">86dp</dimen>
 
diff --git a/packages/SystemUI/customization/res/values/strings.xml b/packages/SystemUI/customization/res/values/strings.xml
new file mode 100644
index 0000000..897c842
--- /dev/null
+++ b/packages/SystemUI/customization/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- default clock face name [CHAR LIMIT=NONE]-->
+    <string name="clock_default_name">Default</string>
+
+    <!-- default clock face description [CHAR LIMIT=NONE]-->
+    <string name="clock_default_description">Digital default</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index b28920c..b076b2c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -65,7 +65,13 @@
     protected var onSecondaryDisplay: Boolean = false
 
     override val events: DefaultClockEvents
-    override val config = ClockConfig(DEFAULT_CLOCK_ID)
+    override val config: ClockConfig by lazy {
+        ClockConfig(
+            DEFAULT_CLOCK_ID,
+            resources.getString(R.string.clock_default_name),
+            resources.getString(R.string.clock_default_description)
+        )
+    }
 
     init {
         val parent = FrameLayout(ctx)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 949641a..dd52e39 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.plugins.ClockSettings
 
 private val TAG = DefaultClockProvider::class.simpleName
-const val DEFAULT_CLOCK_NAME = "Default Clock"
 const val DEFAULT_CLOCK_ID = "DEFAULT"
 
 /** Provides the default system clock */
@@ -35,8 +34,7 @@
     val resources: Resources,
     val hasStepClockAnimation: Boolean = false
 ) : ClockProvider {
-    override fun getClocks(): List<ClockMetadata> =
-        listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
+    override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
 
     override fun createClock(settings: ClockSettings): ClockController {
         if (settings.clockId != DEFAULT_CLOCK_ID) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index e2f4793..485c27e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -192,15 +192,18 @@
 /** Some data about a clock design */
 data class ClockMetadata(
     val clockId: ClockId,
-    val name: String,
-) {
-    constructor(clockId: ClockId) : this(clockId, clockId) {}
-}
+)
 
 /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
 data class ClockConfig(
     val id: String,
 
+    /** Localized name of the clock */
+    val name: String,
+
+    /** Localized accessibility description for the clock */
+    val description: String,
+
     /** Transition to AOD should move smartspace like large clock instead of small clock */
     val useAlternateSmartspaceAODTransition: Boolean = false,
 
diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
new file mode 100644
index 0000000..2187352
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  ~
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/alternate_bouncer"
+    android:focusable="true"
+    android:clickable="true"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.android.systemui.scrim.ScrimView
+        android:id="@+id/alternate_bouncer_scrim"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+    />
+</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
index 593f507f..f68ab81 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
@@ -16,22 +16,30 @@
 */
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/presentation"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <com.android.keyguard.KeyguardStatusView
         android:id="@+id/clock"
-        android:layout_width="410dp"
-        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
         android:layout_gravity="center"
-        android:orientation="vertical">
+        android:orientation="vertical"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
 
         <include
             android:id="@+id/keyguard_clock_container"
             layout="@layout/keyguard_clock_switch"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"/>
     </com.android.keyguard.KeyguardStatusView>
 
-</FrameLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d4b73a4..acee425 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -130,6 +130,11 @@
         android:inflatedId="@+id/multi_shade"
         android:layout="@layout/multi_shade" />
 
+    <include layout="@layout/alternate_bouncer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible" />
+
     <com.android.systemui.biometrics.AuthRippleView
         android:id="@+id/auth_ripple"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 18f24ec..1add90f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -354,9 +354,6 @@
     <!-- Whether to show activity indicators in the status bar -->
     <bool name="config_showActivity">false</bool>
 
-    <!-- Whether or not the button to clear all notifications will be shown. -->
-    <bool name="config_enableNotificationsClearAll">true</bool>
-
     <!-- Whether or not to show the notification shelf that houses the icons of notifications that
      have been scrolled off-screen. -->
     <bool name="config_showNotificationShelf">true</bool>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
new file mode 100644
index 0000000..57a49c8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.dagger.qualifiers
+
+import java.lang.annotation.Documented
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy.RUNTIME
+import javax.inject.Qualifier
+
+/** Annotates a class that is display specific. */
+@Qualifier @Documented @Retention(RUNTIME) annotation class DisplaySpecific
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index c074988..1e89614 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -283,12 +283,28 @@
     }
 
     public void setRotationLockedAtAngle(int rotationSuggestion, String caller) {
-        RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isRotationLocked(),
+        final Boolean isLocked = isRotationLocked();
+        if (isLocked == null) {
+            // Ignore if we can't read the setting for the current user
+            return;
+        }
+        RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isLocked,
                 /* rotation= */ rotationSuggestion, caller);
     }
 
-    public boolean isRotationLocked() {
-        return RotationPolicy.isRotationLocked(mContext);
+    /**
+     * @return whether rotation is currently locked, or <code>null</code> if the setting couldn't
+     *         be read
+     */
+    public Boolean isRotationLocked() {
+        try {
+            return RotationPolicy.isRotationLocked(mContext);
+        } catch (SecurityException e) {
+            // TODO(b/279561841): RotationPolicy uses the current user to resolve the setting which
+            //                    may change before the rotation watcher can be unregistered
+            Log.e(TAG, "Failed to get isRotationLocked", e);
+            return null;
+        }
     }
 
     public void setRotateSuggestionButtonState(boolean visible) {
@@ -462,7 +478,11 @@
 
         // If the screen rotation changes while locked, potentially update lock to flow with
         // new screen rotation and hide any showing suggestions.
-        boolean rotationLocked = isRotationLocked();
+        Boolean rotationLocked = isRotationLocked();
+        if (rotationLocked == null) {
+            // Ignore if we can't read the setting for the current user
+            return;
+        }
         // The isVisible check makes the rotation button disappear when we are not locked
         // (e.g. for tabletop auto-rotate).
         if (rotationLocked || mRotationButton.isVisible()) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 0094820..a6e04ce 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -23,6 +23,7 @@
 import android.window.PictureInPictureSurfaceTransaction;
 import android.window.TaskSnapshot;
 
+import com.android.internal.os.IResultReceiver;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 public class RecentsAnimationControllerCompat {
@@ -89,11 +90,16 @@
      * @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous
      *                          app.
      */
-    public void finish(boolean toHome, boolean sendUserLeaveHint) {
+    public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) {
         try {
-            mAnimationController.finish(toHome, sendUserLeaveHint);
+            mAnimationController.finish(toHome, sendUserLeaveHint, finishCb);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to finish recents animation", e);
+            try {
+                finishCb.send(0, null);
+            } catch (Exception ex) {
+                // Local call, can ignore
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 2f3c1f2..eb7a735 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -205,19 +205,19 @@
     private var isRegistered = false
     private var disposableHandle: DisposableHandle? = null
     private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
-
+    private var largeClockOnSecondaryDisplay = false
 
     private fun updateColors() {
         if (regionSamplingEnabled) {
             clock?.let { clock ->
                 smallRegionSampler?.let {
-                    smallClockIsDark = it.currentRegionDarkness().isDark
-                    clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+                    val isRegionDark = it.currentRegionDarkness().isDark
+                    clock.smallClock.events.onRegionDarknessChanged(isRegionDark)
                 }
 
                 largeRegionSampler?.let {
-                    largeClockIsDark = it.currentRegionDarkness().isDark
-                    clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+                    val isRegionDark = it.currentRegionDarkness().isDark
+                    clock.largeClock.events.onRegionDarknessChanged(isRegionDark)
                 }
             }
             return
@@ -225,12 +225,12 @@
 
         val isLightTheme = TypedValue()
         context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
-        smallClockIsDark = isLightTheme.data == 0
-        largeClockIsDark = isLightTheme.data == 0
+        val isRegionDark = isLightTheme.data == 0
 
         clock?.run {
-            smallClock.events.onRegionDarknessChanged(smallClockIsDark)
-            largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+            Log.i(TAG, "Region isDark: $isRegionDark")
+            smallClock.events.onRegionDarknessChanged(isRegionDark)
+            largeClock.events.onRegionDarknessChanged(isRegionDark)
         }
     }
     protected open fun createRegionSampler(
@@ -260,9 +260,6 @@
         get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD
     private var cachedWeatherData: WeatherData? = null
 
-    private var smallClockIsDark = true
-    private var largeClockIsDark = true
-
     private val configListener =
         object : ConfigurationController.ConfigurationListener {
             override fun onThemeChanged() {
@@ -381,6 +378,19 @@
                 ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
     }
 
+    /**
+     * Sets this clock as showing in a secondary display.
+     *
+     * Not that this is not necessarily needed, as we could get the displayId from [Context]
+     * directly and infere [largeClockOnSecondaryDisplay] from the id being different than the
+     * default display one. However, if we do so, current screenshot tests would not work, as they
+     * pass an activity context always from the default display.
+     */
+    fun setLargeClockOnSecondaryDisplay(onSecondaryDisplay: Boolean) {
+        largeClockOnSecondaryDisplay = onSecondaryDisplay
+        updateFontSizes()
+    }
+
     private fun updateTimeListeners() {
         smallTimeListener?.stop()
         largeTimeListener?.stop()
@@ -403,9 +413,15 @@
             smallClock.events.onFontSettingChanged(
                 resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
             )
-            largeClock.events.onFontSettingChanged(
-                resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
-            )
+            largeClock.events.onFontSettingChanged(getLargeClockSizePx())
+        }
+    }
+
+    private fun getLargeClockSizePx(): Float {
+        return if (largeClockOnSecondaryDisplay) {
+            resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat()
+        } else {
+            resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index d677a15..dec7d79 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -68,10 +68,13 @@
 
         clock = requireViewById(R.id.clock)
         keyguardStatusViewController =
-            keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
-                setDisplayedOnSecondaryDisplay()
-                init()
-            }
+            keyguardStatusViewComponentFactory
+                .build(clock, display)
+                .keyguardStatusViewController
+                .apply {
+                    setDisplayedOnSecondaryDisplay()
+                    init()
+                }
     }
 
     /** [ConnectedDisplayKeyguardPresentation] factory. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ff8e489..3c8301f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -84,7 +84,6 @@
     private final DumpManager mDumpManager;
     private final ClockEventController mClockEventController;
     private final LogBuffer mLogBuffer;
-
     private FrameLayout mSmallClockFrame; // top aligned clock
     private FrameLayout mLargeClockFrame; // centered clock
 
@@ -265,6 +264,7 @@
 
         if (mShownOnSecondaryDisplay) {
             mView.setLargeClockOnSecondaryDisplay(true);
+            mClockEventController.setLargeClockOnSecondaryDisplay(true);
             displayClock(LARGE, /* animate= */ false);
             hideSliceViewAndNotificationIconContainer();
             return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 9c015fe..8a6f101 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -419,7 +419,7 @@
             mClock.post(mMoveTextRunnable);
 
             mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
-                    .build(findViewById(R.id.clock))
+                    .build(findViewById(R.id.clock), getDisplay())
                     .getKeyguardClockSwitchController();
 
             mKeyguardClockSwitchController.setOnlyClock(true);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 51c0676..50be97e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -139,7 +139,7 @@
             case PROMPT_REASON_USER_REQUEST:
                 return R.string.kg_prompt_after_user_lockdown_password;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                return R.string.kg_prompt_unattended_update_password;
+                return R.string.kg_prompt_reason_timeout_password;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
                 return R.string.kg_prompt_reason_timeout_password;
             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 714ba81..57151ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -321,7 +321,7 @@
                 resId = R.string.kg_prompt_after_user_lockdown_pattern;
                 break;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                resId = R.string.kg_prompt_unattended_update_pattern;
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
                 resId = R.string.kg_prompt_reason_timeout_pattern;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 9d6d033..681aa70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -123,7 +123,7 @@
             case PROMPT_REASON_USER_REQUEST:
                 return R.string.kg_prompt_after_user_lockdown_pin;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                return R.string.kg_prompt_unattended_update_pin;
+                return R.string.kg_prompt_reason_timeout_pin;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
                 return R.string.kg_prompt_reason_timeout_pin;
             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 1d2d77f..40d0be1 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
@@ -68,6 +69,7 @@
     private boolean mUseBackground = false;
     private float mDozeAmount = 0f;
 
+    @SuppressLint("ClickableViewAccessibility")
     public LockIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSensorRect = new RectF();
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index a81069a..0d3f726 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -25,9 +25,11 @@
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
+import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -56,11 +58,11 @@
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.biometrics.UdfpsController;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -72,12 +74,16 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.Objects;
 import java.util.function.Consumer;
@@ -99,6 +105,8 @@
     private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
 
+    private static final long FADE_OUT_DURATION_MS = 250L;
+
     private final long mLongPressTimeout;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final KeyguardViewController mKeyguardViewController;
@@ -124,6 +132,8 @@
     @NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
     @NonNull private final KeyguardInteractor mKeyguardInteractor;
     @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
+    @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor;
+    @NonNull private final SceneContainerFlags mSceneContainerFlags;
 
     // Tracks the velocity of a touch to help filter out the touches that move too fast.
     private VelocityTracker mVelocityTracker;
@@ -200,7 +210,9 @@
             @NonNull KeyguardInteractor keyguardInteractor,
             @NonNull FeatureFlags featureFlags,
             PrimaryBouncerInteractor primaryBouncerInteractor,
-            Context context
+            Context context,
+            Lazy<BouncerInteractor> bouncerInteractor,
+            SceneContainerFlags sceneContainerFlags
     ) {
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -229,6 +241,8 @@
         dumpManager.registerDumpable(TAG, this);
         mResources = resources;
         mContext = context;
+        mBouncerInteractor = bouncerInteractor;
+        mSceneContainerFlags = sceneContainerFlags;
 
         mAccessibilityDelegate = new View.AccessibilityDelegate() {
             private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
@@ -253,6 +267,7 @@
     }
 
     /** Sets the LockIconView to the controller and rebinds any that depend on it. */
+    @SuppressLint("ClickableViewAccessibility")
     public void setLockIconView(LockIconView lockIconView) {
         mView = lockIconView;
         mView.setImageDrawable(mIcon);
@@ -302,6 +317,8 @@
         if (lockIconView.isAttachedToWindow()) {
             registerCallbacks();
         }
+
+        lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
     }
 
     private void registerCallbacks() {
@@ -388,6 +405,16 @@
             mView.updateIcon(ICON_LOCK, true);
             mView.setContentDescription(mLockedLabel);
             mView.setVisibility(View.VISIBLE);
+        } else if (mIsDozing && mFeatureFlags.isEnabled(NEW_AOD_TRANSITION)) {
+            mView.animate()
+                    .alpha(0f)
+                    .setDuration(FADE_OUT_DURATION_MS)
+                    .withEndAction(() -> {
+                        mView.clearIcon();
+                        mView.setVisibility(View.INVISIBLE);
+                        mView.setContentDescription(null);
+                    })
+                    .start();
         } else {
             mView.clearIcon();
             mView.setVisibility(View.INVISIBLE);
@@ -622,19 +649,18 @@
     };
 
     /**
-     * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true.
+     * Handles the touch if {@link #isActionable()} is true.
      * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
      * area for {@link #mLongPressTimeout} ms.
      *
      * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
      */
-    public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
-        if (!onInterceptTouchEvent(event)) {
+    private boolean onTouchEvent(MotionEvent event) {
+        if (!actionableDownEventStartedOnView(event)) {
             cancelTouches();
             return false;
         }
 
-        mOnGestureDetectedRunnable = onGestureDetectedRunnable;
         switch(event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_HOVER_ENTER:
@@ -687,12 +713,8 @@
         return true;
     }
 
-    /**
-     * Intercepts the touch if the onDown event and current event are within this lock icon view's
-     * bounds.
-     */
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (!inLockIconArea(event) || !isActionable()) {
+    private boolean actionableDownEventStartedOnView(MotionEvent event) {
+        if (!isActionable()) {
             return false;
         }
 
@@ -703,7 +725,8 @@
         return mDownDetected;
     }
 
-    private void onLongPress() {
+    @VisibleForTesting
+    protected void onLongPress() {
         cancelTouches();
         if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
             Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
@@ -716,14 +739,15 @@
             mAuthRippleController.showUnlockRipple(FINGERPRINT);
         }
         updateVisibility();
-        if (mOnGestureDetectedRunnable != null) {
-            mOnGestureDetectedRunnable.run();
-        }
 
         // play device entry haptic (consistent with UDFPS controller longpress)
         vibrateOnLongPress();
 
-        mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
+        if (mSceneContainerFlags.isEnabled()) {
+            mBouncerInteractor.get().showOrUnlockDevice(null);
+        } else {
+            mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
+        }
     }
 
 
@@ -738,12 +762,6 @@
         }
     }
 
-    private boolean inLockIconArea(MotionEvent event) {
-        mView.getHitRect(mSensorTouchLocation);
-        return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
-                && mView.getVisibility() == View.VISIBLE;
-    }
-
     private boolean isActionable() {
         if (mIsBouncerShowing) {
             Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
@@ -821,6 +839,19 @@
         }
     };
 
+    /**
+     * Whether the lock icon will handle a touch while dozing.
+     */
+    public boolean willHandleTouchWhileDozing(MotionEvent event) {
+        // is in lock icon area
+        mView.getHitRect(mSensorTouchLocation);
+        final boolean inLockIconArea =
+                mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
+                        && mView.getVisibility() == View.VISIBLE;
+
+        return inLockIconArea && actionableDownEventStartedOnView(event);
+    }
+
     private final View.OnClickListener mA11yClickListener = v -> onLongPress();
 
     private final AccessibilityManager.AccessibilityStateChangeListener
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
new file mode 100644
index 0000000..c581788
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
@@ -0,0 +1,40 @@
+package com.android.keyguard.dagger
+
+import android.content.Context
+import android.content.res.Resources
+import android.view.Display
+import com.android.systemui.dagger.qualifiers.DisplaySpecific
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+
+/**
+ * Binds display specific context and resources.
+ *
+ * When a [Display] is available in the scope, binds a [DisplaySpecific] [Context] and [Resources].
+ * When not available, the default display context and resources are used.
+ */
+@Module
+abstract class KeyguardDisplayModule {
+
+    @BindsOptionalOf abstract fun optionalDisplay(): Display
+
+    companion object {
+        @Provides
+        @DisplaySpecific
+        fun getDisplayContext(context: Context, display: Optional<Display>): Context {
+            return if (display.isPresent) {
+                context.createDisplayContext(display.get())
+            } else {
+                context
+            }
+        }
+
+        @Provides
+        @DisplaySpecific
+        fun getDisplayResources(@DisplaySpecific context: Context): Resources {
+            return context.resources
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
index d342377..f3014f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard.dagger;
 
+import android.view.Display;
+
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
@@ -28,13 +30,17 @@
  *
  * TODO: unify this with {@link KeyguardStatusBarViewComponent}
  */
-@Subcomponent(modules = {KeyguardStatusViewModule.class})
+@Subcomponent(modules = {KeyguardStatusViewModule.class, KeyguardDisplayModule.class})
 @KeyguardStatusViewScope
 public interface KeyguardStatusViewComponent {
     /** Simple factory for {@link KeyguardStatusViewComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        KeyguardStatusViewComponent build(@BindsInstance KeyguardStatusView presentation);
+        /** Creates {@link KeyguardStatusViewComponent} for a given display. */
+        KeyguardStatusViewComponent build(
+                @BindsInstance KeyguardStatusView presentation,
+                @BindsInstance Display display
+        );
     }
 
     /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 0180384..7739021 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -87,7 +87,6 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -130,14 +129,14 @@
 import com.android.systemui.util.leak.LeakReporter;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 
+import dagger.Lazy;
+
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import dagger.Lazy;
-
 /**
  * Class to handle ugly dependencies throughout sysui until we determine the
  * long-term dependency injection solution.
@@ -298,7 +297,6 @@
     @Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
     @Inject Lazy<StatusBarStateController> mStatusBarStateController;
     @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
-    @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
     @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
     @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
     @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
@@ -498,7 +496,6 @@
         mProviders.put(NotificationLockscreenUserManager.class,
                 mNotificationLockscreenUserManager::get);
         mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
-        mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
         mProviders.put(NotificationRemoteInputManager.class,
                 mNotificationRemoteInputManager::get);
         mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
index 943216e..b2a7607 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.bouncer.data.repository
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -24,11 +26,18 @@
 
 /** Provides access to bouncer-related application state. */
 @SysUISingleton
-class BouncerRepository @Inject constructor() {
+class BouncerRepository
+@Inject
+constructor(
+    flags: FeatureFlagsClassic,
+) {
     private val _message = MutableStateFlow<String?>(null)
     /** The user-facing message to show in the bouncer. */
     val message: StateFlow<String?> = _message.asStateFlow()
 
+    /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
+    val isUserSwitcherVisible: Boolean = flags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)
+
     fun setMessage(message: String?) {
         _message.value = message
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 0c02369..4ce1422 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -96,6 +96,9 @@
     /** Whether the pattern should be visible for the currently-selected user. */
     val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
 
+    /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
+    val isUserSwitcherVisible: Boolean = repository.isUserSwitcherVisible
+
     init {
         if (flags.isEnabled()) {
             // Clear the message if moved from throttling to no-longer throttling.
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 2cb98d8..ef0609a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -99,6 +99,8 @@
                 initialValue = emptyList(),
             )
 
+    val isUserSwitcherVisible: Boolean = bouncerInteractor.isUserSwitcherVisible
+
     private val isInputEnabled: StateFlow<Boolean> =
         bouncerInteractor.isThrottled
             .map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt
index 4efc21b..4b78e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt
@@ -35,10 +35,8 @@
  * The input is guaranteed to always contain a initial [ClearAll] token as a sentinel, thus clients
  * can always assume there is a 'ClearAll' watermark available.
  */
-data class PinInputViewModel
-internal constructor(
-    @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    internal val input: List<EntryToken>
+data class PinInputViewModel(
+    @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) val input: List<EntryToken>
 ) {
     init {
         require(input.firstOrNull() is ClearAll) { "input does not begin with a ClearAll token" }
@@ -132,8 +130,7 @@
     val sequenceNumber: Int
 
     /** The pin bouncer [input] as digits 0-9. */
-    data class Digit
-    internal constructor(val input: Int, override val sequenceNumber: Int = nextSequenceNumber++) :
+    data class Digit(val input: Int, override val sequenceNumber: Int = nextSequenceNumber++) :
         EntryToken {
         init {
             check(input in 0..9)
@@ -144,8 +141,7 @@
      * Marker to indicate the input is completely cleared, and subsequent [EntryToken]s mark a new
      * pin entry.
      */
-    data class ClearAll
-    internal constructor(override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken
+    data class ClearAll(override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken
 
     override fun compareTo(other: EntryToken): Int =
         compareValuesBy(this, other, EntryToken::sequenceNumber)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index c0ee71c..0dfaf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -18,8 +18,15 @@
 
 import android.view.MotionEvent;
 
+import javax.inject.Inject;
+
 /** */
 public class FalsingCollectorFake implements FalsingCollector {
+
+    @Inject
+    public FalsingCollectorFake() {
+    }
+
     @Override
     public void onSuccessfulUnlock() {
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index 046ccf16..a90980f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -18,7 +18,6 @@
 
 import com.android.systemui.globalactions.ShutdownUiModule;
 import com.android.systemui.keyguard.CustomizationProvider;
-import com.android.systemui.shade.ShadeModule;
 import com.android.systemui.statusbar.NotificationInsetsModule;
 import com.android.systemui.statusbar.QsFrameTranslateModule;
 
@@ -33,7 +32,6 @@
         DependencyProvider.class,
         NotificationInsetsModule.class,
         QsFrameTranslateModule.class,
-        ShadeModule.class,
         ShutdownUiModule.class,
         SystemUIBinder.class,
         SystemUIModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 9e5fd55..1dd4abf 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -21,12 +21,9 @@
 
 import android.content.Context;
 import android.hardware.SensorPrivacyManager;
-import android.os.Handler;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.battery.BatterySaverModule;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
@@ -34,7 +31,6 @@
 import com.android.systemui.navigationbar.NavigationBarControllerModule;
 import com.android.systemui.navigationbar.gestural.GestureModule;
 import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -45,7 +41,7 @@
 import com.android.systemui.screenshot.ReferenceScreenshotModule;
 import com.android.systemui.settings.dagger.MultiUserUtilsModule;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyboardShortcutsModule;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -53,20 +49,13 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
 import com.android.systemui.statusbar.events.StatusBarEventsModule;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.HeadsUpModule;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.AospPolicyModule;
-import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
@@ -100,11 +89,13 @@
         BatterySaverModule.class,
         CollapsedStatusBarFragmentStartableModule.class,
         GestureModule.class,
+        HeadsUpModule.class,
         MediaModule.class,
         MultiUserUtilsModule.class,
         NavigationBarControllerModule.class,
         PowerModule.class,
         QSModule.class,
+        ShadeModule.class,
         ReferenceScreenshotModule.class,
         RotationLockModule.class,
         SceneContainerFrameworkModule.class,
@@ -161,38 +152,6 @@
         return true;
     }
 
-    @SysUISingleton
-    @Provides
-    static HeadsUpManagerPhone provideHeadsUpManagerPhone(
-            Context context,
-            HeadsUpManagerLogger headsUpManagerLogger,
-            StatusBarStateController statusBarStateController,
-            KeyguardBypassController bypassController,
-            GroupMembershipManager groupManager,
-            VisualStabilityProvider visualStabilityProvider,
-            ConfigurationController configurationController,
-            @Main Handler handler,
-            AccessibilityManagerWrapper accessibilityManagerWrapper,
-            UiEventLogger uiEventLogger,
-            ShadeExpansionStateManager shadeExpansionStateManager) {
-        return new HeadsUpManagerPhone(
-                context,
-                headsUpManagerLogger,
-                statusBarStateController,
-                bypassController,
-                groupManager,
-                visualStabilityProvider,
-                configurationController,
-                handler,
-                accessibilityManagerWrapper,
-                uiEventLogger,
-                shadeExpansionStateManager
-        );
-    }
-
-    @Binds
-    abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
-
     @Provides
     @SysUISingleton
     static Recents provideRecents(Context context, RecentsImplementation recentsImplementation,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index d89332d..5d6949b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.keyguard.KeyguardViewConfigurator
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardDismissActionBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardDismissBinder
 import com.android.systemui.log.SessionTracker
@@ -92,6 +93,11 @@
     @ClassKey(AuthController::class)
     abstract fun bindAuthController(service: AuthController): CoreStartable
 
+    @Binds
+    @IntoMap
+    @ClassKey(AlternateBouncerBinder::class)
+    abstract fun bindAlternateBouncerBinder(impl: AlternateBouncerBinder): CoreStartable
+
     /** Inject into BiometricNotificationService */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 5b85ad0..1e29e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -2,7 +2,7 @@
 
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -51,7 +51,7 @@
      * When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss
      * the lockscreen.
      */
-    fun isBypassEnabled(): Boolean
+    val isBypassEnabled: StateFlow<Boolean>
 }
 
 /** Encapsulates application state for device entry. */
@@ -68,7 +68,7 @@
 ) : DeviceEntryRepository {
 
     override val isUnlocked =
-        ConflatedCallbackFlow.conflatedCallbackFlow {
+        conflatedCallbackFlow {
                 val callback =
                     object : KeyguardStateController.Callback {
                         override fun onUnlockedChanged() {
@@ -112,7 +112,24 @@
         }
     }
 
-    override fun isBypassEnabled() = keyguardBypassController.bypassEnabled
+    override val isBypassEnabled: StateFlow<Boolean> =
+        conflatedCallbackFlow {
+                val listener =
+                    object : KeyguardBypassController.OnBypassStateChangedListener {
+                        override fun onBypassStateChanged(isEnabled: Boolean) {
+                            trySend(isEnabled)
+                        }
+                    }
+                keyguardBypassController.registerOnBypassStateChangedListener(listener)
+                awaitClose {
+                    keyguardBypassController.unregisterOnBypassStateChangedListener(listener)
+                }
+            }
+            .stateIn(
+                applicationScope,
+                SharingStarted.Eagerly,
+                initialValue = keyguardBypassController.bypassEnabled,
+            )
 
     companion object {
         private const val TAG = "DeviceEntryRepositoryImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 5612c9a..e96e318 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -106,5 +106,5 @@
      * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
      * lock screen.
      */
-    fun isBypassEnabled() = repository.isBypassEnabled()
+    val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 126a1b5..6bbd40c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -48,6 +48,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -77,9 +78,9 @@
     private final SystemPropertiesHelper mSystemProperties;
     private final ServerFlagReader mServerFlagReader;
     private final Map<String, Flag<?>> mAllFlags;
-    private final Map<String, Boolean> mBooleanFlagCache = new TreeMap<>();
-    private final Map<String, String> mStringFlagCache = new TreeMap<>();
-    private final Map<String, Integer> mIntFlagCache = new TreeMap<>();
+    private final Map<String, Boolean> mBooleanFlagCache = new ConcurrentHashMap<>();
+    private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>();
+    private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>();
     private final Restarter mRestarter;
 
     private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
@@ -160,87 +161,92 @@
 
     private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
         String name = flag.getName();
-        if (!mBooleanFlagCache.containsKey(name)) {
-            mBooleanFlagCache.put(name,
-                    readBooleanFlagInternal(flag, flag.getDefault()));
+
+        Boolean value = mBooleanFlagCache.get(name);
+        if (value == null) {
+            value = readBooleanFlagInternal(flag, flag.getDefault());
+            mBooleanFlagCache.put(name, value);
         }
 
-        return mBooleanFlagCache.get(name);
+        return value;
     }
 
     @Override
     public boolean isEnabled(@NonNull ResourceBooleanFlag flag) {
         String name = flag.getName();
-        if (!mBooleanFlagCache.containsKey(name)) {
-            mBooleanFlagCache.put(name,
-                    readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())));
+        Boolean value = mBooleanFlagCache.get(name);
+        if (value == null) {
+            value = readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId()));
+            mBooleanFlagCache.put(name, value);
         }
 
-        return mBooleanFlagCache.get(name);
+        return value;
     }
 
     @Override
     public boolean isEnabled(@NonNull SysPropBooleanFlag flag) {
         String name = flag.getName();
-        if (!mBooleanFlagCache.containsKey(name)) {
-            // Use #readFlagValue to get the default. That will allow it to fall through to
-            // teamfood if need be.
-            mBooleanFlagCache.put(
-                    name,
+        Boolean value = mBooleanFlagCache.get(name);
+        if (value == null) {
+            value = readBooleanFlagInternal(flag,
                     mSystemProperties.getBoolean(
                             flag.getName(),
                             readBooleanFlagInternal(flag, flag.getDefault())));
+            mBooleanFlagCache.put(name, value);
         }
-
-        return mBooleanFlagCache.get(name);
+        return value;
     }
 
     @NonNull
     @Override
     public String getString(@NonNull StringFlag flag) {
         String name = flag.getName();
-        if (!mStringFlagCache.containsKey(name)) {
-            mStringFlagCache.put(name,
-                    readFlagValueInternal(name, flag.getDefault(), StringFlagSerializer.INSTANCE));
+        String value = mStringFlagCache.get(name);
+        if (value == null) {
+            value = readFlagValueInternal(name, flag.getDefault(), StringFlagSerializer.INSTANCE);
+            mStringFlagCache.put(name, value);
         }
 
-        return mStringFlagCache.get(name);
+        return value;
     }
 
     @NonNull
     @Override
     public String getString(@NonNull ResourceStringFlag flag) {
         String name = flag.getName();
-        if (!mStringFlagCache.containsKey(name)) {
-            mStringFlagCache.put(name,
-                    readFlagValueInternal(name, mResources.getString(flag.getResourceId()),
-                            StringFlagSerializer.INSTANCE));
+        String value = mStringFlagCache.get(name);
+        if (value == null) {
+            value = readFlagValueInternal(
+                    name,
+                    mResources.getString(flag.getResourceId()),
+                    StringFlagSerializer.INSTANCE);
+            mStringFlagCache.put(name, value);
         }
-
-        return mStringFlagCache.get(name);
+        return value;
     }
 
     @Override
     public int getInt(@NonNull IntFlag flag) {
         String name = flag.getName();
-        if (!mIntFlagCache.containsKey(name)) {
-            mIntFlagCache.put(name,
-                    readFlagValueInternal(name, flag.getDefault(), IntFlagSerializer.INSTANCE));
+        Integer value = mIntFlagCache.get(name);
+        if (value == null) {
+            value = readFlagValueInternal(name, flag.getDefault(), IntFlagSerializer.INSTANCE);
+            mIntFlagCache.put(name, value);
         }
 
-        return mIntFlagCache.get(name);
+        return value;
     }
 
     @Override
     public int getInt(@NonNull ResourceIntFlag flag) {
         String name = flag.getName();
-        if (!mIntFlagCache.containsKey(name)) {
-            mIntFlagCache.put(name,
-                    readFlagValueInternal(name, mResources.getInteger(flag.getResourceId()),
-                            IntFlagSerializer.INSTANCE));
+        Integer value = mIntFlagCache.get(name);
+        if (value == null) {
+            value = readFlagValueInternal(
+                    name, mResources.getInteger(flag.getResourceId()), IntFlagSerializer.INSTANCE);
+            mIntFlagCache.put(name, value);
         }
-
-        return mIntFlagCache.get(name);
+        return value;
     }
 
     /** Specific override for Boolean flags that checks against the teamfood list.*/
@@ -531,11 +537,22 @@
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("can override: true");
+
         pw.println("booleans: " + mBooleanFlagCache.size());
-        mBooleanFlagCache.forEach((key, value) -> pw.println("  sysui_flag_" + key + ": " + value));
+        // Sort our flags for dumping
+        TreeMap<String, Boolean> dumpBooleanMap = new TreeMap<>(mBooleanFlagCache);
+        dumpBooleanMap.forEach((key, value) -> pw.println("  sysui_flag_" + key + ": " + value));
+
         pw.println("Strings: " + mStringFlagCache.size());
-        mStringFlagCache.forEach((key, value) -> pw.println("  sysui_flag_" + key
+        // Sort our flags for dumping
+        TreeMap<String, String> dumpStringMap = new TreeMap<>(mStringFlagCache);
+        dumpStringMap.forEach((key, value) -> pw.println("  sysui_flag_" + key
                 + ": [length=" + value.length() + "] \"" + value + "\""));
+
+        pw.println("Integers: " + mIntFlagCache.size());
+        // Sort our flags for dumping
+        TreeMap<String, Integer> dumpIntMap = new TreeMap<>(mIntFlagCache);
+        dumpIntMap.forEach((key, value) -> pw.println("  sysui_flag_" + key + ": " + value));
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index e3cc2b0..bf9018a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -177,6 +177,13 @@
                 || (flag instanceof SysPropFlag);
     }
 
+    private Boolean isTeamfoodFlag(Flag<?>  flag) {
+        if (!(flag instanceof BooleanFlag)) {
+            return null;
+        }
+        return flag.getTeamfood();
+    }
+
     private boolean isBooleanFlagEnabled(Flag<?> flag) {
         if (flag instanceof ReleasedFlag) {
             return mFeatureFlags.isEnabled((ReleasedFlag) flag);
@@ -232,11 +239,13 @@
         for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
             pw.print(" ");
         }
-        pw.println(" Value");
+        pw.print(" Value  ");
+        pw.println(" Teamfood?");
         for (int i = 0; i < longestFieldName; i++) {
             pw.print("=");
         }
-        pw.println(" ========");
+        pw.println(" ======= ===========");
+
         for (String fieldName : fields.keySet()) {
             Flag<?> flag = fields.get(fieldName);
             if (!mAllFlags.containsKey(flag.getName())) {
@@ -249,7 +258,19 @@
             }
             pw.print(" ");
             if (isBooleanFlag(flag)) {
-                pw.println(isBooleanFlagEnabled(flag));
+                boolean enabled = isBooleanFlagEnabled(flag);
+                pw.print(enabled);
+                if (enabled) {
+                    pw.print("    ");
+                } else {
+                    pw.print("   ");
+                }
+                Boolean teamfood = isTeamfoodFlag(flag);
+                if (teamfood != null) {
+                    pw.print(teamfood);
+                }
+                pw.println();
+
             } else if (isStringFlag(flag)) {
                 pw.println(getStringFlag(flag));
             } else if (isIntFlag(flag)) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 8587329..f6f24e0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -40,8 +40,8 @@
 
     // 100 - notification
     // TODO(b/297792660): Tracking Bug
-    val ADD_TRANSIENT_HUN_IN_STACK_STATE_ANIMATOR =
-        unreleasedFlag("add_transient_hun_in_stack_state_animator", teamfood = false)
+    @JvmField val UNCLEARED_TRANSIENT_HUN_FIX =
+        unreleasedFlag("uncleared_transient_hun_fix", teamfood = false)
 
     // TODO(b/298308067): Tracking Bug
     @JvmField val SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX =
@@ -144,11 +144,6 @@
             "lockscreen_custom_clocks"
         )
 
-    // TODO(b/275694445): Tracking Bug
-    @JvmField
-    val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING =
-        releasedFlag("lockscreen_without_secure_lock_when_dreaming")
-
     // TODO(b/286092087): Tracking Bug
     @JvmField
     val ENABLE_SYSTEM_UI_DREAM_CONTROLLER = unreleasedFlag("enable_system_ui_dream_controller")
@@ -176,7 +171,11 @@
      */
     // TODO(b/281655028): Tracking bug
     @JvmField
-    val LIGHT_REVEAL_MIGRATION = unreleasedFlag("light_reveal_migration", teamfood = false)
+    val LIGHT_REVEAL_MIGRATION = unreleasedFlag("light_reveal_migration", teamfood = true)
+
+    // TODO(b/301915812): Tracking Bug
+    @JvmField
+    val NEW_AOD_TRANSITION = unreleasedFlag("new_aod_transition", teamfood = true)
 
     /** Flag to control the migration of face auth to modern architecture. */
     // TODO(b/262838215): Tracking bug
@@ -230,8 +229,7 @@
     /** Whether page transition animations in the wallpaper picker are enabled */
     // TODO(b/291710220): Tracking bug.
     @JvmField
-    val WALLPAPER_PICKER_PAGE_TRANSITIONS =
-        unreleasedFlag("wallpaper_picker_page_transitions", teamfood = true)
+    val WALLPAPER_PICKER_PAGE_TRANSITIONS = releasedFlag("wallpaper_picker_page_transitions")
 
     /** Add "Apply" button to wall paper picker's grid preview page. */
     // TODO(b/294866904): Tracking bug.
@@ -306,6 +304,11 @@
     @JvmField
     val WALLPAPER_PICKER_PREVIEW_ANIMATION = releasedFlag("wallpaper_picker_preview_animation")
 
+    /** Flag to enable rest to unlock feature. */
+    // TODO(b/303672286): Tracking bug
+    @JvmField
+    val REST_TO_UNLOCK: UnreleasedFlag = unreleasedFlag("rest_to_unlock")
+
     /**
      * TODO(b/278086361): Tracking bug
      * Complete rewrite of the interactions between System UI and Window Manager involving keyguard
@@ -328,7 +331,7 @@
     /** Flag to use a separate view for the alternate bouncer. */
     // TODO(b/300440924): Tracking bug
     @JvmField
-    val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
+    val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
 
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index f500017..86bf368 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -82,7 +82,8 @@
                 val statusViewComponent =
                     keyguardStatusViewComponentFactory.build(
                         LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null)
-                            as KeyguardStatusView
+                            as KeyguardStatusView,
+                        context.display
                     )
                 val controller = statusViewComponent.keyguardStatusViewController
                 controller.init()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7678f4d..39742a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -171,6 +171,8 @@
 import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -180,7 +182,8 @@
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
-import dagger.Lazy;
+
+
 import kotlinx.coroutines.CoroutineDispatcher;
 
 /**
@@ -1933,11 +1936,7 @@
     public void onDreamingStarted() {
         mUpdateMonitor.dispatchDreamingStarted();
         synchronized (this) {
-            final boolean alwaysShowKeyguard =
-                mFeatureFlags.isEnabled(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING);
-            if (mDeviceInteractive
-                && (alwaysShowKeyguard ||
-                mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()))) {
+            if (mDeviceInteractive) {
                 doKeyguardLaterLocked();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 03c166c..41bde91 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -28,6 +28,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardDisplayModule;
 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -100,6 +101,7 @@
             KeyguardDataQuickAffordanceModule.class,
             KeyguardRepositoryModule.class,
             KeyguardFaceAuthModule.class,
+            KeyguardDisplayModule.class,
             StartKeyguardTransitionModule.class,
             ResourceTrimmerModule.class,
         })
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 3ccf446..d06f31f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -32,6 +32,7 @@
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 
 @SysUISingleton
 class FromAlternateBouncerTransitionInteractor
@@ -129,11 +130,11 @@
     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
         return ValueAnimator().apply {
             interpolator = Interpolators.LINEAR
-            duration = TRANSITION_DURATION_MS
+            duration = TRANSITION_DURATION_MS.inWholeMilliseconds
         }
     }
 
     companion object {
-        private const val TRANSITION_DURATION_MS = 300L
+        val TRANSITION_DURATION_MS = 300.milliseconds
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index e57c919..89aca76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -61,6 +61,7 @@
     fun onSwipeUpOnBouncer()
     fun onPrimaryBouncerUserInput()
     fun onAccessibilityAction()
+    fun onWalletLaunched()
 }
 
 /**
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 d6987629..ac012f8 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
@@ -56,7 +58,11 @@
         if (event.handleAction()) {
             when (event.keyCode) {
                 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
-                KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
+                KeyEvent.KEYCODE_SPACE,
+                KeyEvent.KEYCODE_ENTER ->
+                    if (isDeviceAwake()) {
+                        return collapseShadeLockedOrShowPrimaryBouncer()
+                    }
             }
         }
         return false
@@ -92,16 +98,24 @@
                 (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 -> {
+                if (!statusBarKeyguardViewManager.primaryBouncerIsShowing()) {
+                    statusBarKeyguardViewManager.showPrimaryBouncer(true)
+                    return true
+                }
+            }
         }
         return false
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 1c43609..fbe26de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -34,16 +34,17 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.stateIn
-import javax.inject.Inject
 
 /** Encapsulates business-logic related to the keyguard transitions. */
 @SysUISingleton
@@ -176,18 +177,17 @@
             .map { step -> step.to }
             .stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN)
 
-
     /**
      * Whether we're currently in a transition to a new [KeyguardState] and haven't yet completed
      * it.
      */
     val isInTransitionToAnyState =
-            combine(
-                    startedKeyguardTransitionStep,
-                    finishedKeyguardState,
-            ) { startedStep, finishedState ->
-                startedStep.to != finishedState
-            }
+        combine(
+            startedKeyguardTransitionStep,
+            finishedKeyguardState,
+        ) { startedStep, finishedState ->
+            startedStep.to != finishedState
+        }
 
     /**
      * The amount of transition into or out of the given [KeyguardState].
@@ -236,14 +236,9 @@
 
     /** Whether we're in a transition to the given [KeyguardState], but haven't yet completed it. */
     fun isInTransitionToState(
-            state: KeyguardState,
+        state: KeyguardState,
     ): Flow<Boolean> {
-        return combine(
-                startedKeyguardTransitionStep,
-                finishedKeyguardState,
-        ) { startedStep, finishedState ->
-            startedStep.to == state && finishedState != state
-        }
+        return isInTransitionToStateWhere { it == state }
     }
 
     /**
@@ -251,28 +246,18 @@
      * haven't yet completed it.
      */
     fun isInTransitionToStateWhere(
-            stateMatcher: (KeyguardState) -> Boolean,
+        stateMatcher: (KeyguardState) -> Boolean,
     ): Flow<Boolean> {
-        return combine(
-                startedKeyguardTransitionStep,
-                finishedKeyguardState,
-        ) { startedStep, finishedState ->
-            stateMatcher(startedStep.to) && finishedState != startedStep.from
-        }
+        return isInTransitionWhere(fromStatePredicate = { true }, toStatePredicate = stateMatcher)
     }
 
     /**
      * Whether we're in a transition out of the given [KeyguardState], but haven't yet completed it.
      */
     fun isInTransitionFromState(
-            state: KeyguardState,
+        state: KeyguardState,
     ): Flow<Boolean> {
-        return combine(
-                startedKeyguardTransitionStep,
-                finishedKeyguardState,
-        ) { startedStep, finishedState ->
-            startedStep.from == state && finishedState != state
-        }
+        return isInTransitionFromStateWhere { it == state }
     }
 
     /**
@@ -280,14 +265,9 @@
      * haven't yet completed it.
      */
     fun isInTransitionFromStateWhere(
-            stateMatcher: (KeyguardState) -> Boolean,
+        stateMatcher: (KeyguardState) -> Boolean,
     ): Flow<Boolean> {
-        return combine(
-                startedKeyguardTransitionStep,
-                finishedKeyguardState,
-        ) { startedStep, finishedState ->
-            stateMatcher(startedStep.from) && finishedState != startedStep.from
-        }
+        return isInTransitionWhere(fromStatePredicate = stateMatcher, toStatePredicate = { true })
     }
 
     /**
@@ -299,26 +279,23 @@
         toStatePredicate: (KeyguardState) -> Boolean,
     ): Flow<Boolean> {
         return combine(
-            startedKeyguardTransitionStep,
-            finishedKeyguardState,
-        ) { startedStep, finishedState ->
-            fromStatePredicate(startedStep.from)
-                    && toStatePredicate(startedStep.to)
-                    && finishedState != startedStep.from
-        }
+                startedKeyguardTransitionStep,
+                finishedKeyguardState,
+            ) { startedStep, finishedState ->
+                fromStatePredicate(startedStep.from) &&
+                    toStatePredicate(startedStep.to) &&
+                    finishedState != startedStep.to
+            }
+            .distinctUntilChanged()
     }
 
-    /**
-     * Whether we've FINISHED a transition to a state that matches the given predicate.
-     */
+    /** Whether we've FINISHED a transition to a state that matches the given predicate. */
     fun isFinishedInStateWhere(stateMatcher: (KeyguardState) -> Boolean): Flow<Boolean> {
-        return finishedKeyguardState.map { stateMatcher(it) }
+        return finishedKeyguardState.map { stateMatcher(it) }.distinctUntilChanged()
     }
 
-    /**
-     * Whether we've FINISHED a transition to a state that matches the given predicate.
-     */
-    fun isFinishedInState(state: KeyguardState) : Flow<Boolean> {
-        return finishedKeyguardState.map { it == state }
+    /** Whether we've FINISHED a transition to a state that matches the given predicate. */
+    fun isFinishedInState(state: KeyguardState): Flow<Boolean> {
+        return finishedKeyguardState.map { it == state }.distinctUntilChanged()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index 596a1c0..f38bb2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -61,4 +61,5 @@
     override fun onSwipeUpOnBouncer() {}
     override fun onPrimaryBouncerUserInput() {}
     override fun onAccessibilityAction() {}
+    override fun onWalletLaunched() = Unit
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index ef1d5ac..797dec2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.biometrics.shared.model.LockoutMode
+import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -33,7 +34,6 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -44,6 +44,7 @@
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -56,7 +57,6 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.yield
-import javax.inject.Inject
 
 /**
  * Encapsulates business logic related face authentication being triggered for device entry from
@@ -79,7 +79,6 @@
     private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val userRepository: UserRepository,
     private val facePropertyRepository: FacePropertyRepository,
-    private val keyguardRepository: KeyguardRepository,
     private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig,
     private val powerInteractor: PowerInteractor,
 ) : CoreStartable, KeyguardFaceAuthInteractor {
@@ -207,6 +206,12 @@
         runFaceAuth(FaceAuthUiEvent.FACE_AUTH_ACCESSIBILITY_ACTION, false)
     }
 
+    override fun onWalletLaunched() {
+        if (facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG) {
+            runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true)
+        }
+    }
+
     override fun registerListener(listener: FaceAuthenticationListener) {
         listeners.add(listener)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
new file mode 100644
index 0000000..41af9e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.scrim.ScrimView
+import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AlternateBouncerBinder
+@Inject
+constructor(
+    private val notificationShadeWindowView: NotificationShadeWindowView,
+    private val featureFlags: FeatureFlagsClassic,
+    private val alternateBouncerViewModel: AlternateBouncerViewModel,
+    @Application private val scope: CoroutineScope,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+) : CoreStartable {
+    override fun start() {
+        if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+            return
+        }
+
+        AlternateBouncerViewBinder.bind(
+            notificationShadeWindowView.requireViewById(R.id.alternate_bouncer),
+            alternateBouncerViewModel,
+            scope,
+            notificationShadeWindowController,
+        )
+    }
+}
+
+/**
+ * Binds the alternate bouncer view to its view-model.
+ *
+ * To use this properly, users should maintain a one-to-one relationship between the [View] and the
+ * view-binding, binding each view only once. It is okay and expected for the same instance of the
+ * view-model to be reused for multiple view/view-binder bindings.
+ */
+@ExperimentalCoroutinesApi
+object AlternateBouncerViewBinder {
+
+    /** Binds the view to the view-model, continuing to update the former based on the latter. */
+    @JvmStatic
+    fun bind(
+        view: ViewGroup,
+        viewModel: AlternateBouncerViewModel,
+        scope: CoroutineScope,
+        notificationShadeWindowController: NotificationShadeWindowController,
+    ) {
+        scope.launch {
+            // forcePluginOpen is necessary to show over occluded apps.
+            // This cannot be tied to the view's lifecycle because setting this allows the view
+            // to be started in the first place.
+            viewModel.forcePluginOpen.collect {
+                notificationShadeWindowController.setForcePluginOpen(it, this)
+            }
+        }
+
+        val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+        view.repeatWhenAttached { alternateBouncerViewContainer ->
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                scrim.viewAlpha = 0f
+
+                launch {
+                    viewModel.onClickListener.collect {
+                        // TODO (b/287599719): Support swiping to dismiss altBouncer
+                        alternateBouncerViewContainer.setOnClickListener(it)
+                    }
+                }
+
+                launch {
+                    viewModel.scrimAlpha.collect {
+                        alternateBouncerViewContainer.visibility =
+                            if (it < .1f) View.INVISIBLE else View.VISIBLE
+                        scrim.viewAlpha = it
+                    }
+                }
+
+                launch { viewModel.scrimColor.collect { scrim.tint = it } }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index aa76702..f0d118c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -134,7 +134,14 @@
         vibratorHelper: VibratorHelper?,
     ) {
         if (!viewModel.isVisible) {
-            view.isInvisible = true
+            view.alpha = 1f
+            view
+                    .animate()
+                    .alpha(0f)
+                    .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                    .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
+                    .withEndAction { view.isInvisible = true }
+                    .start()
             return
         }
 
@@ -142,11 +149,9 @@
             view.isVisible = true
             if (viewModel.animateReveal) {
                 view.alpha = 0f
-                view.translationY = view.height / 2f
                 view
                     .animate()
                     .alpha(1f)
-                    .translationY(0f)
                     .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
                     .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
                     .start()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 231eeb5..c6d8ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -22,17 +22,19 @@
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
-import android.content.res.Resources
 import android.graphics.Rect
 import android.hardware.display.DisplayManager
 import android.os.Bundle
 import android.os.Handler
 import android.os.IBinder
+import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
 import android.view.LayoutInflater
 import android.view.SurfaceControlViewHost
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD
 import android.widget.FrameLayout
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.isInvisible
@@ -126,6 +128,8 @@
     private val shouldHideClock: Boolean =
         bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
     private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS)
+    private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
+    private val display: Display = displayManager.getDisplay(displayId)
 
     private var host: SurfaceControlViewHost
 
@@ -165,7 +169,7 @@
             host =
                 SurfaceControlViewHost(
                     context,
-                    displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)),
+                    displayManager.getDisplay(DEFAULT_DISPLAY),
                     hostToken,
                     "KeyguardPreviewRenderer"
                 )
@@ -175,21 +179,27 @@
 
     fun render() {
         mainHandler.post {
-            val rootView = FrameLayout(context)
+            val previewContext = context.createDisplayContext(display)
 
-            setupKeyguardRootView(rootView)
+            val rootView = FrameLayout(previewContext)
+
+            setupKeyguardRootView(previewContext, rootView)
 
             if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
                 setUpBottomArea(rootView)
             }
 
+            val windowContext = context.createWindowContext(display, TYPE_KEYGUARD, null)
+            val windowManagerOfDisplay = windowContext.getSystemService(WindowManager::class.java)
             rootView.measure(
                 View.MeasureSpec.makeMeasureSpec(
-                    windowManager.currentWindowMetrics.bounds.width(),
+                    windowManagerOfDisplay?.currentWindowMetrics?.bounds?.width()
+                        ?: windowManager.currentWindowMetrics.bounds.width(),
                     View.MeasureSpec.EXACTLY
                 ),
                 View.MeasureSpec.makeMeasureSpec(
-                    windowManager.currentWindowMetrics.bounds.height(),
+                    windowManagerOfDisplay?.currentWindowMetrics?.bounds?.height()
+                        ?: windowManager.currentWindowMetrics.bounds.height(),
                     View.MeasureSpec.EXACTLY
                 ),
             )
@@ -252,7 +262,7 @@
      *
      * The end padding is as follows: Below clock padding end
      */
-    private fun setUpSmartspace(parentView: ViewGroup) {
+    private fun setUpSmartspace(previewContext: Context, parentView: ViewGroup) {
         if (
             !lockscreenSmartspaceController.isEnabled() ||
                 !lockscreenSmartspaceController.isDateWeatherDecoupled()
@@ -264,12 +274,12 @@
 
         val topPadding: Int =
             KeyguardPreviewSmartspaceViewModel.getLargeClockSmartspaceTopPadding(
-                context.resources,
+                previewContext.resources,
             )
         val startPadding: Int =
-            context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start)
+            previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start)
         val endPadding: Int =
-            context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
+            previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
 
         smartSpaceView?.let {
             it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
@@ -309,8 +319,8 @@
     }
 
     @OptIn(ExperimentalCoroutinesApi::class)
-    private fun setupKeyguardRootView(rootView: FrameLayout) {
-        val keyguardRootView = KeyguardRootView(context, null).apply { removeAllViews() }
+    private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
+        val keyguardRootView = KeyguardRootView(previewContext, null).apply { removeAllViews() }
         disposables.add(
             KeyguardRootViewBinder.bind(
                 keyguardRootView,
@@ -334,10 +344,10 @@
             if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
                 setupShortcuts(keyguardRootView)
             }
-            setUpUdfps(rootView)
+            setUpUdfps(previewContext, rootView)
 
             if (!shouldHideClock) {
-                setUpClock(rootView)
+                setUpClock(previewContext, rootView)
                 KeyguardPreviewClockViewBinder.bind(
                     largeClockHostView,
                     smallClockHostView,
@@ -345,7 +355,7 @@
                 )
             }
 
-            setUpSmartspace(rootView)
+            setUpSmartspace(previewContext, rootView)
             smartSpaceView?.let {
                 KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel)
             }
@@ -382,7 +392,7 @@
         }
     }
 
-    private fun setUpUdfps(parentView: ViewGroup) {
+    private fun setUpUdfps(previewContext: Context, parentView: ViewGroup) {
         val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds
 
         // If sensorBounds are default rect, then there is no UDFPS
@@ -400,7 +410,7 @@
             sensorBounds.bottom
         )
         val finger =
-            LayoutInflater.from(context)
+            LayoutInflater.from(previewContext)
                 .inflate(
                     R.layout.udfps_keyguard_preview,
                     parentView,
@@ -409,17 +419,54 @@
         parentView.addView(finger, fingerprintLayoutParams)
     }
 
-    private fun setUpClock(parentView: ViewGroup) {
+    private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
         largeClockHostView =
-            if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW))
+            if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
                 parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large)
-            else createLargeClockHostView()
+            } else {
+                val hostView = FrameLayout(previewContext)
+                hostView.layoutParams =
+                    FrameLayout.LayoutParams(
+                        FrameLayout.LayoutParams.MATCH_PARENT,
+                        FrameLayout.LayoutParams.MATCH_PARENT,
+                    )
+                parentView.addView(hostView)
+                hostView
+            }
         largeClockHostView.isInvisible = true
 
         smallClockHostView =
-            if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW))
+            if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
                 parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view)
-            else createSmallClockHostView(parentView.resources)
+            } else {
+                val resources = parentView.resources
+                val hostView = FrameLayout(previewContext)
+                val layoutParams =
+                    FrameLayout.LayoutParams(
+                        FrameLayout.LayoutParams.WRAP_CONTENT,
+                        resources.getDimensionPixelSize(
+                            com.android.systemui.customization.R.dimen.small_clock_height
+                        )
+                    )
+                layoutParams.topMargin =
+                    KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) +
+                        resources.getDimensionPixelSize(
+                            com.android.systemui.customization.R.dimen.small_clock_padding_top
+                        )
+                hostView.layoutParams = layoutParams
+
+                hostView.setPaddingRelative(
+                    resources.getDimensionPixelSize(
+                        com.android.systemui.customization.R.dimen.clock_padding_start
+                    ),
+                    0,
+                    0,
+                    0
+                )
+                hostView.clipChildren = false
+                parentView.addView(hostView)
+                hostView
+            }
         smallClockHostView.isInvisible = true
 
         // TODO (b/283465254): Move the listeners to KeyguardClockRepository
@@ -476,44 +523,6 @@
         onClockChanged()
     }
 
-    private fun createLargeClockHostView(): FrameLayout {
-        val hostView = FrameLayout(context)
-        hostView.layoutParams =
-            FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.MATCH_PARENT,
-            )
-        return hostView
-    }
-
-    private fun createSmallClockHostView(resources: Resources): FrameLayout {
-        val hostView = FrameLayout(context)
-        val layoutParams =
-            FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                resources.getDimensionPixelSize(
-                    com.android.systemui.customization.R.dimen.small_clock_height
-                )
-            )
-        layoutParams.topMargin =
-            KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) +
-                resources.getDimensionPixelSize(
-                    com.android.systemui.customization.R.dimen.small_clock_padding_top
-                )
-        hostView.layoutParams = layoutParams
-
-        hostView.setPaddingRelative(
-            resources.getDimensionPixelSize(
-                com.android.systemui.customization.R.dimen.clock_padding_start
-            ),
-            0,
-            0,
-            0
-        )
-        hostView.clipChildren = false
-        return hostView
-    }
-
     private fun onClockChanged() {
         val clock = clockRegistry.createCurrentClock()
         clockController.clock = clock
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 1a9f021..0b0f21d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -31,12 +31,12 @@
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import com.android.keyguard.KeyguardStatusView
 import com.android.keyguard.dagger.KeyguardStatusViewComponent
-import com.android.systemui.res.R
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.KeyguardViewConfigurator
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationPanelView
 import com.android.systemui.shade.NotificationPanelViewController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
@@ -84,7 +84,8 @@
     override fun bindData(constraintLayout: ConstraintLayout) {
         if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
             constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let {
-                val statusViewComponent = keyguardStatusViewComponentFactory.build(it)
+                val statusViewComponent =
+                    keyguardStatusViewComponentFactory.build(it, context.display)
                 val controller = statusViewComponent.keyguardStatusViewController
                 controller.init()
                 keyguardMediaController.attachSplitShadeContainer(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
new file mode 100644
index 0000000..235a28d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.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.viewmodel
+
+import android.graphics.Color
+import android.view.View
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.wm.shell.animation.Interpolators
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+@ExperimentalCoroutinesApi
+class AlternateBouncerViewModel
+@Inject
+constructor(
+    statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+    transitionInteractor: KeyguardTransitionInteractor,
+    falsingManager: FalsingManager,
+) {
+    // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
+    private val alternateBouncerScrimAlpha = .66f
+    private val toAlternateBouncerTransition =
+        KeyguardTransitionAnimationFlow(
+                transitionDuration = TRANSITION_DURATION_MS,
+                transitionFlow = transitionInteractor.anyStateToAlternateBouncerTransition,
+            )
+            .createFlow(
+                duration = TRANSITION_DURATION_MS,
+                onStep = { it },
+                onFinish = { 1f },
+                // Reset on cancel
+                onCancel = { 0f },
+                interpolator = Interpolators.FAST_OUT_SLOW_IN,
+            )
+    private val fromAlternateBouncerTransition =
+        KeyguardTransitionAnimationFlow(
+                transitionDuration = TRANSITION_DURATION_MS,
+                transitionFlow = transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER),
+            )
+            .createFlow(
+                duration = TRANSITION_DURATION_MS,
+                onStep = { 1f - it },
+                // Reset on cancel
+                onCancel = { 0f },
+                interpolator = Interpolators.FAST_OUT_SLOW_IN,
+            )
+
+    /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
+    private val transitionToAlternateBouncerProgress =
+        merge(fromAlternateBouncerTransition, toAlternateBouncerTransition)
+
+    val forcePluginOpen: Flow<Boolean> =
+        transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged()
+
+    /** An observable for the scrim alpha. */
+    val scrimAlpha = transitionToAlternateBouncerProgress.map { it * alternateBouncerScrimAlpha }
+
+    /** An observable for the scrim color. Change color for easier debugging. */
+    val scrimColor: Flow<Int> = flowOf(Color.BLACK)
+
+    private val clickListener =
+        View.OnClickListener {
+            if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
+            }
+        }
+
+    val onClickListener: Flow<View.OnClickListener?> =
+        transitionToAlternateBouncerProgress
+            .map {
+                if (it == 1f) {
+                    clickListener
+                } else {
+                    null
+                }
+            }
+            .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index 54abc5b..0b1079f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -19,13 +19,13 @@
 import android.content.Context
 import androidx.annotation.ColorInt
 import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.res.R
 import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.res.R
 import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
 import kotlin.math.roundToInt
@@ -62,7 +62,7 @@
 
     private val toAlternateBouncer: Flow<TransitionViewModel> =
         keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
-            transitionInteractor.anyStateToAlternateBouncerTransition.map {
+            transitionInteractor.transitionStepsToState(KeyguardState.ALTERNATE_BOUNCER).map {
                 TransitionViewModel(
                     alpha = 1f,
                     scale =
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 68202d5..80be766 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -38,6 +38,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
 import android.provider.MediaStore;
 import android.util.Log;
 
@@ -111,7 +112,7 @@
         @Override
         public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
                 throws RemoteException {
-            if (Ringtone.useRingtoneV2()) {
+            if (Flags.hapticsCustomizationRingtoneV2Enabled()) {
                 playRemoteRingtone(token, uri, aa, true, Ringtone.MEDIA_SOUND,
                         null, volume, looping, /* hapticGenerator= */ false,
                         null);
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 2b56d0c..d08d040 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -239,6 +239,8 @@
     protected void onDestroy() {
         super.onDestroy();
         if (mDialog != null) {
+            mDialog.setOnDismissListener(null);
+            mDialog.setOnCancelListener(null);
             mDialog.dismiss();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index 8589ae9..68bf88b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -72,7 +72,7 @@
 
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
+        QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(getView());
         mQsImpl = mQsImplProvider.get();
         mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 2a36fdb..65a2c8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -105,7 +105,7 @@
 
     override fun removeCallback(callback: QSHost.Callback) {
         if (useNewHost) {
-            synchronized(callbacksMap) { callbacksMap.get(callback)?.cancel() }
+            synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
         } else {
             qsTileHost.removeCallback(callback)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index a32a024..202254b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -199,11 +199,13 @@
         mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
     }
 
+    /**
+     * This method will set up all the necessary fields. Methods from the implemented interfaces
+     * should not be called before this method returns.
+     */
     public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) {
         mRootView = qsComponent.getRootView();
 
-        mCommandQueue.addCallback(this);
-
         mQSPanelController = qsComponent.getQSPanelController();
         mQuickQSPanelController = qsComponent.getQuickQSPanelController();
 
@@ -270,6 +272,9 @@
                     mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f);
                     mQSAnimator.requestAnimatorUpdate();
                 });
+
+        // This will immediately call disable, so it needs to be added after setting up the fields.
+        mCommandQueue.addCallback(this);
     }
 
     private void bindFooterActionsView(View root) {
@@ -323,6 +328,8 @@
     public void onDestroy() {
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
+        mQSPanelController.destroy();
+        mQuickQSPanelController.destroy();
         if (mListening) {
             setListening(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 60c92c0..fc2f5f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -158,6 +158,7 @@
     protected void onInit() {
         mView.initialize(mQSLogger);
         mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
+        mHost.addCallback(mQSHostCallback);
     }
 
     /**
@@ -172,6 +173,18 @@
     }
 
     @Override
+    public void destroy() {
+        super.destroy();
+        mHost.removeCallback(mQSHostCallback);
+
+        for (TileRecord record : mRecords) {
+            record.tile.removeCallback(record.callback);
+            mView.removeTile(record);
+        }
+        mRecords.clear();
+    }
+
+    @Override
     protected void onViewAttached() {
         mQsTileRevealController = createTileRevealController();
         if (mQsTileRevealController != null) {
@@ -180,7 +193,6 @@
 
         mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
-        mHost.addCallback(mQSHostCallback);
         setTiles();
         mLastOrientation = getResources().getConfiguration().orientation;
         mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
@@ -193,16 +205,11 @@
     protected void onViewDetached() {
         mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag());
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
-        mHost.removeCallback(mQSHostCallback);
 
         mView.getTileLayout().setListening(false, mUiEventLogger);
 
         mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
 
-        for (TileRecord record : mRecords) {
-            record.tile.removeCallback(record.callback);
-        }
-        mRecords.clear();
         mDumpManager.unregisterDumpable(mView.getDumpableTag());
     }
 
@@ -222,15 +229,30 @@
         if (!collapsedView && mQsTileRevealController != null) {
             mQsTileRevealController.updateRevealedTiles(tiles);
         }
-
-        for (QSPanelControllerBase.TileRecord record : mRecords) {
-            mView.removeTile(record);
-            record.tile.removeCallback(record.callback);
+        boolean shouldChange = false;
+        if (tiles.size() == mRecords.size()) {
+            int i = 0;
+            for (QSTile tile : tiles) {
+                if (tile != mRecords.get(i).tile) {
+                    shouldChange = true;
+                    break;
+                }
+                i++;
+            }
+        } else {
+            shouldChange = true;
         }
-        mRecords.clear();
-        mCachedSpecs = "";
-        for (QSTile tile : tiles) {
-            addTile(tile, collapsedView);
+
+        if (shouldChange) {
+            for (QSPanelControllerBase.TileRecord record : mRecords) {
+                mView.removeTile(record);
+                record.tile.removeCallback(record.callback);
+            }
+            mRecords.clear();
+            mCachedSpecs = "";
+            for (QSTile tile : tiles) {
+                addTile(tile, collapsedView);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 327e858..ce8db7898 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.qs.dagger;
 
+import android.view.View;
+
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.qs.QSFragmentLegacy;
 
 import dagger.BindsInstance;
@@ -31,6 +34,7 @@
     /** Factory for building a {@link QSFragmentComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment);
+        /** */
+        QSFragmentComponent create(@BindsInstance @RootView View view);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 0c9c24d..0e75b21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -20,34 +20,17 @@
 import static com.android.systemui.util.Utils.useQsMediaPlayer;
 
 import android.content.Context;
-import android.view.View;
 
-import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragmentLegacy;
-
-import javax.inject.Named;
-
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
+import javax.inject.Named;
+
 /**
  * Dagger Module for {@link QSFragmentComponent}.
  */
 @Module(includes = {QSScopeModule.class})
 public  interface QSFragmentModule {
-
-    @Provides
-    @RootView
-    static View provideRootView(QSFragmentLegacy qsFragment) {
-        return qsFragment.getView();
-    }
-
-    /** */
-    @Binds
-    QS bindQS(QSFragmentLegacy qsFragment);
-
     /** */
     @Provides
     @Named(QSScopeModule.QS_USING_MEDIA_PLAYER)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index c5512c1..4bb8c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -272,7 +272,6 @@
                             // repository
                             launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
                         }
-                        Log.d("Fabian", "Finished resolving tiles")
                     }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 584456d..91b4d17 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -51,7 +52,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.launch
-import javax.inject.Inject
 
 /**
  * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI
@@ -142,7 +142,7 @@
                                 // When the device becomes unlocked in Lockscreen, go to Gone if
                                 // bypass is enabled.
                                 renderedScenes.contains(SceneKey.Lockscreen) ->
-                                    if (deviceEntryInteractor.isBypassEnabled()) {
+                                    if (deviceEntryInteractor.isBypassEnabled.value) {
                                         SceneKey.Gone to
                                             "device unlocked in Lockscreen scene with bypass"
                                     } else {
@@ -179,36 +179,34 @@
         }
 
         applicationScope.launch {
-            powerInteractor.isAsleep
-                .collect { isAsleep ->
-                    if (isAsleep) {
-                        switchToScene(
-                                targetSceneKey = SceneKey.Lockscreen,
-                                loggingReason = "device is starting to sleep",
-                        )
-                    } else {
-                        val authMethod = authenticationInteractor.getAuthenticationMethod()
-                        val isUnlocked = deviceEntryInteractor.isUnlocked.value
-                        when {
-                            authMethod == AuthenticationMethodModel.None -> {
-                                switchToScene(
-                                        targetSceneKey = SceneKey.Gone,
-                                        loggingReason =
-                                        "device is starting to wake up while auth method is" +
-                                                " none",
-                                )
-                            }
-                            authMethod.isSecure && isUnlocked -> {
-                                switchToScene(
-                                        targetSceneKey = SceneKey.Gone,
-                                        loggingReason =
-                                        "device is starting to wake up while unlocked with a" +
-                                                " secure auth method",
-                                )
-                            }
+            powerInteractor.isAsleep.collect { isAsleep ->
+                if (isAsleep) {
+                    switchToScene(
+                        targetSceneKey = SceneKey.Lockscreen,
+                        loggingReason = "device is starting to sleep",
+                    )
+                } else {
+                    val authMethod = authenticationInteractor.getAuthenticationMethod()
+                    val isUnlocked = deviceEntryInteractor.isUnlocked.value
+                    when {
+                        authMethod == AuthenticationMethodModel.None -> {
+                            switchToScene(
+                                targetSceneKey = SceneKey.Gone,
+                                loggingReason =
+                                    "device is starting to wake up while auth method is" + " none",
+                            )
+                        }
+                        authMethod.isSecure && isUnlocked -> {
+                            switchToScene(
+                                targetSceneKey = SceneKey.Gone,
+                                loggingReason =
+                                    "device is starting to wake up while unlocked with a" +
+                                        " secure auth method",
+                            )
                         }
                     }
                 }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index 3927873..f704894 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -42,6 +42,13 @@
          * scene, this value will remain true after the pointer is no longer touching the screen and
          * will be true in any transition created to animate back to the original position.
          */
-        val isUserInputDriven: Boolean,
+        val isInitiatedByUserInput: Boolean,
+
+        /**
+         * Whether user input is currently driving the transition. For example, if a user is
+         * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+         * the transition completes/settles.
+         */
+        val isUserInputOngoing: Flow<Boolean>,
     ) : ObservableTransitionState()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 4bc93a8..2e45353 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -61,12 +61,17 @@
     data class Swipe(
         /** The direction of the swipe. */
         val direction: Direction,
+        /**
+         * The edge from which the swipe originated or `null`, if the swipe didn't start close to an
+         * edge.
+         */
+        val fromEdge: Edge? = null,
         /** The number of pointers that were used (for example, one or two fingers). */
         val pointerCount: Int = 1,
     ) : UserAction
 
     /** The user has hit the back button or performed the back navigation gesture. */
-    object Back : UserAction
+    data object Back : UserAction
 }
 
 /** Enumerates all known "cardinal" directions for user actions. */
@@ -76,3 +81,11 @@
     RIGHT,
     DOWN,
 }
+
+/** Enumerates all known edges from which a swipe can start. */
+enum class Edge {
+    LEFT,
+    TOP,
+    RIGHT,
+    BOTTOM,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 2f87301..393a698 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -132,6 +132,7 @@
         setUserIdInternal(startingUser)
 
         val filter = IntentFilter().apply {
+            addAction(Intent.ACTION_LOCALE_CHANGED)
             addAction(Intent.ACTION_USER_INFO_CHANGED)
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
@@ -149,6 +150,7 @@
 
     override fun onReceive(context: Context, intent: Intent) {
         when (intent.action) {
+            Intent.ACTION_LOCALE_CHANGED,
             Intent.ACTION_USER_INFO_CHANGED,
             Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
index 9235fcc..c42fdf8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -67,8 +67,6 @@
         mDebugPaint.setStrokeWidth(2);
         mDebugPaint.setStyle(Paint.Style.STROKE);
         mDebugPaint.setTextSize(24);
-        String headerDebugInfo = mNotificationPanelViewController.getHeaderDebugInfo();
-        if (headerDebugInfo != null) canvas.drawText(headerDebugInfo, 50, 100, mDebugPaint);
 
         drawDebugInfo(canvas, mNotificationPanelViewController.getMaxPanelHeight(),
                 Color.RED, "getMaxPanelHeight()");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d3d38e5c..2ef83dd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -138,7 +138,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.power.shared.model.WakefulnessModel;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
@@ -162,9 +161,10 @@
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.power.shared.model.WakefulnessModel;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.data.repository.ShadeRepository;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.shade.transition.ShadeTransitionController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
@@ -200,7 +200,6 @@
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
@@ -217,6 +216,7 @@
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
@@ -362,7 +362,7 @@
     private boolean mIsLaunchAnimationRunning;
     private float mOverExpansion;
     private CentralSurfaces mCentralSurfaces;
-    private HeadsUpManagerPhone mHeadsUpManager;
+    private HeadsUpManager mHeadsUpManager;
     private float mExpandedHeight = 0;
     /** The current squish amount for the predictive back animation */
     private float mCurrentBackProgress = 0.0f;
@@ -1275,7 +1275,8 @@
             KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById(
                     R.id.keyguard_status_view);
             KeyguardStatusViewComponent statusViewComponent =
-                    mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
+                    mKeyguardStatusViewComponentFactory.build(keyguardStatusView,
+                            mView.getContext().getDisplay());
             mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
             mKeyguardStatusViewController.init();
         }
@@ -3025,7 +3026,7 @@
         return headsUpVisible || isExpanded() || mBouncerShowing;
     }
 
-    private void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+    private void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         mHeadsUpTouchHelper = new HeadsUpTouchHelper(
@@ -3507,7 +3508,7 @@
             GestureRecorder recorder,
             Runnable hideExpandedRunnable,
             NotificationShelfController notificationShelfController,
-            HeadsUpManagerPhone headsUpManager) {
+            HeadsUpManager headsUpManager) {
         setHeadsUpManager(headsUpManager);
         // TODO(b/254859580): this can be injected.
         mCentralSurfaces = centralSurfaces;
@@ -3556,10 +3557,6 @@
         mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
     }
 
-    String getHeaderDebugInfo() {
-        return "USER " + mHeadsUpManager.getUser();
-    }
-
     @Override
     public void onThemeChanged() {
         mConfigurationListener.onThemeChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 6cf4ff5..5414b3f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -21,7 +21,6 @@
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.app.StatusBarManager;
-import android.os.PowerManager;
 import android.util.Log;
 import android.view.GestureDetector;
 import android.view.InputDevice;
@@ -36,10 +35,11 @@
 import com.android.keyguard.LockIconViewController;
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.back.domain.interactor.BackActionInteractor;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel;
 import com.android.systemui.classifier.FalsingCollector;
@@ -56,6 +56,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.log.BouncerLogger;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.res.R;
 import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -106,6 +107,8 @@
     private final boolean mIsTrackpadCommonEnabled;
     private final FeatureFlags mFeatureFlags;
     private final KeyEventInteractor mKeyEventInteractor;
+    private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+    private final AlternateBouncerInteractor mAlternateBouncerInteractor;
     private GestureDetector mPulsingWakeupGestureHandler;
     private GestureDetector mDreamingWakeupGestureHandler;
     private View mBrightnessMirror;
@@ -182,7 +185,9 @@
             SystemClock clock,
             BouncerMessageInteractor bouncerMessageInteractor,
             BouncerLogger bouncerLogger,
-            KeyEventInteractor keyEventInteractor) {
+            KeyEventInteractor keyEventInteractor,
+            PrimaryBouncerInteractor primaryBouncerInteractor,
+            AlternateBouncerInteractor alternateBouncerInteractor) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -210,6 +215,8 @@
         mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
         mFeatureFlags = featureFlags;
         mKeyEventInteractor = keyEventInteractor;
+        mPrimaryBouncerInteractor = primaryBouncerInteractor;
+        mAlternateBouncerInteractor = alternateBouncerInteractor;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -351,16 +358,6 @@
                 if (mStatusBarStateController.isDozing()) {
                     mDozeScrimController.extendPulse();
                 }
-                mLockIconViewController.onTouchEvent(
-                        ev,
-                        /* onGestureDetectedRunnable */
-                        () -> {
-                            mService.userActivity();
-                            mPowerInteractor.wakeUpIfDozing(
-                                    "LOCK_ICON_TOUCH",
-                                    PowerManager.WAKE_REASON_GESTURE);
-                        }
-                );
 
                 // In case we start outside of the view bounds (below the status bar), we need to
                 // dispatch the touch manually as the view system can't accommodate for touches
@@ -415,8 +412,18 @@
 
             private boolean shouldInterceptTouchEventInternal(MotionEvent ev) {
                 mLastInterceptWasDragDownHelper = false;
-                if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
-                        && !mDockManager.isDocked()) {
+                // When the device starts dozing, there's a delay before the device's display state
+                // changes from ON => DOZE to allow for the light reveal animation to run at
+                // a higher refresh rate and to delay visual changes (ie: display blink) when
+                // changing the display state. We'll call this specific state the
+                // "aodDefermentState". In this state we:
+                //     - don't want touches to get sent to underlying views, except the lock icon
+                //     - handle the tap to wake gesture via the PulsingGestureListener
+                if (mStatusBarStateController.isDozing()
+                        && !mDozeServiceHost.isPulsing()
+                        && !mDockManager.isDocked()
+                        && !mLockIconViewController.willHandleTouchWhileDozing(ev)
+                ) {
                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                         mShadeLogger.d("NSWVC: capture all touch events in always-on");
                     }
@@ -432,16 +439,15 @@
                     return true;
                 }
 
-                if (mLockIconViewController.onInterceptTouchEvent(ev)) {
-                    // immediately return true; don't send the touch to the drag down helper
-                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                        mShadeLogger.d("NSWVC: don't send touch to drag down helper");
-                    }
-                    return true;
+                boolean bouncerShowing;
+                if (mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+                    bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
+                            || mAlternateBouncerInteractor.isVisibleState();
+                } else {
+                    bouncerShowing = mService.isBouncerShowing();
                 }
-
                 if (mNotificationPanelViewController.isFullyExpanded()
-                        && !mService.isBouncerShowing()
+                        && !bouncerShowing
                         && !mStatusBarStateController.isDozing()) {
                     if (mDragDownHelper.isDragDownEnabled()) {
                         // This handles drag down over lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index 447a15d..2c4b0b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -178,9 +178,6 @@
 
     /** Listens for shade visibility changes. */
     interface ShadeVisibilityListener {
-        /** Called when the visibility of the shade changes. */
-        void visibilityChanged(boolean visible);
-
         /** Called when shade expanded and visible state changed. */
         void expandedVisibleChanged(boolean expandedVisible);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index 367449b..fdc7eec 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -24,6 +24,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import com.android.systemui.DejankUtils;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -75,6 +76,7 @@
     private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
 
     private boolean mExpandedVisible;
+    private boolean mLockscreenOrShadeVisible;
 
     private NotificationPresenter mPresenter;
     private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -399,8 +401,19 @@
     }
 
     private void notifyVisibilityChanged(boolean visible) {
-        mShadeVisibilityListener.visibilityChanged(visible);
         mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible);
+        if (mLockscreenOrShadeVisible != visible) {
+            mLockscreenOrShadeVisible = visible;
+            if (visible) {
+                // It would be best if this could be done as a side effect of listening to the
+                // [WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible] flow inside
+                // NotificationShadeWindowViewController. However, there's no guarantee that the
+                // flow will emit in the same frame as when the visibility changed, and we want the
+                // DejankUtils to be notified immediately, so we do it immediately here.
+                DejankUtils.notifyRendererOfExpensiveFrame(
+                        getNotificationShadeWindowView(), "onShadeVisibilityChanged");
+            }
+        }
     }
 
     private void notifyExpandedVisibleChanged(boolean expandedVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index 6ee6cbf..4e23e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.statusbar.NotificationShelfController
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.HeadsUpManager
 
 /**
  * Allows CentralSurfacesImpl to interact with the shade. Only CentralSurfacesImpl should reference
@@ -34,7 +34,7 @@
         recorder: GestureRecorder,
         hideExpandedRunnable: Runnable,
         notificationShelfController: NotificationShelfController,
-        headsUpManager: HeadsUpManagerPhone
+        headsUpManager: HeadsUpManager
     )
 
     /** Cancels any pending collapses. */
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 ac8333a..6117f9f 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
@@ -19,7 +19,11 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -27,8 +31,9 @@
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
@@ -56,13 +61,16 @@
 @Inject
 constructor(
     @Application scope: CoroutineScope,
+    deviceProvisioningRepository: DeviceProvisioningRepository,
     disableFlagsRepository: DisableFlagsRepository,
+    dozeParams: DozeParameters,
     sceneContainerFlags: SceneContainerFlags,
     // TODO(b/300258424) convert to direct reference instead of provider
     sceneInteractorProvider: Provider<SceneInteractor>,
     keyguardRepository: KeyguardRepository,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    powerInteractor: PowerInteractor,
     userSetupRepository: UserSetupRepository,
-    deviceProvisionedController: DeviceProvisionedController,
     userInteractor: UserInteractor,
     sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
     repository: ShadeRepository,
@@ -187,6 +195,26 @@
         combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
             .distinctUntilChanged()
 
+    /** Are touches allowed on the notification panel? */
+    val isShadeTouchable: Flow<Boolean> =
+        combine(
+            powerInteractor.isAsleep,
+            keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
+            keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
+            deviceProvisioningRepository.isFactoryResetProtectionActive,
+        ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
+            when {
+                // Touches are disabled when Factory Reset Protection is active
+                isFrpActive -> false
+                // If the device is going to sleep, only accept touches if we're still
+                // animating
+                goingToSleep -> dozeParams.shouldControlScreenOff()
+                // If the device is asleep, only accept touches if there's a pulse
+                isAsleep -> isPulsing
+                else -> true
+            }
+        }
+
     /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
     val isExpandToQsEnabled: Flow<Boolean> =
         combine(
@@ -194,8 +222,9 @@
             isShadeEnabled,
             keyguardRepository.isDozing,
             userSetupRepository.isUserSetupFlow,
-        ) { disableFlags, isShadeEnabled, isDozing, isUserSetup ->
-            deviceProvisionedController.isDeviceProvisioned &&
+            deviceProvisioningRepository.isDeviceProvisioned,
+        ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
+            isDeviceProvisioned &&
                 // Disallow QS during setup if it's a simple user switcher. (The user intends to
                 // use the lock screen user switcher, QS is not needed.)
                 (isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
@@ -232,7 +261,7 @@
                 when (state) {
                     is ObservableTransitionState.Idle -> false
                     is ObservableTransitionState.Transition ->
-                        state.isUserInputDriven &&
+                        state.isInitiatedByUserInput &&
                             (state.toScene == sceneKey || state.fromScene == sceneKey)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index d24f9d8..77b0958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -18,6 +18,8 @@
 
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import com.android.app.animation.Interpolators;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -113,7 +115,16 @@
         fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
     }
 
+    public static void fadeIn(final View view, Runnable endRunnable) {
+        fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, endRunnable);
+    }
+
     public static void fadeIn(final View view, long duration, int delay) {
+        fadeIn(view, duration, delay, /* endRunnable= */ null);
+    }
+
+    public static void fadeIn(final View view, long duration, int delay,
+            @Nullable Runnable endRunnable) {
         view.animate().cancel();
         if (view.getVisibility() == View.INVISIBLE) {
             view.setAlpha(0.0f);
@@ -124,7 +135,7 @@
                 .setDuration(duration)
                 .setStartDelay(delay)
                 .setInterpolator(Interpolators.ALPHA_IN)
-                .withEndAction(null);
+                .withEndAction(endRunnable);
         if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) {
             view.animate().withLayer();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 3640ae0..4ea7026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -31,22 +31,22 @@
 import com.android.app.animation.Interpolators
 import com.android.systemui.Dumpable
 import com.android.systemui.Gefingerpoken
-import com.android.systemui.res.R
 import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeExpansionStateManager
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.HeadsUpManager
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.max
@@ -63,7 +63,7 @@
     context: Context,
     private val wakeUpCoordinator: NotificationWakeUpCoordinator,
     private val bypassController: KeyguardBypassController,
-    private val headsUpManager: HeadsUpManagerPhone,
+    private val headsUpManager: HeadsUpManager,
     private val roundnessManager: NotificationRoundnessManager,
     configurationController: ConfigurationController,
     private val statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index f616b91..3a4ad0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -51,6 +51,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.app.animation.Interpolators;
@@ -959,12 +960,17 @@
     }
 
     public void setDozing(boolean dozing, boolean fade, long delay) {
+        setDozing(dozing, fade, delay, /* onChildCompleted= */ null);
+    }
+
+    public void setDozing(boolean dozing, boolean fade, long delay,
+            @Nullable Runnable endRunnable) {
         mDozer.setDozing(f -> {
             mDozeAmount = f;
             updateDecorColor();
             updateIconColor();
             updateAllowAnimation();
-        }, dozing, fade, delay, this);
+        }, dozing, fade, delay, this, endRunnable);
     }
 
     private void updateAllowAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index ff40f70..91ca148 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -87,7 +87,7 @@
      */
     public void init() {
         if (mWifiPickerTracker == null) {
-            mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this);
+            mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this, TAG);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt
index ddbfcef..dc2ebe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt
@@ -23,8 +23,8 @@
 import android.os.SimpleClock
 import androidx.lifecycle.Lifecycle
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.ThreadFactory
 import com.android.systemui.util.time.SystemClock
 import com.android.wifitrackerlib.WifiPickerTracker
 import com.android.wifitrackerlib.WifiPickerTracker.WifiPickerTrackerCallback
@@ -46,7 +46,7 @@
     private val connectivityManager: ConnectivityManager,
     private val systemClock: SystemClock,
     @Main private val mainHandler: Handler,
-    @Background private val workerHandler: Handler,
+    private val threadFactory: ThreadFactory,
 ) {
     private val clock: Clock =
         object : SimpleClock(ZoneOffset.UTC) {
@@ -60,11 +60,13 @@
     /**
      * Creates a [WifiPickerTracker] instance.
      *
+     * @param name a name to identify the worker thread used for [WifiPickerTracker] operations.
      * @return a new [WifiPickerTracker] or null if [WifiManager] is null.
      */
     fun create(
         lifecycle: Lifecycle,
         listener: WifiPickerTrackerCallback,
+        name: String,
     ): WifiPickerTracker? {
         return if (wifiManager == null) {
             null
@@ -75,7 +77,10 @@
                 wifiManager,
                 connectivityManager,
                 mainHandler,
-                workerHandler,
+                // WifiPickerTracker can take tens of seconds to finish operations, so it can't use
+                // the default background handler (it would block all other background operations).
+                // Use a custom handler instead.
+                threadFactory.buildHandlerOnNewThread("WifiPickerTracker-$name"),
                 clock,
                 MAX_SCAN_AGE_MILLIS,
                 SCAN_INTERVAL_MILLIS,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index 167efc7..dc0eb7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -24,6 +24,8 @@
 import android.view.View;
 import android.widget.ImageView;
 
+import androidx.annotation.Nullable;
+
 import com.android.app.animation.Interpolators;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -81,6 +83,11 @@
 
     public void setDozing(Consumer<Float> listener, boolean dozing,
             boolean animate, long delay, View view) {
+        setDozing(listener, dozing, animate, delay, view, /* endRunnable= */ null);
+    }
+
+    public void setDozing(Consumer<Float> listener, boolean dozing,
+            boolean animate, long delay, View view, @Nullable Runnable endRunnable) {
         if (animate) {
             startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dozing,
                     delay,
@@ -89,6 +96,9 @@
                         @Override
                         public void onAnimationEnd(Animator animation) {
                             view.setTag(DOZE_ANIMATOR_TAG, null);
+                            if (endRunnable != null) {
+                                endRunnable.run();
+                            }
                         }
 
                         @Override
@@ -102,6 +112,9 @@
                 animator.cancel();
             }
             listener.accept(dozing ? 1f : 0f);
+            if (endRunnable != null) {
+                endRunnable.run();
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index c62546f..756151b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.HeadsUpUtil
 import kotlin.math.ceil
 import kotlin.math.max
@@ -35,7 +35,7 @@
 class NotificationLaunchAnimatorControllerProvider(
     private val notificationExpansionRepository: NotificationExpansionRepository,
     private val notificationListContainer: NotificationListContainer,
-    private val headsUpManager: HeadsUpManagerPhone,
+    private val headsUpManager: HeadsUpManager,
     private val jankMonitor: InteractionJankMonitor
 ) {
     @JvmOverloads
@@ -62,7 +62,7 @@
 class NotificationLaunchAnimatorController(
     private val notificationExpansionRepository: NotificationExpansionRepository,
     private val notificationListContainer: NotificationListContainer,
-    private val headsUpManager: HeadsUpManagerPhone,
+    private val headsUpManager: HeadsUpManager,
     private val notification: ExpandableNotificationRow,
     private val jankMonitor: InteractionJankMonitor,
     private val onFinishAnimationCallback: Runnable?
@@ -152,16 +152,17 @@
         }
     }
 
-    private val headsUpNotificationRow: ExpandableNotificationRow? get() {
-        val summaryEntry = notificationEntry.parent?.summary
+    private val headsUpNotificationRow: ExpandableNotificationRow?
+        get() {
+            val summaryEntry = notificationEntry.parent?.summary
 
-        return when {
-            headsUpManager.isAlerting(notificationKey) -> notification
-            summaryEntry == null -> null
-            headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row
-            else -> null
+            return when {
+                headsUpManager.isAlerting(notificationKey) -> notification
+                summaryEntry == null -> null
+                headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row
+                else -> null
+            }
         }
-    }
 
     private fun removeHun(animate: Boolean) {
         val row = headsUpNotificationRow ?: return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 07eb8a00..2d83970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -37,8 +37,8 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.asIndenting
@@ -85,7 +85,7 @@
     @Application private val scope: CoroutineScope,
     private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
     private val secureSettings: SecureSettings,
-    private val seenNotifsProvider: SeenNotificationsProviderImpl,
+    private val notificationListInteractor: NotificationListInteractor,
     private val statusBarStateController: StatusBarStateController,
 ) : Coordinator, Dumpable {
 
@@ -351,7 +351,7 @@
 
             override fun onCleanup() {
                 logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs)
-                seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs
+                notificationListInteractor.setHasFilteredOutSeenNotifications(hasFilteredAnyNotifs)
                 hasFilteredAnyNotifs = false
             }
         }
@@ -388,8 +388,8 @@
     override fun dump(pw: PrintWriter, args: Array<out String>) =
         with(pw.asIndenting()) {
             println(
-                "seenNotifsProvider.hasFilteredOutSeenNotifications=" +
-                    seenNotifsProvider.hasFilteredOutSeenNotifications
+                "notificationListInteractor.hasFilteredOutSeenNotifications.value=" +
+                    notificationListInteractor.hasFilteredOutSeenNotifications.value
             )
             println("unseen notifications:")
             indentIfPossible {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 657c394d..c0f674846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.Compile
+import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
 /**
@@ -122,8 +123,10 @@
 
     private fun updateNotificationsOnUiModeChanged() {
         log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
-        mPipeline?.allNotifs?.forEach { entry ->
-            entry.row?.onUiModeChanged()
+        traceSection("updateNotifOnUiModeChanged") {
+            mPipeline?.allNotifs?.forEach { entry ->
+                entry.row?.onUiModeChanged()
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt
deleted file mode 100644
index cff47e2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt
+++ /dev/null
@@ -1,41 +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.statusbar.notification.collection.provider
-
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-
-/** Keeps track of whether "seen" notification content has been filtered out of the shade. */
-interface SeenNotificationsProvider {
-    /** Are any already-seen notifications currently filtered out of the shade? */
-    val hasFilteredOutSeenNotifications: Boolean
-}
-
-@Module
-interface SeenNotificationsProviderModule {
-    @Binds
-    fun bindSeenNotificationsProvider(
-        impl: SeenNotificationsProviderImpl
-    ): SeenNotificationsProvider
-}
-
-@SysUISingleton
-class SeenNotificationsProviderImpl @Inject constructor() : SeenNotificationsProvider {
-    override var hasFilteredOutSeenNotifications: Boolean = false
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 59fc387..1a88815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -231,18 +231,24 @@
     fun getChildCount(): Int = controller.getChildCount()
 
     fun addChildAt(child: ShadeNode, index: Int) {
-        controller.addChildAt(child.controller, index)
-        child.controller.onViewAdded()
+        traceSection("ShadeNode#addChildAt") {
+            controller.addChildAt(child.controller, index)
+            child.controller.onViewAdded()
+        }
     }
 
     fun moveChildTo(child: ShadeNode, index: Int) {
-        controller.moveChildTo(child.controller, index)
-        child.controller.onViewMoved()
+        traceSection("ShadeNode#moveChildTo") {
+            controller.moveChildTo(child.controller, index)
+            child.controller.onViewMoved()
+        }
     }
 
     fun removeChild(child: ShadeNode, isTransfer: Boolean) {
-        controller.removeChild(child.controller, isTransfer)
-        child.controller.onViewRemoved()
+        traceSection("ShadeNode#removeChild") {
+            controller.removeChild(child.controller, isTransfer)
+            child.controller.onViewRemoved()
+        }
     }
 
     fun offerToKeepInParentForAnimation(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 8ee0de6..8561869 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -20,10 +20,10 @@
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.shade.ShadeEventsModule;
 import com.android.systemui.statusbar.NotificationListener;
@@ -45,7 +45,6 @@
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProviderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
@@ -54,6 +53,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.data.NotificationDataLayerModule;
 import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
 import com.android.systemui.statusbar.notification.icon.ConversationIconManager;
 import com.android.systemui.statusbar.notification.icon.IconManager;
@@ -74,9 +74,9 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
 import dagger.Binds;
@@ -95,8 +95,8 @@
 @Module(includes = {
         CoordinatorsModule.class,
         KeyguardNotificationVisibilityProviderModule.class,
-        SeenNotificationsProviderModule.class,
         ShadeEventsModule.class,
+        NotificationDataLayerModule.class,
         NotifPipelineChoreographerModule.class,
         NotificationSectionHeadersModule.class,
         NotificationListViewModelModule.class,
@@ -206,7 +206,7 @@
     static NotificationLaunchAnimatorControllerProvider provideNotifLaunchAnimControllerProvider(
             NotificationExpansionRepository notificationExpansionRepository,
             NotificationListContainer notificationListContainer,
-            HeadsUpManagerPhone headsUpManager,
+            HeadsUpManager headsUpManager,
             InteractionJankMonitor jankMonitor) {
         return new NotificationLaunchAnimatorControllerProvider(
                 notificationExpansionRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
new file mode 100644
index 0000000..5435fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.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.statusbar.notification.data
+
+import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardStateRepositoryModule
+import dagger.Module
+
+@Module(includes = [NotificationsKeyguardStateRepositoryModule::class])
+interface NotificationDataLayerModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
new file mode 100644
index 0000000..cf03d1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** View-states pertaining to notifications on the keyguard. */
+interface NotificationsKeyguardViewStateRepository {
+    /** Are notifications fully hidden from view? */
+    val areNotificationsFullyHidden: Flow<Boolean>
+
+    /** Is a pulse expansion occurring? */
+    val isPulseExpanding: Flow<Boolean>
+}
+
+@Module
+interface NotificationsKeyguardStateRepositoryModule {
+    @Binds
+    fun bindImpl(
+        impl: NotificationsKeyguardViewStateRepositoryImpl
+    ): NotificationsKeyguardViewStateRepository
+}
+
+@SysUISingleton
+class NotificationsKeyguardViewStateRepositoryImpl
+@Inject
+constructor(
+    wakeUpCoordinator: NotificationWakeUpCoordinator,
+) : NotificationsKeyguardViewStateRepository {
+    override val areNotificationsFullyHidden: Flow<Boolean> = conflatedCallbackFlow {
+        val listener =
+            object : NotificationWakeUpCoordinator.WakeUpListener {
+                override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
+                    trySend(isFullyHidden)
+                }
+            }
+        trySend(wakeUpCoordinator.notificationsFullyHidden)
+        wakeUpCoordinator.addListener(listener)
+        awaitClose { wakeUpCoordinator.removeListener(listener) }
+    }
+
+    override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow {
+        val listener =
+            object : NotificationWakeUpCoordinator.WakeUpListener {
+                override fun onPulseExpansionChanged(expandingChanged: Boolean) {
+                    trySend(expandingChanged)
+                }
+            }
+        trySend(wakeUpCoordinator.isPulseExpanding())
+        wakeUpCoordinator.addListener(listener)
+        awaitClose { wakeUpCoordinator.removeListener(listener) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
new file mode 100644
index 0000000..87b8e55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Domain logic pertaining to notifications on the keyguard. */
+class NotificationsKeyguardInteractor
+@Inject
+constructor(
+    repository: NotificationsKeyguardViewStateRepository,
+) {
+    /** Is a pulse expansion occurring? */
+    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
+
+    /** Are notifications fully hidden from view? */
+    val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
+}
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 20241c3..805a4db 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
@@ -26,25 +26,21 @@
 import androidx.annotation.ColorInt
 import androidx.annotation.VisibleForTesting
 import androidx.collection.ArrayMap
-import com.android.app.animation.Interpolators
 import com.android.internal.statusbar.StatusBarIcon
 import com.android.internal.util.ContrastColorUtil
 import com.android.settingslib.Utils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.ViewRefactorFlag
 import com.android.systemui.plugins.DarkIconDispatcher
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.CrossFadeHelper
 import com.android.systemui.statusbar.NotificationListener
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.NotificationShelfController
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.NotificationUtils
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -59,6 +55,7 @@
 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.statusbar.window.StatusBarWindowController
 import com.android.wm.shell.bubbles.Bubbles
 import java.util.Optional
@@ -78,9 +75,9 @@
 @Inject
 constructor(
     private val context: Context,
-    private val statusBarStateController: StatusBarStateController,
     private val wakeUpCoordinator: NotificationWakeUpCoordinator,
     private val bypassController: KeyguardBypassController,
+    private val configurationController: ConfigurationController,
     private val mediaManager: NotificationMediaManager,
     notificationListener: NotificationListener,
     private val dozeParameters: DozeParameters,
@@ -88,7 +85,7 @@
     private val bubblesOptional: Optional<Bubbles>,
     demoModeController: DemoModeController,
     darkIconDispatcher: DarkIconDispatcher,
-    featureFlags: FeatureFlags,
+    private val featureFlags: FeatureFlagsClassic,
     private val statusBarWindowController: StatusBarWindowController,
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val shelfIconsViewModel: NotificationIconContainerShelfViewModel,
@@ -97,14 +94,12 @@
 ) :
     NotificationIconAreaController,
     DarkIconDispatcher.DarkReceiver,
-    StatusBarStateController.StateListener,
     NotificationWakeUpCoordinator.WakeUpListener,
     DemoMode {
 
     private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context)
     private val updateStatusBarIcons = Runnable { updateStatusBarIcons() }
     private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
-    private val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
     private val tintAreas = ArrayList<Rect>()
 
     private var iconSize = 0
@@ -117,9 +112,7 @@
     private var aodIcons: NotificationIconContainer? = null
     private var aodBindJob: DisposableHandle? = null
     private var aodIconAppearTranslation = 0
-    private var animationsEnabled = false
     private var aodIconTint = 0
-    private var aodIconsVisible = false
     private var showLowPriority = true
 
     @VisibleForTesting
@@ -132,7 +125,6 @@
         }
 
     init {
-        statusBarStateController.addCallback(this)
         wakeUpCoordinator.addListener(this)
         demoModeController.addCallback(this)
         notificationListener.addNotificationSettingsListener(settingsListener)
@@ -156,9 +148,15 @@
         }
         this.aodIcons = aodIcons
         this.aodIcons!!.setOnLockScreen(true)
-        aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel)
-        updateAodIconsVisibility(animate = false, forceUpdate = changed)
-        updateAnimations()
+        aodBindJob =
+            NotificationIconContainerViewBinder.bind(
+                aodIcons,
+                aodIconsViewModel,
+                configurationController,
+                dozeParameters,
+                featureFlags,
+                screenOffAnimationController,
+            )
         if (changed) {
             updateAodNotificationIcons()
         }
@@ -170,7 +168,14 @@
 
     override fun setShelfIcons(icons: NotificationIconContainer) {
         if (shelfRefactor.expectEnabled()) {
-            NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel)
+            NotificationIconContainerViewBinder.bind(
+                icons,
+                shelfIconsViewModel,
+                configurationController,
+                dozeParameters,
+                featureFlags,
+                screenOffAnimationController,
+            )
             shelfIcons = icons
         }
     }
@@ -243,23 +248,7 @@
         notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate)
     }
 
-    override fun onDozingChanged(isDozing: Boolean) {
-        if (aodIcons == null) {
-            return
-        }
-        val animate = (dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking)
-        aodIcons!!.setDozing(isDozing, animate, 0)
-    }
-
-    override fun setAnimationsEnabled(enabled: Boolean) {
-        animationsEnabled = enabled
-        updateAnimations()
-    }
-
-    override fun onStateChanged(newState: Int) {
-        updateAodIconsVisibility(animate = false, forceUpdate = false)
-        updateAnimations()
-    }
+    override fun setAnimationsEnabled(enabled: Boolean) = unsupported
 
     override fun onThemeChanged() {
         reloadAodColor()
@@ -270,50 +259,11 @@
         return if (aodIcons == null) 0 else aodIcons!!.height
     }
 
-    @VisibleForTesting
-    fun appearAodIcons() {
-        if (aodIcons == null) {
-            return
-        }
-        if (screenOffAnimationController.shouldAnimateAodIcons()) {
-            if (!statusViewMigrated) {
-                aodIcons!!.translationY = -aodIconAppearTranslation.toFloat()
-            }
-            aodIcons!!.alpha = 0f
-            animateInAodIconTranslation()
-            aodIcons!!
-                .animate()
-                .alpha(1f)
-                .setInterpolator(Interpolators.LINEAR)
-                .setDuration(AOD_ICONS_APPEAR_DURATION)
-                .start()
-        } else {
-            aodIcons!!.alpha = 1.0f
-            if (!statusViewMigrated) {
-                aodIcons!!.translationY = 0f
-            }
-        }
-    }
-
     override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
-        var animate = true
-        if (!bypassController.bypassEnabled) {
-            animate = dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
-            // We only want the appear animations to happen when the notifications get fully hidden,
-            // since otherwise the unhide animation overlaps
-            animate = animate and isFullyHidden
-        }
-        updateAodIconsVisibility(animate, false /* force */)
         updateAodNotificationIcons()
         updateAodIconColors()
     }
 
-    override fun onPulseExpansionChanged(expandingChanged: Boolean) {
-        if (expandingChanged) {
-            updateAodIconsVisibility(animate = true, forceUpdate = false)
-        }
-    }
-
     override fun demoCommands(): List<String> {
         val commands = ArrayList<String>()
         commands.add(DemoMode.COMMAND_NOTIFICATIONS)
@@ -344,7 +294,14 @@
         val layoutInflater = LayoutInflater.from(context)
         notificationIconArea = inflateIconArea(layoutInflater)
         notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons)
-        NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel)
+        NotificationIconContainerViewBinder.bind(
+            notificationIcons!!,
+            statusBarIconsViewModel,
+            configurationController,
+            dozeParameters,
+            featureFlags,
+            screenOffAnimationController,
+        )
     }
 
     private fun updateIconLayoutParams(context: Context) {
@@ -594,25 +551,6 @@
         v.setDecorColor(tint)
     }
 
-    private fun updateAnimations() {
-        val inShade = statusBarStateController.state == StatusBarState.SHADE
-        if (aodIcons != null) {
-            aodIcons!!.setAnimationsEnabled(animationsEnabled && !inShade)
-        }
-        notificationIcons!!.setAnimationsEnabled(animationsEnabled && inShade)
-    }
-
-    private fun animateInAodIconTranslation() {
-        if (!statusViewMigrated) {
-            aodIcons!!
-                .animate()
-                .setInterpolator(Interpolators.DECELERATE_QUINT)
-                .translationY(0f)
-                .setDuration(AOD_ICONS_APPEAR_DURATION)
-                .start()
-        }
-    }
-
     private fun reloadAodColor() {
         aodIconTint =
             Utils.getColorAttrDefaultColor(
@@ -635,59 +573,13 @@
         }
     }
 
-    private fun updateAodIconsVisibility(animate: Boolean, forceUpdate: Boolean) {
-        if (aodIcons == null) {
-            return
-        }
-        var visible = (bypassController.bypassEnabled || wakeUpCoordinator.notificationsFullyHidden)
-
-        // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is
-        // playing, in which case we want them to be visible since we're animating in the AOD UI and
-        // will be switching to KEYGUARD shortly.
-        if (
-            statusBarStateController.state != StatusBarState.KEYGUARD &&
-                !screenOffAnimationController.shouldShowAodIconsWhenShade()
-        ) {
-            visible = false
-        }
-        if (visible && wakeUpCoordinator.isPulseExpanding() && !bypassController.bypassEnabled) {
-            visible = false
-        }
-        if (aodIconsVisible != visible || forceUpdate) {
-            aodIconsVisible = visible
-            aodIcons!!.animate().cancel()
-            if (animate) {
-                val wasFullyInvisible = aodIcons!!.visibility != View.VISIBLE
-                if (aodIconsVisible) {
-                    if (wasFullyInvisible) {
-                        // No fading here, let's just appear the icons instead!
-                        aodIcons!!.visibility = View.VISIBLE
-                        aodIcons!!.alpha = 1.0f
-                        appearAodIcons()
-                    } else {
-                        // Let's make sure the icon are translated to 0, since we cancelled it above
-                        animateInAodIconTranslation()
-                        // We were fading out, let's fade in instead
-                        CrossFadeHelper.fadeIn(aodIcons)
-                    }
-                } else {
-                    // Let's make sure the icon are translated to 0, since we cancelled it above
-                    animateInAodIconTranslation()
-                    CrossFadeHelper.fadeOut(aodIcons)
-                }
-            } else {
-                aodIcons!!.alpha = 1.0f
-                if (!statusViewMigrated) {
-                    aodIcons!!.translationY = 0f
-                }
-                aodIcons!!.visibility = if (visible) View.VISIBLE else View.INVISIBLE
-            }
-        }
-    }
-
     companion object {
-        private const val AOD_ICONS_APPEAR_DURATION: Long = 200
-
         @ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1
+
+        val unsupported: Nothing
+            get() =
+                error(
+                    "Code path not supported when NOTIFICATION_ICON_CONTAINER_REFACTOR 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 8293bb3..0d2f00a 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
@@ -15,19 +15,172 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewbinder
 
+import android.content.res.Resources
+import android.view.View
+import androidx.annotation.DimenRes
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.CrossFadeHelper
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel
+import com.android.systemui.statusbar.phone.DozeParameters
 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.statusbar.policy.onDensityOrFontScaleChanged
+import com.android.systemui.util.kotlin.stateFlow
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
 
 /** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */
 object NotificationIconContainerViewBinder {
     fun bind(
         view: NotificationIconContainer,
         viewModel: NotificationIconContainerViewModel,
+        configurationController: ConfigurationController,
+        dozeParameters: DozeParameters,
+        featureFlags: FeatureFlagsClassic,
+        screenOffAnimationController: ScreenOffAnimationController,
     ): DisposableHandle {
-        return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} }
+        return view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.CREATED) {
+                launch { viewModel.animationsEnabled.collect(view::setAnimationsEnabled) }
+                launch {
+                    viewModel.isDozing.collect { (isDozing, animate) ->
+                        val animateIfNotBlanking = animate && !dozeParameters.displayNeedsBlanking
+                        view.setDozing(isDozing, animateIfNotBlanking, /* delay= */ 0) {
+                            viewModel.completeDozeAnimation()
+                        }
+                    }
+                }
+                // TODO(278765923): this should live where AOD is bound, not inside of the NIC
+                //  view-binder
+                launch {
+                    val iconAppearTranslation =
+                        view.resources.getConfigAwareDimensionPixelSize(
+                            this,
+                            configurationController,
+                            R.dimen.shelf_appear_translation,
+                        )
+                    bindVisibility(
+                        viewModel,
+                        view,
+                        featureFlags,
+                        screenOffAnimationController,
+                        iconAppearTranslation,
+                    ) {
+                        viewModel.completeVisibilityAnimation()
+                    }
+                }
+            }
+        }
     }
+    private suspend fun bindVisibility(
+        viewModel: NotificationIconContainerViewModel,
+        view: NotificationIconContainer,
+        featureFlags: FeatureFlagsClassic,
+        screenOffAnimationController: ScreenOffAnimationController,
+        iconAppearTranslation: StateFlow<Int>,
+        onAnimationEnd: () -> Unit,
+    ) {
+        val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
+        viewModel.isVisible.collect { (isVisible, animate) ->
+            view.animate().cancel()
+            when {
+                !animate -> {
+                    view.alpha = 1f
+                    if (!statusViewMigrated) {
+                        view.translationY = 0f
+                    }
+                    view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
+                }
+                featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
+                    animateInIconTranslation(view, statusViewMigrated)
+                    if (isVisible) {
+                        CrossFadeHelper.fadeIn(view, onAnimationEnd)
+                    } else {
+                        CrossFadeHelper.fadeOut(view, onAnimationEnd)
+                    }
+                }
+                !isVisible -> {
+                    // Let's make sure the icon are translated to 0, since we cancelled it above
+                    animateInIconTranslation(view, statusViewMigrated)
+                    CrossFadeHelper.fadeOut(view, onAnimationEnd)
+                }
+                view.visibility != View.VISIBLE -> {
+                    // No fading here, let's just appear the icons instead!
+                    view.visibility = View.VISIBLE
+                    view.alpha = 1f
+                    appearIcons(
+                        view,
+                        animate = screenOffAnimationController.shouldAnimateAodIcons(),
+                        iconAppearTranslation.value,
+                        statusViewMigrated,
+                    )
+                    onAnimationEnd()
+                }
+                else -> {
+                    // Let's make sure the icons are translated to 0, since we cancelled it above
+                    animateInIconTranslation(view, statusViewMigrated)
+                    // We were fading out, let's fade in instead
+                    CrossFadeHelper.fadeIn(view, onAnimationEnd)
+                }
+            }
+        }
+    }
+
+    private fun appearIcons(
+        view: View,
+        animate: Boolean,
+        iconAppearTranslation: Int,
+        statusViewMigrated: Boolean,
+    ) {
+        if (animate) {
+            if (!statusViewMigrated) {
+                view.translationY = -iconAppearTranslation.toFloat()
+            }
+            view.alpha = 0f
+            animateInIconTranslation(view, statusViewMigrated)
+            view
+                .animate()
+                .alpha(1f)
+                .setInterpolator(Interpolators.LINEAR)
+                .setDuration(AOD_ICONS_APPEAR_DURATION)
+                .start()
+        } else {
+            view.alpha = 1.0f
+            if (!statusViewMigrated) {
+                view.translationY = 0f
+            }
+        }
+    }
+
+    private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) {
+        if (!statusViewMigrated) {
+            view
+                .animate()
+                .setInterpolator(Interpolators.DECELERATE_QUINT)
+                .translationY(0f)
+                .setDuration(AOD_ICONS_APPEAR_DURATION)
+                .start()
+        }
+    }
+
+    private const val AOD_ICONS_APPEAR_DURATION: Long = 200
 }
+
+fun Resources.getConfigAwareDimensionPixelSize(
+    scope: CoroutineScope,
+    configurationController: ConfigurationController,
+    @DimenRes id: Int,
+): StateFlow<Int> =
+    scope.stateFlow(
+        changedSignals = configurationController.onDensityOrFontScaleChanged,
+        getValue = { getDimensionPixelSize(id) }
+    )
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 f68b0ef..3289a3c 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,8 +15,146 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+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.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.ui.AnimatableEvent
+import com.android.systemui.util.ui.AnimatedValue
+import com.android.systemui.util.ui.toAnimatedValueFlow
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
 
 /** View-model for the row of notification icons displayed on the always-on display. */
-class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() :
-    NotificationIconContainerViewModel
+@SysUISingleton
+class NotificationIconContainerAlwaysOnDisplayViewModel
+@Inject
+constructor(
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+    private val dozeParameters: DozeParameters,
+    private val featureFlags: FeatureFlagsClassic,
+    keyguardInteractor: KeyguardInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+    screenOffAnimationController: ScreenOffAnimationController,
+    shadeInteractor: ShadeInteractor,
+) : NotificationIconContainerViewModel {
+
+    private val onDozeAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+    private val onVisAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+
+    override val animationsEnabled: Flow<Boolean> =
+        combine(
+            shadeInteractor.isShadeTouchable,
+            keyguardInteractor.isKeyguardVisible,
+        ) { panelTouchesEnabled, isKeyguardVisible ->
+            panelTouchesEnabled && isKeyguardVisible
+        }
+
+    override val isDozing: Flow<AnimatedValue<Boolean>> =
+        keyguardTransitionInteractor.startedKeyguardTransitionStep
+            // Determine if we're dozing based on the most recent transition
+            .map { step: TransitionStep ->
+                val isDozing = step.to == KeyguardState.AOD || step.to == KeyguardState.DOZING
+                isDozing to step
+            }
+            // Only emit changes based on whether we've started or stopped dozing
+            .distinctUntilChanged { (wasDozing, _), (isDozing, _) -> wasDozing != isDozing }
+            // Determine whether we need to animate
+            .map { (isDozing, step) ->
+                val animate = step.to == KeyguardState.AOD || step.from == KeyguardState.AOD
+                AnimatableEvent(isDozing, animate)
+            }
+            .distinctUntilChanged()
+            .toAnimatedValueFlow(completionEvents = onDozeAnimationComplete)
+
+    override val isVisible: Flow<AnimatedValue<Boolean>> =
+        combine(
+                keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE },
+                deviceEntryInteractor.isBypassEnabled,
+                areNotifsFullyHiddenAnimated(),
+                isPulseExpandingAnimated(),
+            ) {
+                onKeyguard: Boolean,
+                bypassEnabled: Boolean,
+                (notifsFullyHidden: Boolean, isAnimatingHide: Boolean),
+                (pulseExpanding: Boolean, isAnimatingPulse: Boolean),
+                ->
+                val isAnimating = isAnimatingHide || isAnimatingPulse
+                when {
+                    // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
+                    // animation is playing, in which case we want them to be visible if we're
+                    // animating in the AOD UI and will be switching to KEYGUARD shortly.
+                    !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() ->
+                        AnimatedValue(false, isAnimating = false)
+                    // If we're bypassing, then we're visible
+                    bypassEnabled -> AnimatedValue(true, isAnimating)
+                    // If we are pulsing (and not bypassing), then we are hidden
+                    pulseExpanding -> AnimatedValue(false, isAnimating)
+                    // If notifs are fully gone, then we're visible
+                    notifsFullyHidden -> AnimatedValue(true, isAnimating)
+                    // Otherwise, we're hidden
+                    else -> AnimatedValue(false, isAnimating)
+                }
+            }
+            .distinctUntilChanged()
+
+    override fun completeDozeAnimation() {
+        onDozeAnimationComplete.tryEmit(Unit)
+    }
+
+    override fun completeVisibilityAnimation() {
+        onVisAnimationComplete.tryEmit(Unit)
+    }
+
+    /** Is there an expanded pulse, are we animating in response? */
+    private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
+        return notificationsKeyguardInteractor.isPulseExpanding
+            .pairwise(initialValue = null)
+            // If pulsing changes, start animating, unless it's the first emission
+            .map { (prev, expanding) ->
+                AnimatableEvent(expanding!!, startAnimating = prev != null)
+            }
+            .toAnimatedValueFlow(completionEvents = onVisAnimationComplete)
+    }
+
+    /** Are notifications completely hidden from view, are we animating in response? */
+    private fun areNotifsFullyHiddenAnimated(): Flow<AnimatedValue<Boolean>> {
+        return notificationsKeyguardInteractor.areNotificationsFullyHidden
+            .pairwise(initialValue = null)
+            .sample(deviceEntryInteractor.isBypassEnabled) { (prev, fullyHidden), bypassEnabled ->
+                val animate =
+                    when {
+                        // Don't animate for the first value
+                        prev == null -> false
+                        // Always animate if bypass is enabled.
+                        bypassEnabled -> true
+                        // If we're not bypassing and we're not going to AOD, then we're not
+                        // animating.
+                        !dozeParameters.alwaysOn -> false
+                        // Don't animate when going to AOD if the display needs blanking.
+                        dozeParameters.displayNeedsBlanking -> false
+                        // We only want the appear animations to happen when the notifications
+                        // get fully hidden, since otherwise the un-hide animation overlaps.
+                        featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true
+                        else -> fullyHidden!!
+                    }
+                AnimatableEvent(fullyHidden!!, animate)
+            }
+            .toAnimatedValueFlow(completionEvents = onVisAnimationComplete)
+    }
+}
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 933c76f..c44a2b6 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,8 +15,18 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
+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
 
 /** View-model for the overflow row of notification icons displayed in the notification shade. */
 class NotificationIconContainerShelfViewModel @Inject constructor() :
-    NotificationIconContainerViewModel
+    NotificationIconContainerViewModel {
+    override val animationsEnabled: Flow<Boolean> = flowOf(true)
+    override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
+    override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
+    override fun completeDozeAnimation() {}
+    override fun completeVisibilityAnimation() {}
+}
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 2217646..035687a 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
@@ -15,8 +15,31 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.ui.AnimatedValue
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
 
 /** View-model for the row of notification icons displayed in the status bar, */
-class NotificationIconContainerStatusBarViewModel @Inject constructor() :
-    NotificationIconContainerViewModel
+class NotificationIconContainerStatusBarViewModel
+@Inject
+constructor(
+    keyguardInteractor: KeyguardInteractor,
+    shadeInteractor: ShadeInteractor,
+) : NotificationIconContainerViewModel {
+    override val animationsEnabled: Flow<Boolean> =
+        combine(
+            shadeInteractor.isShadeTouchable,
+            keyguardInteractor.isKeyguardShowing,
+        ) { panelTouchesEnabled, isKeyguardShowing ->
+            panelTouchesEnabled && !isKeyguardShowing
+        }
+
+    override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
+    override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
+    override fun completeDozeAnimation() {}
+    override fun completeVisibilityAnimation() {}
+}
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
index 892b2be..65eb220 100644
--- 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
@@ -15,8 +15,32 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
+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
+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>>
+
+    /**
+     * Signal completion of the [isDozing] animation; if [isDozing]'s [AnimatedValue.isAnimating]
+     * property was `true`, calling this method will update it to `false.
+     */
+    fun completeDozeAnimation()
+
+    /**
+     * Signal completion of the [isVisible] animation; if [isVisible]'s [AnimatedValue.isAnimating]
+     * property was `true`, calling this method will update it to `false.
+     */
+    fun completeVisibilityAnimation()
+}
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 c61258b..847d948 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
@@ -355,12 +355,8 @@
             AnimatorListenerAdapter animationListener) {
         enableAppearDrawing(true);
         mIsHeadsUpAnimation = isHeadsUpAnimation;
-        if (mDrawingAppearAnimation) {
-            startAppearAnimation(false /* isAppearing */, translationDirection,
-                    delay, duration, onFinishedRunnable, animationListener);
-        } else if (onFinishedRunnable != null) {
-            onFinishedRunnable.run();
-        }
+        startAppearAnimation(false /* isAppearing */, translationDirection,
+                delay, duration, onFinishedRunnable, animationListener);
         return 0;
     }
 
@@ -369,10 +365,8 @@
             Runnable onFinishRunnable) {
         enableAppearDrawing(true);
         mIsHeadsUpAnimation = isHeadsUpAppear;
-        if (mDrawingAppearAnimation) {
-            startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
-                    duration, null, null);
-        }
+        startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+                duration, null, null);
     }
 
     private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index d18f991..061132f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -772,6 +772,7 @@
     /**
      * @return if the view is in heads up state, i.e either still heads upped or it's disappearing.
      */
+    @Override
     public boolean isHeadsUpState() {
         return mIsHeadsUp || mHeadsupDisappearRunning;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 5aae488..f2f55a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -621,6 +621,10 @@
         return false;
     }
 
+    public boolean isHeadsUpState() {
+        return false;
+    }
+
     public boolean isChildInGroup() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 42c80ed..40897da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -42,9 +42,8 @@
 import android.widget.TextView;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
-import com.android.systemui.res.R;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.util.Compile;
@@ -76,7 +75,9 @@
             final StatusBarNotification sbn,
             final NotificationEntry entry,
             final ExpandableNotificationRow row,
-            final AssistantFeedbackController controller) {
+            final AssistantFeedbackController controller,
+            final IStatusBarService statusBarService,
+            final NotificationGutsManager notificationGutsManager) {
         mPkg = sbn.getPackageName();
         mPm = pm;
         mEntry = entry;
@@ -84,8 +85,8 @@
         mRanking = entry.getRanking();
         mFeedbackController = controller;
         mAppName = mPkg;
-        mStatusBarService = Dependency.get(IStatusBarService.class);
-        mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+        mStatusBarService = statusBarService;
+        mNotificationGutsManager = notificationGutsManager;
 
         bindHeader();
         bindPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6d656605..9e9116b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -44,9 +44,9 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -54,6 +54,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.shade.ShadeController;
@@ -69,8 +70,8 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.wmshell.BubblesManager;
 
@@ -99,6 +100,7 @@
     // Dependencies:
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final StatusBarStateController mStatusBarStateController;
+    private final IStatusBarService mStatusBarService;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final AssistantFeedbackController mAssistantFeedbackController;
 
@@ -127,7 +129,7 @@
     private final ShadeController mShadeController;
     private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
     private NotifGutsViewListener mGutsListener;
-    private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+    private final HeadsUpManager mHeadsUpManager;
     private final ActivityStarter mActivityStarter;
 
     @Inject
@@ -152,9 +154,10 @@
             WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             StatusBarStateController statusBarStateController,
+            IStatusBarService statusBarService,
             DeviceProvisionedController deviceProvisionedController,
             MetricsLogger metricsLogger,
-            HeadsUpManagerPhone headsUpManagerPhone,
+            HeadsUpManager headsUpManager,
             ActivityStarter activityStarter) {
         mContext = context;
         mMainHandler = mainHandler;
@@ -177,9 +180,10 @@
         mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
         mLockscreenUserManager = notificationLockscreenUserManager;
         mStatusBarStateController = statusBarStateController;
+        mStatusBarService = statusBarService;
         mDeviceProvisionedController = deviceProvisionedController;
         mMetricsLogger = metricsLogger;
-        mHeadsUpManagerPhone = headsUpManagerPhone;
+        mHeadsUpManager = headsUpManager;
         mActivityStarter = activityStarter;
     }
 
@@ -296,7 +300,7 @@
             if (mGutsListener != null) {
                 mGutsListener.onGutsClose(entry);
             }
-            mHeadsUpManagerPhone.setGutsShown(row.getEntry(), false);
+            mHeadsUpManager.setGutsShown(row.getEntry(), false);
         });
 
         View gutsView = item.getGutsView();
@@ -358,7 +362,8 @@
         PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext,
                 userHandle.getIdentifier());
 
-        feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController);
+        feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController,
+                mStatusBarService, this);
     }
 
     /**
@@ -676,7 +681,7 @@
                 row.closeRemoteInput();
                 mListContainer.onHeightChanged(row, true /* needsAnimation */);
                 mGutsMenuItem = menuItem;
-                mHeadsUpManagerPhone.setGutsShown(row.getEntry(), true);
+                mHeadsUpManager.setGutsShown(row.getEntry(), true);
             }
         };
         guts.post(mOpenRunnable);
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 28f0a0c..78d7558 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
@@ -203,6 +203,8 @@
     private final ViewRefactorFlag mAnimatedInsets;
     private final ViewRefactorFlag mShelfRefactor;
 
+    private final boolean mNewAodTransition;
+
     private int mContentHeight;
     private float mIntrinsicContentHeight;
     private int mPaddingBetweenElements;
@@ -485,8 +487,6 @@
     private ShadeController mShadeController;
     private Consumer<Boolean> mOnStackYChanged;
 
-    protected boolean mClearAllEnabled;
-
     private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
 
     private final NotificationSectionsManager mSectionsManager;
@@ -632,6 +632,7 @@
         mIsSmallLandscapeLockscreenEnabled = featureFlags.isEnabled(
                 Flags.LOCKSCREEN_ENABLE_LANDSCAPE);
         mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
+        mNewAodTransition = featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION);
         mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
         mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
         mAnimatedInsets =
@@ -670,7 +671,6 @@
             mDebugPaint.setStyle(Paint.Style.STROKE);
             mDebugPaint.setTextSize(25f);
         }
-        mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
         mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
         mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -758,8 +758,7 @@
     }
 
     private boolean shouldShowDismissView() {
-        return mClearAllEnabled
-                && mController.hasActiveClearableNotifications(ROWS_ALL);
+        return mController.hasActiveClearableNotifications(ROWS_ALL);
     }
 
     private boolean shouldShowFooterView(boolean showDismissView) {
@@ -1432,12 +1431,14 @@
 
     @VisibleForTesting
     public void updateStackHeight(float endHeight, float fraction) {
-        // During the (AOD<=>LS) transition where dozeAmount is changing,
-        // apply dozeAmount to stack height instead of expansionFraction
-        // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
-        final float dozeAmount = mAmbientState.getDozeAmount();
-        if (0f < dozeAmount && dozeAmount < 1f) {
-            fraction = 1f - dozeAmount;
+        if (!mNewAodTransition) {
+            // During the (AOD<=>LS) transition where dozeAmount is changing,
+            // apply dozeAmount to stack height instead of expansionFraction
+            // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
+            final float dozeAmount = mAmbientState.getDozeAmount();
+            if (0f < dozeAmount && dozeAmount < 1f) {
+                fraction = 1f - dozeAmount;
+            }
         }
         mAmbientState.setStackHeight(
                 MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION,
@@ -5173,7 +5174,6 @@
                     DumpUtilsKt.withIncreasedIndent(
                             pw,
                             () -> {
-                                pw.println("mClearAllEnabled: " + mClearAllEnabled);
                                 pw.println(
                                         "hasActiveClearableNotifications: "
                                                 + mController.hasActiveClearableNotifications(
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 66b2555..9695cb1 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
@@ -99,7 +99,6 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
@@ -115,10 +114,10 @@
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor;
 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.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -127,6 +126,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -156,7 +156,7 @@
     private final NotificationGutsManager mNotificationGutsManager;
     private final NotificationsController mNotificationsController;
     private final NotificationVisibilityProvider mVisibilityProvider;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final NotificationRoundnessManager mNotificationRoundnessManager;
     private final TunerService mTunerService;
     private final DeviceProvisionedController mDeviceProvisionedController;
@@ -194,7 +194,7 @@
 
     private final GroupExpansionManager mGroupExpansionManager;
     private final NotifPipelineFlags mNotifPipelineFlags;
-    private final SeenNotificationsProvider mSeenNotificationsProvider;
+    private final NotificationListInteractor mNotificationListInteractor;
     private final KeyguardTransitionRepository mKeyguardTransitionRepo;
 
     private NotificationStackScrollLayout mView;
@@ -631,7 +631,7 @@
             NotificationGutsManager notificationGutsManager,
             NotificationsController notificationsController,
             NotificationVisibilityProvider visibilityProvider,
-            HeadsUpManagerPhone headsUpManager,
+            HeadsUpManager headsUpManager,
             NotificationRoundnessManager notificationRoundnessManager,
             TunerService tunerService,
             DeviceProvisionedController deviceProvisionedController,
@@ -662,7 +662,7 @@
             UiEventLogger uiEventLogger,
             NotificationRemoteInputManager remoteInputManager,
             VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
-            SeenNotificationsProvider seenNotificationsProvider,
+            NotificationListInteractor notificationListInteractor,
             ShadeController shadeController,
             InteractionJankMonitor jankMonitor,
             StackStateLogger stackLogger,
@@ -715,7 +715,7 @@
         mUiEventLogger = uiEventLogger;
         mRemoteInputManager = remoteInputManager;
         mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
-        mSeenNotificationsProvider = seenNotificationsProvider;
+        mNotificationListInteractor = notificationListInteractor;
         mShadeController = shadeController;
         mNotifIconAreaController = notifIconAreaController;
         mFeatureFlags = featureFlags;
@@ -2006,7 +2006,7 @@
         public void setNotifStats(@NonNull NotifStats notifStats) {
             mNotifStats = notifStats;
             mView.setHasFilteredOutSeenNotifications(
-                    mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
+                    mNotificationListInteractor.getHasFilteredOutSeenNotifications().getValue());
             updateFooter();
             updateShowEmptyShadeView();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index f2d5394..8ca1852 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -142,7 +142,15 @@
                 viewState.setAlpha(1f);
             } else if (ambientState.isOnKeyguard()) {
                 // Adjust alpha for wakeup to lockscreen.
-                viewState.setAlpha(1f - ambientState.getHideAmount());
+                if (view.isHeadsUpState()) {
+                    // Pulsing HUN should be visible on AOD and stay visible during 
+                    // AOD=>lockscreen transition
+                    viewState.setAlpha(1f - ambientState.getHideAmount());
+                } else {
+                    // Normal notifications are hidden on AOD and should fade in during 
+                    // AOD=>lockscreen transition
+                    viewState.setAlpha(1f - ambientState.getDozeAmount());
+                }
             } else if (ambientState.isExpansionChanging()) {
                 // Adjust alpha for shade open & close.
                 float expansion = ambientState.getExpansionFraction();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt
new file mode 100644
index 0000000..f6ed8c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.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.statusbar.notification.stack.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Repository for information about the current notification list. */
+@SysUISingleton
+class NotificationListRepository @Inject constructor() {
+    private val _hasFilteredOutSeenNotifications = MutableStateFlow(false)
+    val hasFilteredOutSeenNotifications: StateFlow<Boolean> =
+        _hasFilteredOutSeenNotifications.asStateFlow()
+
+    fun setHasFilteredOutSeenNotifications(value: Boolean) {
+        _hasFilteredOutSeenNotifications.value = value
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt
new file mode 100644
index 0000000..3fd68a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.stack.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** Interactor for business logic associated with the notification stack. */
+@SysUISingleton
+class NotificationListInteractor
+@Inject
+constructor(
+    private val notificationListRepository: NotificationListRepository,
+) {
+    /** Are any already-seen notifications currently filtered out of the shade? */
+    val hasFilteredOutSeenNotifications: StateFlow<Boolean>
+        get() = notificationListRepository.hasFilteredOutSeenNotifications
+
+    fun setHasFilteredOutSeenNotifications(value: Boolean) {
+        notificationListRepository.setHasFilteredOutSeenNotifications(value)
+    }
+}
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 3e2f10d..6e6318e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -220,6 +220,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -236,6 +237,8 @@
 import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
 import com.android.wm.shell.startingsurface.StartingSurface;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.List;
@@ -247,8 +250,6 @@
 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.
@@ -417,7 +418,7 @@
     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
     private final KeyguardBypassController mKeyguardBypassController;
     private final KeyguardStateController mKeyguardStateController;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
     private final FalsingCollector mFalsingCollector;
     private final FalsingManager mFalsingManager;
@@ -611,7 +612,7 @@
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             KeyguardBypassController keyguardBypassController,
             KeyguardStateController keyguardStateController,
-            HeadsUpManagerPhone headsUpManagerPhone,
+            HeadsUpManager headsUpManager,
             DynamicPrivacyController dynamicPrivacyController,
             FalsingManager falsingManager,
             FalsingCollector falsingCollector,
@@ -718,7 +719,7 @@
         mWakeUpCoordinator = notificationWakeUpCoordinator;
         mKeyguardBypassController = keyguardBypassController;
         mKeyguardStateController = keyguardStateController;
-        mHeadsUpManager = headsUpManagerPhone;
+        mHeadsUpManager = headsUpManager;
         mBackActionInteractor = backActionInteractor;
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
@@ -1088,11 +1089,6 @@
     void initShadeVisibilityListener() {
         mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
             @Override
-            public void visibilityChanged(boolean visible) {
-                onShadeVisibilityChanged(visible);
-            }
-
-            @Override
             public void expandedVisibleChanged(boolean expandedVisible) {
                 if (expandedVisible) {
                     setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
@@ -1195,7 +1191,7 @@
                 });
         mStatusBarInitializer.initializeStatusBar();
 
-        mStatusBarTouchableRegionManager.setup(this, getNotificationShadeWindowView());
+        mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
 
         createNavigationBar(result);
 
@@ -2688,7 +2684,9 @@
                 !mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive());
 
         mShadeSurface.setTouchAndAnimationDisabled(disabled);
-        mNotificationIconAreaController.setAnimationsEnabled(!disabled);
+        if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+            mNotificationIconAreaController.setAnimationsEnabled(!disabled);
+        }
     }
 
     final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@@ -2844,13 +2842,16 @@
         mScrimController.setExpansionAffectsAlpha(!unlocking);
 
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
-                    && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
-                    || mTransitionToFullShadeProgress > 0f)) {
-                mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
-            } else {
-                mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+            if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+                if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+                        && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                        || mTransitionToFullShadeProgress > 0f)) {
+                    mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+                } else {
+                    mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+                }
             }
+
             // This will cancel the keyguardFadingAway animation if it is running. We need to do
             // this as otherwise it can remain pending and leave keyguard in a weird state.
             mUnlockScrimCallback.onCancelled();
@@ -2913,8 +2914,6 @@
 
     protected boolean mDeviceInteractive;
 
-    protected boolean mVisible;
-
     protected DevicePolicyManager mDevicePolicyManager;
     private final PowerManager mPowerManager;
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -3032,16 +3031,6 @@
                 afterKeyguardGone);
     }
 
-    private void onShadeVisibilityChanged(boolean visible) {
-        if (mVisible != visible) {
-            mVisible = visible;
-            if (visible) {
-                DejankUtils.notifyRendererOfExpensiveFrame(
-                        getNotificationShadeWindowView(), "onShadeVisibilityChanged");
-            }
-        }
-    }
-
     private void clearNotificationEffects() {
         try {
             mBarService.clearNotificationEffects();
@@ -3163,7 +3152,6 @@
             // TODO: Bring these out of CentralSurfaces.
             mUserInfoControllerImpl.onDensityOrFontScaleChanged();
             mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
-            mHeadsUpManager.onDensityOrFontScaleChanged();
         }
 
         @Override
@@ -3200,7 +3188,8 @@
                     // down on the lockscreen), clear notification LED, vibration,
                     // ringing.
                     // Other transitions are covered in WindowRootViewVisibilityInteractor.
-                    if (mVisible && (newState == StatusBarState.SHADE_LOCKED
+                    if (mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible().getValue()
+                            && (newState == StatusBarState.SHADE_LOCKED
                             || mStatusBarStateController.goingToFullShade())) {
                         clearNotificationEffects();
                     }
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 4849f64..d3d11ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 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;
 
@@ -80,7 +81,7 @@
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final DeviceProvisionedController mDeviceProvisionedController;
-    private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+    private final HeadsUpManager mHeadsUpManager;
     private final BatteryController mBatteryController;
     private final ScrimController mScrimController;
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -106,7 +107,7 @@
             WakefulnessLifecycle wakefulnessLifecycle,
             SysuiStatusBarStateController statusBarStateController,
             DeviceProvisionedController deviceProvisionedController,
-            HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController,
+            HeadsUpManager headsUpManager, BatteryController batteryController,
             ScrimController scrimController,
             Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
             Lazy<AssistManager> assistManagerLazy,
@@ -123,7 +124,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mStatusBarStateController = statusBarStateController;
         mDeviceProvisionedController = deviceProvisionedController;
-        mHeadsUpManagerPhone = headsUpManagerPhone;
+        mHeadsUpManager = headsUpManager;
         mBatteryController = batteryController;
         mScrimController = scrimController;
         mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
@@ -135,7 +136,7 @@
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
         mAuthController = authController;
         mNotificationIconAreaController = notificationIconAreaController;
-        mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener);
+        mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         mDozeInteractor = dozeInteractor;
     }
 
@@ -337,8 +338,8 @@
         if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) {
             mScrimController.setWakeLockScreenSensorActive(true);
         }
-        if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) {
-            mHeadsUpManagerPhone.extendHeadsUp();
+        if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) {
+            mHeadsUpManager.extendHeadsUp();
         } else {
             mDozeScrimController.extendPulse();
         }
@@ -493,7 +494,7 @@
                     mDozeScrimController.cancelPendingPulseTimeout();
                 }
             }
-            if (!isHeadsUp && !mHeadsUpManagerPhone.hasNotifications()) {
+            if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
                 // There are no longer any notifications to show.  We should end the
                 // pulse now.
                 stopPulsing();
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 270c40e..c493eeda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,9 +26,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ViewClippingUtil;
-import com.android.systemui.res.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeHeadsUpTracker;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.util.ViewController;
@@ -70,7 +71,7 @@
     private static final SourceType HEADS_UP = SourceType.from("HeadsUp");
     private static final SourceType PULSING = SourceType.from("Pulsing");
     private final NotificationIconAreaController mNotificationIconAreaController;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final NotificationStackScrollLayoutController mStackScrollerController;
 
     private final DarkIconDispatcher mDarkIconDispatcher;
@@ -108,7 +109,7 @@
     @Inject
     public HeadsUpAppearanceController(
             NotificationIconAreaController notificationIconAreaController,
-            HeadsUpManagerPhone headsUpManager,
+            HeadsUpManager headsUpManager,
             StatusBarStateController stateController,
             PhoneStatusBarTransitions phoneStatusBarTransitions,
             KeyguardBypassController bypassController,
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 a5ea142..6b4382f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,11 +29,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 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.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -42,10 +42,12 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.AnimationStateHandler;
+import com.android.systemui.statusbar.policy.BaseHeadsUpManager;
 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.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -53,11 +55,11 @@
 import java.util.List;
 import java.util.Stack;
 
-/**
- * A implementation of HeadsUpManager for phone and car.
- */
-public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
-        OnHeadsUpChangedListener {
+import javax.inject.Inject;
+
+/** A implementation of HeadsUpManager for phone. */
+@SysUISingleton
+public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUpChangedListener {
     private static final String TAG = "HeadsUpManagerPhone";
 
     @VisibleForTesting
@@ -102,7 +104,7 @@
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  Constructor:
-
+    @Inject
     public HeadsUpManagerPhone(@NonNull final Context context,
             HeadsUpManagerLogger logger,
             StatusBarStateController statusBarStateController,
@@ -154,7 +156,8 @@
     /**
      * Add a listener to receive callbacks onHeadsUpGoingAway
      */
-    void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
+    @Override
+    public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
         mHeadsUpPhoneListeners.add(listener);
     }
 
@@ -162,7 +165,8 @@
      * Gets the touchable region needed for heads up notifications. Returns null if no touchable
      * region is required (ie: no heads up notification currently exists).
      */
-    @Nullable Region getTouchableRegion() {
+    @Override
+    public @Nullable Region getTouchableRegion() {
         NotificationEntry topEntry = getTopEntry();
 
         // This call could be made in an inconsistent state while the pinnedMode hasn't been
@@ -197,8 +201,9 @@
      * @param key the key of the touched notification
      * @return whether the touch is invalid and should be discarded
      */
-    boolean shouldSwallowClick(@NonNull String key) {
-        HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
+    @Override
+    public boolean shouldSwallowClick(@NonNull String key) {
+        BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
         return entry != null && mClock.currentTimeMillis() < entry.mPostTime;
     }
 
@@ -238,7 +243,8 @@
      * Set that we are exiting the headsUp pinned mode, but some notifications might still be
      * animating out. This is used to keep the touchable regions in a reasonable state.
      */
-    void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+    @Override
+    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
         if (headsUpGoingAway != mHeadsUpGoingAway) {
             mHeadsUpGoingAway = headsUpGoingAway;
             for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
@@ -247,7 +253,8 @@
         }
     }
 
-    boolean isHeadsUpGoingAway() {
+    @Override
+    public boolean isHeadsUpGoingAway() {
         return mHeadsUpGoingAway;
     }
 
@@ -260,8 +267,8 @@
     public void setRemoteInputActive(
             @NonNull NotificationEntry entry, boolean remoteInputActive) {
         HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
-        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
-            headsUpEntry.remoteInputActive = remoteInputActive;
+        if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
+            headsUpEntry.mRemoteInputActive = remoteInputActive;
             if (remoteInputActive) {
                 headsUpEntry.removeAutoRemovalCallbacks("setRemoteInputActive(true)");
             } else {
@@ -313,6 +320,7 @@
         mSwipedOutKeys.add(key);
     }
 
+    @Override
     public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
             boolean animate) {
         if (animate) {
@@ -411,7 +419,7 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  HeadsUpEntryPhone:
 
-    protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+    protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry {
 
         private boolean mGutsShownPinned;
 
@@ -459,11 +467,11 @@
 
         @Override
         public void setExpanded(boolean expanded) {
-            if (this.expanded == expanded) {
+            if (this.mExpanded == expanded) {
                 return;
             }
 
-            this.expanded = expanded;
+            this.mExpanded = expanded;
             if (expanded) {
                 removeAutoRemovalCallbacks("setExpanded(true)");
             } else {
@@ -504,21 +512,6 @@
         }
     }
 
-    public interface AnimationStateHandler {
-        void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed);
-    }
-
-    /**
-     * Listener to register for HeadsUpNotification Phone changes.
-     */
-    public interface OnHeadsUpPhoneListenerChange {
-        /**
-         * Called when a heads up notification is 'going away' or no longer 'going away'.
-         * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}.
-         */
-        void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway);
-    }
-
     private final StateListener mStatusBarStateListener = new StateListener() {
         @Override
         public void onStateChanged(int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
new file mode 100644
index 0000000..0d0f2cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface HeadsUpModule {
+    @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerPhone): HeadsUpManager
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index dcbaac2..198272e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -26,13 +26,14 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 /**
  * A helper class to handle touches on the heads-up views.
  */
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final IStatusBarService mStatusBarService;
     private final Callback mCallback;
     private int mTrackingPointer;
@@ -45,7 +46,7 @@
     private final HeadsUpNotificationViewController mPanel;
     private ExpandableNotificationRow mPickedChild;
 
-    public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
+    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
             IStatusBarService statusBarService,
             Callback callback,
             HeadsUpNotificationViewController notificationPanelView) {
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 660aa3f..1b9e5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
@@ -109,6 +111,8 @@
 
     private final ViewRefactorFlag mShelfRefactor;
 
+    private final boolean mNewAodTransition;
+
     private int mAodIconAppearTranslation;
 
     private boolean mAnimationsEnabled;
@@ -147,6 +151,7 @@
         mContext = context;
         mStatusBarStateController = statusBarStateController;
         mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
+        mNewAodTransition = featureFlags.isEnabled(NEW_AOD_TRANSITION);
         mStatusBarStateController.addCallback(this);
         mMediaManager = notificationMediaManager;
         mDozeParameters = dozeParameters;
@@ -609,9 +614,11 @@
         boolean animate = true;
         if (!mBypassController.getBypassEnabled()) {
             animate = mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking();
-            // We only want the appear animations to happen when the notifications get fully hidden,
-            // since otherwise the unhide animation overlaps
-            animate &= fullyHidden;
+            if (!mNewAodTransition) {
+                // We only want the appear animations to happen when the notifications get fully
+                // hidden, since otherwise the unhide animation overlaps
+                animate &= fullyHidden;
+            }
         }
         updateAodIconsVisibility(animate, false /* force */);
         updateAodNotificationIcons();
@@ -647,23 +654,34 @@
             mAodIconsVisible = visible;
             mAodIcons.animate().cancel();
             if (animate) {
-                boolean wasFullyInvisible = mAodIcons.getVisibility() != View.VISIBLE;
-                if (mAodIconsVisible) {
-                    if (wasFullyInvisible) {
-                        // No fading here, let's just appear the icons instead!
-                        mAodIcons.setVisibility(View.VISIBLE);
-                        mAodIcons.setAlpha(1.0f);
-                        appearAodIcons();
+                if (mNewAodTransition) {
+                    // Let's make sure the icon are translated to 0, since we cancelled it above
+                    animateInAodIconTranslation();
+                    if (mAodIconsVisible) {
+                        CrossFadeHelper.fadeIn(mAodIcons);
+                    } else {
+                        CrossFadeHelper.fadeOut(mAodIcons);
+                    }
+                } else {
+                    boolean wasFullyInvisible = mAodIcons.getVisibility() != View.VISIBLE;
+                    if (mAodIconsVisible) {
+                        if (wasFullyInvisible) {
+                            // No fading here, let's just appear the icons instead!
+                            mAodIcons.setVisibility(View.VISIBLE);
+                            mAodIcons.setAlpha(1.0f);
+                            appearAodIcons();
+                        } else {
+                            // Let's make sure the icon are translated to 0, since we cancelled it
+                            // above
+                            animateInAodIconTranslation();
+                            // We were fading out, let's fade in instead
+                            CrossFadeHelper.fadeIn(mAodIcons);
+                        }
                     } else {
                         // Let's make sure the icon are translated to 0, since we cancelled it above
                         animateInAodIconTranslation();
-                        // We were fading out, let's fade in instead
-                        CrossFadeHelper.fadeIn(mAodIcons);
+                        CrossFadeHelper.fadeOut(mAodIcons);
                     }
-                } else {
-                    // Let's make sure the icon are translated to 0, since we cancelled it above
-                    animateInAodIconTranslation();
-                    CrossFadeHelper.fadeOut(mAodIcons);
                 }
             } else {
                 mAodIcons.setAlpha(1.0f);
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 7cbaf63..b15c0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -33,6 +33,7 @@
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArrayMap;
 
@@ -624,12 +625,32 @@
     }
 
     public void setDozing(boolean dozing, boolean fade, long delay) {
+        setDozing(dozing, fade, delay, /* endRunnable= */ null);
+    }
+
+    public void setDozing(boolean dozing, boolean fade, long delay,
+            @Nullable Runnable endRunnable) {
         mDozing = dozing;
         mDisallowNextAnimation |= !fade;
-        for (int i = 0; i < getChildCount(); i++) {
+        final int childCount = getChildCount();
+        // Track all the child invocations of setDozing, invoking the top-level endRunnable once
+        // they have all completed.
+        final Runnable onChildCompleted = endRunnable == null ? null : new Runnable() {
+            private int mPendingCallbacks = childCount;
+
+            @Override
+            public void run() {
+                if (--mPendingCallbacks == 0) {
+                    endRunnable.run();
+                }
+            }
+        };
+        for (int i = 0; i < childCount; i++) {
             View view = getChildAt(i);
             if (view instanceof StatusBarIconView) {
-                ((StatusBarIconView) view).setDozing(dozing, fade, delay);
+                ((StatusBarIconView) view).setDozing(dozing, fade, delay, onChildCompleted);
+            } else if (onChildCompleted != null) {
+                onChildCompleted.run();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 4b39854..235ed25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
 
@@ -39,7 +40,7 @@
     private final ShadeViewController mShadeViewController;
     private final NotificationStackScrollLayoutController mNsslController;
     private final KeyguardBypassController mKeyguardBypassController;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final StatusBarStateController mStatusBarStateController;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
 
@@ -50,7 +51,7 @@
             ShadeViewController shadeViewController,
             NotificationStackScrollLayoutController nsslController,
             KeyguardBypassController keyguardBypassController,
-            HeadsUpManagerPhone headsUpManager,
+            HeadsUpManager headsUpManager,
             StatusBarStateController statusBarStateController,
             NotificationRemoteInputManager notificationRemoteInputManager) {
         mNotificationShadeWindowController = notificationShadeWindowController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index eedf35f..62b2445 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1573,6 +1573,9 @@
      * notification shade's child views.
      */
     public boolean shouldInterceptTouchEvent(MotionEvent event) {
+        if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+            return false;
+        }
         return mAlternateBouncerInteractor.isVisibleState();
     }
 
@@ -1581,6 +1584,10 @@
      * showing.
      */
     public boolean onTouch(MotionEvent event) {
+        if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+            return false;
+        }
+
         boolean handleTouch = shouldInterceptTouchEvent(event);
         if (handleTouch) {
             final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 053c27c..dbee080 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -76,6 +76,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.wmshell.BubblesManager;
@@ -102,7 +103,7 @@
     private final Executor mUiBgExecutor;
 
     private final NotificationVisibilityProvider mVisibilityProvider;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final ActivityStarter mActivityStarter;
     private final NotificationClickNotifier mClickNotifier;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -141,7 +142,7 @@
             Handler mainThreadHandler,
             Executor uiBgExecutor,
             NotificationVisibilityProvider visibilityProvider,
-            HeadsUpManagerPhone headsUpManager,
+            HeadsUpManager headsUpManager,
             ActivityStarter activityStarter,
             NotificationClickNotifier clickNotifier,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -213,12 +214,15 @@
      */
     @Override
     public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) {
-        mLogger.logStartingActivityFromClick(entry);
+        mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(),
+                mKeyguardStateController.isVisible(),
+                mNotificationShadeWindowController.getPanelExpanded());
 
         if (mRemoteInputManager.isRemoteInputActive(entry)) {
             // We have an active remote input typed and the user clicked on the notification.
             // this was probably unintentional, so we're closing the edit text instead.
             mRemoteInputManager.closeRemoteInputs();
+            mLogger.logCloseRemoteInput(entry);
             return;
         }
         Notification notification = entry.getSbn().getNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
index d07378e..4211cab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
@@ -30,11 +30,16 @@
 class StatusBarNotificationActivityStarterLogger @Inject constructor(
     @NotifInteractionLog private val buffer: LogBuffer
 ) {
-    fun logStartingActivityFromClick(entry: NotificationEntry) {
+    fun logStartingActivityFromClick(entry: NotificationEntry, isHeadsUpState: Boolean,
+                                     isKeyguardVisible: Boolean, isPanelExpanded: Boolean) {
         buffer.log(TAG, DEBUG, {
             str1 = entry.logKey
+            bool1 = isHeadsUpState
+            bool2 = isKeyguardVisible
+            bool3 = isPanelExpanded
         }, {
-            "(1/5) onNotificationClicked: $str1"
+            "(1/5) onNotificationClicked: $str1 isHeadsUpState: $bool1 " +
+                    "isKeyguardVisible: $bool2 isPanelExpanded: $bool3"
         })
     }
 
@@ -72,6 +77,14 @@
         })
     }
 
+    fun logCloseRemoteInput(entry: NotificationEntry) {
+        buffer.log(TAG, DEBUG, {
+            str1 = entry.logKey
+        }, {
+            "Closing remote input for $str1"
+        })
+    }
+
     fun logExpandingBubble(entry: NotificationEntry) {
         buffer.log(TAG, DEBUG, {
             str1 = entry.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index fb7f9d1..2d14f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -31,11 +31,11 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.InitController;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeViewController;
@@ -60,6 +60,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import javax.inject.Inject;
@@ -76,7 +77,7 @@
     private final NotificationMediaManager mMediaManager;
     private final NotificationGutsManager mGutsManager;
     private final ShadeViewController mNotificationPanel;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final AboveShelfObserver mAboveShelfObserver;
     private final DozeScrimController mDozeScrimController;
     private final NotificationsInteractor mNotificationsInteractor;
@@ -98,7 +99,7 @@
             Context context,
             ShadeViewController panel,
             QuickSettingsController quickSettingsController,
-            HeadsUpManagerPhone headsUp,
+            HeadsUpManager headsUp,
             NotificationShadeWindowView statusBarWindow,
             ActivityStarter activityStarter,
             NotificationStackScrollLayoutController stackScrollerController,
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 40bd8d3..ba73c10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -31,15 +31,18 @@
 
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.ScreenDecorations;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
 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.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
@@ -58,13 +61,12 @@
     private static final String TAG = "TouchableRegionManager";
 
     private final Context mContext;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private boolean mIsStatusBarExpanded = false;
     private boolean mShouldAdjustInsets = false;
-    private CentralSurfaces mCentralSurfaces;
     private View mNotificationShadeWindowView;
     private View mNotificationPanelView;
     private boolean mForceCollapsedUntilLayout = false;
@@ -72,6 +74,8 @@
     private Region mTouchableRegion = new Region();
     private int mDisplayCutoutTouchableRegionSize;
     private int mStatusBarHeight;
+    private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+    private final AlternateBouncerInteractor mAlternateBouncerInteractor;
 
     private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
 
@@ -80,12 +84,14 @@
             Context context,
             NotificationShadeWindowController notificationShadeWindowController,
             ConfigurationController configurationController,
-            HeadsUpManagerPhone headsUpManager,
+            HeadsUpManager headsUpManager,
             ShadeExpansionStateManager shadeExpansionStateManager,
             Provider<SceneInteractor> sceneInteractor,
             Provider<JavaAdapter> javaAdapter,
             SceneContainerFlags sceneContainerFlags,
-            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController
+            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+            PrimaryBouncerInteractor primaryBouncerInteractor,
+            AlternateBouncerInteractor alternateBouncerInteractor
     ) {
         mContext = context;
         initResources();
@@ -128,13 +134,12 @@
                     this::onShadeExpansionFullyChanged);
         }
 
+        mPrimaryBouncerInteractor = primaryBouncerInteractor;
+        mAlternateBouncerInteractor = alternateBouncerInteractor;
         mOnComputeInternalInsetsListener = this::onComputeInternalInsets;
     }
 
-    protected void setup(
-            @NonNull CentralSurfaces centralSurfaces,
-            @NonNull View notificationShadeWindowView) {
-        mCentralSurfaces = centralSurfaces;
+    protected void setup(@NonNull View notificationShadeWindowView) {
         mNotificationShadeWindowView = notificationShadeWindowView;
         mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel);
     }
@@ -260,7 +265,8 @@
         // since we don't want stray touches to go through the light reveal scrim to whatever is
         // underneath.
         return mIsStatusBarExpanded
-                || mCentralSurfaces.isBouncerShowing()
+                || mPrimaryBouncerInteractor.isShowing().getValue()
+                || mAlternateBouncerInteractor.isVisibleState()
                 || mUnlockedScreenOffAnimationController.isAnimationPlaying();
     }
 
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 babd435..63c022c 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
@@ -38,11 +38,11 @@
 import com.android.app.animation.InterpolatorsAndroidX;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -74,8 +74,6 @@
 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;
@@ -85,6 +83,7 @@
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
+import kotlin.Unit;
 
 /**
  * Contains the collapsed status bar and handles hiding/showing based on disable flags
@@ -279,7 +278,8 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mDumpManager.registerDumpable(getClass().getSimpleName(), this);
-        mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this);
+        mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(
+                (PhoneStatusBarView) getView());
         mStatusBarFragmentComponent.init();
         mStartableStates.clear();
         for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index d9a5844..0618abb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -27,11 +27,11 @@
 import com.android.systemui.statusbar.phone.StatusBarDemoMode;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 
-import java.util.Set;
-
 import dagger.BindsInstance;
 import dagger.Subcomponent;
 
+import java.util.Set;
+
 /**
  * A subcomponent that gets re-created each time we create a new {@link CollapsedStatusBarFragment}.
  *
@@ -54,7 +54,7 @@
     @Subcomponent.Factory
     interface Factory {
         StatusBarFragmentComponent create(
-                @BindsInstance CollapsedStatusBarFragment collapsedStatusBarFragment);
+                @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 6ef877b..3741f14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,15 +19,14 @@
 import android.view.View;
 import android.view.ViewStub;
 
-import com.android.systemui.res.R;
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -51,15 +50,6 @@
 
     /** */
     @Provides
-    @RootView
-    @StatusBarFragmentScope
-    static PhoneStatusBarView providePhoneStatusBarView(
-            CollapsedStatusBarFragment collapsedStatusBarFragment) {
-        return (PhoneStatusBarView) collapsedStatusBarFragment.getView();
-    }
-
-    /** */
-    @Provides
     @StatusBarFragmentScope
     static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.battery);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
index 9b404f1..e9e52a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
@@ -165,7 +165,7 @@
                     }
 
                 wifiPickerTracker =
-                    wifiPickerTrackerFactory.create(lifecycle, callback).apply {
+                    wifiPickerTrackerFactory.create(lifecycle, callback, "WifiRepository").apply {
                         // By default, [WifiPickerTracker] will scan to see all available wifi
                         // networks in the area. Because SysUI only needs to display the
                         // **connected** network, we don't need scans to be running (and in fact,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index e59ec04..cec76f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -34,8 +34,8 @@
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.EventLogTags;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -47,7 +47,8 @@
  * A manager which handles heads up notifications which is a special mode where
  * they simply peek from the top of the screen.
  */
-public abstract class HeadsUpManager extends AlertingNotificationManager {
+public abstract class BaseHeadsUpManager extends AlertingNotificationManager implements
+        HeadsUpManager {
     private static final String TAG = "HeadsUpManager";
     private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
 
@@ -81,7 +82,7 @@
         }
     }
 
-    public HeadsUpManager(@NonNull final Context context,
+    public BaseHeadsUpManager(@NonNull final Context context,
             HeadsUpManagerLogger logger,
             @Main Handler handler,
             AccessibilityManagerWrapper accessibilityManagerWrapper,
@@ -131,6 +132,7 @@
         mListeners.remove(listener);
     }
 
+    /** Updates the notification with the given key. */
     public void updateNotification(@NonNull String key, boolean alert) {
         super.updateNotification(key, alert);
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
@@ -146,7 +148,7 @@
             // the NotificationEntry into AlertingNotificationManager's mAlertEntries map.
             return hasFullScreenIntent(entry);
         }
-        return hasFullScreenIntent(entry) && !headsUpEntry.wasUnpinned;
+        return hasFullScreenIntent(entry) && !headsUpEntry.mWasUnpinned;
     }
 
     protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
@@ -154,11 +156,11 @@
     }
 
     protected void setEntryPinned(
-            @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
+            @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
         mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
         NotificationEntry entry = headsUpEntry.mEntry;
         if (!isPinned) {
-            headsUpEntry.wasUnpinned = true;
+            headsUpEntry.mWasUnpinned = true;
         }
         if (entry.isRowPinned() != isPinned) {
             entry.setRowPinned(isPinned);
@@ -292,10 +294,12 @@
         mUser = user;
     }
 
+    /** Returns the ID of the current user. */
     public int getUser() {
         return  mUser;
     }
 
+    @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("HeadsUpManager state:");
         dumpInternal(pw, args);
@@ -309,9 +313,9 @@
         for (AlertEntry entry: mAlertEntries.values()) {
             pw.print("  HeadsUpEntry="); pw.println(entry.mEntry);
         }
-        int N = mSnoozedPackages.size();
-        pw.println("  snoozed packages: " + N);
-        for (int i = 0; i < N; i++) {
+        int n = mSnoozedPackages.size();
+        pw.println("  snoozed packages: " + n);
+        for (int i = 0; i < n; i++) {
             pw.print("    "); pw.print(mSnoozedPackages.valueAt(i));
             pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
         }
@@ -399,20 +403,20 @@
      *
      * @param entry the entry that might be indirectly removed by the user's action
      *
-     * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener
+     * @see HeadsUpCoordinator#mActionPressListener
      * @see #canRemoveImmediately(String)
      */
     public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
         if (headsUpEntry != null) {
-            headsUpEntry.userActionMayIndirectlyRemove = true;
+            headsUpEntry.mUserActionMayIndirectlyRemove = true;
         }
     }
 
     @Override
     public boolean canRemoveImmediately(@NonNull String key) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
-        if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) {
+        if (headsUpEntry != null && headsUpEntry.mUserActionMayIndirectlyRemove) {
             return true;
         }
         return super.canRemoveImmediately(key);
@@ -424,9 +428,6 @@
         return new HeadsUpEntry();
     }
 
-    public void onDensityOrFontScaleChanged() {
-    }
-
     /**
      * Determines if the notification is for a critical call that must display on top of an active
      * input notification.
@@ -445,16 +446,16 @@
      * lifecycle automatically when created.
      */
     protected class HeadsUpEntry extends AlertEntry {
-        public boolean remoteInputActive;
-        public boolean userActionMayIndirectlyRemove;
+        public boolean mRemoteInputActive;
+        public boolean mUserActionMayIndirectlyRemove;
 
-        protected boolean expanded;
-        protected boolean wasUnpinned;
+        protected boolean mExpanded;
+        protected boolean mWasUnpinned;
 
         @Override
         public boolean isSticky() {
-            return (mEntry.isRowPinned() && expanded)
-                    || remoteInputActive
+            return (mEntry.isRowPinned() && mExpanded)
+                    || mRemoteInputActive
                     || hasFullScreenIntent(mEntry);
         }
 
@@ -490,9 +491,9 @@
                 return 1;
             }
 
-            if (remoteInputActive && !headsUpEntry.remoteInputActive) {
+            if (mRemoteInputActive && !headsUpEntry.mRemoteInputActive) {
                 return -1;
-            } else if (!remoteInputActive && headsUpEntry.remoteInputActive) {
+            } else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) {
                 return 1;
             }
 
@@ -500,14 +501,14 @@
         }
 
         public void setExpanded(boolean expanded) {
-            this.expanded = expanded;
+            this.mExpanded = expanded;
         }
 
         @Override
         public void reset() {
             super.reset();
-            expanded = false;
-            remoteInputActive = false;
+            mExpanded = false;
+            mRemoteInputActive = false;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt
new file mode 100644
index 0000000..21acfb4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.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.statusbar.policy
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * A [Flow] that emits whenever screen density or font scale has changed.
+ *
+ * @see ConfigurationController.ConfigurationListener.onDensityOrFontScaleChanged
+ */
+val ConfigurationController.onDensityOrFontScaleChanged: Flow<Unit>
+    get() =
+        ConflatedCallbackFlow.conflatedCallbackFlow {
+            val listener =
+                object : ConfigurationController.ConfigurationListener {
+                    override fun onDensityOrFontScaleChanged() {
+                        trySend(Unit)
+                    }
+                }
+            addCallback(listener)
+            awaitClose { removeCallback(listener) }
+        }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
new file mode 100644
index 0000000..d9527fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -0,0 +1,245 @@
+package com.android.systemui.statusbar.policy
+
+import android.graphics.Region
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import dagger.Binds
+import dagger.Module
+import java.io.PrintWriter
+import java.util.stream.Stream
+import javax.inject.Inject
+
+/**
+ * A manager which handles heads up notifications which is a special mode where they simply peek
+ * from the top of the screen.
+ */
+interface HeadsUpManager : Dumpable {
+    /** The stream of all current notifications managed by this manager. */
+    val allEntries: Stream<NotificationEntry>
+
+    /** Add a listener to receive callbacks onHeadsUpGoingAway. */
+    fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange)
+
+    /** Adds an OnHeadUpChangedListener to observe events. */
+    fun addListener(listener: OnHeadsUpChangedListener)
+
+    fun addSwipedOutNotification(key: String)
+
+    /**
+     * Whether or not the alert can be removed currently. If it hasn't been on screen long enough it
+     * should not be removed unless forced
+     *
+     * @param key the key to check if removable
+     * @return true if the alert entry can be removed
+     */
+    fun canRemoveImmediately(key: String): Boolean
+
+    /**
+     * Compare two entries and decide how they should be ranked.
+     *
+     * @return -1 if the first argument should be ranked higher than the second, 1 if the second one
+     *   should be ranked higher and 0 if they are equal.
+     */
+    fun compare(a: NotificationEntry?, b: NotificationEntry?): Int
+    /**
+     * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
+     * longer.
+     */
+    fun extendHeadsUp()
+
+    /** Returns when a HUN entry should be removed in milliseconds from now. */
+    fun getEarliestRemovalTime(key: String?): Long
+
+    /** Returns the top Heads Up Notification, which appears to show at first. */
+    fun getTopEntry(): NotificationEntry?
+
+    /**
+     * Gets the touchable region needed for heads up notifications. Returns null if no touchable
+     * region is required (ie: no heads up notification currently exists).
+     */
+    fun getTouchableRegion(): Region?
+
+    /**
+     * Whether or not there are any active alerting notifications.
+     *
+     * @return true if there is an alert, false otherwise
+     */
+    fun hasNotifications(): Boolean = false
+
+    /** Returns whether there are any pinned Heads Up Notifications or not. */
+    fun hasPinnedHeadsUp(): Boolean
+
+    /** Returns whether or not the given notification is alerting and managed by this manager. */
+    fun isAlerting(key: String): Boolean
+
+    fun isHeadsUpGoingAway(): Boolean
+
+    /** Returns if the given notification is snoozed or not. */
+    fun isSnoozed(packageName: String): Boolean
+
+    /** Returns whether the entry is (pinned and expanded) or (has an active remote input). */
+    fun isSticky(key: String?): Boolean
+
+    fun isTrackingHeadsUp(): Boolean
+
+    fun onExpandingFinished()
+
+    /** Removes the OnHeadUpChangedListener from the observer list. */
+    fun removeListener(listener: OnHeadsUpChangedListener)
+
+    /**
+     * Try to remove the notification. May not succeed if the notification has not been shown long
+     * enough and needs to be kept around.
+     *
+     * @param key the key of the notification to remove
+     * @param releaseImmediately force a remove regardless of earliest removal time
+     * @return true if notification is removed, false otherwise
+     */
+    fun removeNotification(key: String, releaseImmediately: Boolean): Boolean
+
+    /**
+     * Try to remove the notification. May not succeed if the notification has not been shown long
+     * enough and needs to be kept around.
+     *
+     * @param key the key of the notification to remove
+     * @param releaseImmediately force a remove regardless of earliest removal time
+     * @param animate if true, animate the removal
+     * @return true if notification is removed, false otherwise
+     */
+    fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean): Boolean
+
+    /** Clears all managed notifications. */
+    fun releaseAllImmediately()
+
+    fun setAnimationStateHandler(handler: AnimationStateHandler)
+
+    /**
+     * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
+     * it's collapsed again.
+     */
+    fun setExpanded(entry: NotificationEntry, expanded: Boolean)
+
+    /**
+     * Sets whether an entry's guts are exposed and therefore it should stick in the heads up area
+     * if it's pinned until it's hidden again.
+     */
+    fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean)
+
+    /**
+     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+     * animating out. This is used to keep the touchable regions in a reasonable state.
+     */
+    fun setHeadsUpGoingAway(headsUpGoingAway: Boolean)
+
+    /**
+     * Notifies that a remote input textbox in notification gets active or inactive.
+     *
+     * @param entry The entry of the target notification.
+     * @param remoteInputActive True to notify active, False to notify inactive.
+     */
+    fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean)
+
+    fun setTrackingHeadsUp(tracking: Boolean)
+
+    /** Sets the current user. */
+    fun setUser(user: Int)
+
+    /**
+     * Notes that the user took an action on an entry that might indirectly cause the system or the
+     * app to remove the notification.
+     *
+     * @param entry the entry that might be indirectly removed by the user's action
+     * @see
+     *   com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator.mActionPressListener
+     * @see .canRemoveImmediately
+     */
+    fun setUserActionMayIndirectlyRemove(entry: NotificationEntry)
+
+    /**
+     * Decides whether a click is invalid for a notification, i.e. it has not been shown long enough
+     * that a user might have consciously clicked on it.
+     *
+     * @param key the key of the touched notification
+     * @return whether the touch is invalid and should be discarded
+     */
+    fun shouldSwallowClick(key: String): Boolean
+
+    /**
+     * Called when posting a new notification that should alert the user and appear on screen. Adds
+     * the notification to be managed.
+     *
+     * @param entry entry to show
+     */
+    fun showNotification(entry: NotificationEntry)
+
+    fun snooze()
+
+    /**
+     * Unpins all pinned Heads Up Notifications.
+     *
+     * @param userUnPinned The unpinned action is trigger by user real operation.
+     */
+    fun unpinAll(userUnPinned: Boolean)
+
+    fun updateNotification(key: String, alert: Boolean)
+}
+
+/** Sets the animation state of the HeadsUpManager. */
+interface AnimationStateHandler {
+    fun setHeadsUpGoingAwayAnimationsAllowed(allowed: Boolean)
+}
+
+/** Listener to register for HeadsUpNotification Phone changes. */
+interface OnHeadsUpPhoneListenerChange {
+    /**
+     * Called when a heads up notification is 'going away' or no longer 'going away'. See
+     * [HeadsUpManager.setHeadsUpGoingAway].
+     */
+    fun onHeadsUpGoingAwayStateChanged(headsUpGoingAway: Boolean)
+}
+
+/* No op impl of HeadsUpManager. */
+class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager {
+    override val allEntries = Stream.empty<NotificationEntry>()
+    override fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) {}
+    override fun addListener(listener: OnHeadsUpChangedListener) {}
+    override fun addSwipedOutNotification(key: String) {}
+    override fun canRemoveImmediately(key: String) = false
+    override fun compare(a: NotificationEntry?, b: NotificationEntry?) = 0
+    override fun dump(pw: PrintWriter, args: Array<out String>) {}
+    override fun extendHeadsUp() {}
+    override fun getEarliestRemovalTime(key: String?) = 0L
+    override fun getTouchableRegion(): Region? = null
+    override fun getTopEntry() = null
+    override fun hasPinnedHeadsUp() = false
+    override fun isAlerting(key: String) = false
+    override fun isHeadsUpGoingAway() = false
+    override fun isSnoozed(packageName: String) = false
+    override fun isSticky(key: String?) = false
+    override fun isTrackingHeadsUp() = false
+    override fun onExpandingFinished() {}
+    override fun releaseAllImmediately() {}
+    override fun removeListener(listener: OnHeadsUpChangedListener) {}
+    override fun removeNotification(key: String, releaseImmediately: Boolean) = false
+    override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) =
+        false
+    override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
+    override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
+    override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {}
+    override fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) {}
+    override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {}
+    override fun setTrackingHeadsUp(tracking: Boolean) {}
+    override fun setUser(user: Int) {}
+    override fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) {}
+    override fun shouldSwallowClick(key: String): Boolean = false
+    override fun showNotification(entry: NotificationEntry) {}
+    override fun snooze() {}
+    override fun unpinAll(userUnPinned: Boolean) {}
+    override fun updateNotification(key: String, alert: Boolean) {}
+}
+
+@Module
+interface HeadsUpEmptyImplModule {
+    @Binds @SysUISingleton fun bindsHeadsUpManager(noOpHum: HeadsUpManagerEmptyImpl): HeadsUpManager
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
index c302d6a..859e636 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
@@ -17,6 +17,7 @@
 
 import android.content.res.Resources
 import com.android.systemui.res.R
+import javax.inject.Inject
 
 /**
  * Fake SplitShadeStateController
@@ -24,7 +25,7 @@
  * Identical behaviour to legacy implementation (that used LargeScreenUtils.kt) I.E., behaviour
  * based solely on resources, no extra flag logic.
  */
-class ResourcesSplitShadeStateController : SplitShadeStateController {
+class ResourcesSplitShadeStateController @Inject constructor() : SplitShadeStateController {
     override fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
         return resources.getBoolean(R.bool.config_use_split_notification_shade)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index a77c692..818ba95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository;
 import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl;
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule;
 
 import dagger.Binds;
 import dagger.Module;
@@ -80,7 +81,7 @@
 import javax.inject.Named;
 
 /** Dagger Module for code in the statusbar.policy package. */
-@Module
+@Module(includes = { DeviceProvisioningRepositoryModule.class })
 public interface StatusBarPolicyModule {
 
     String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
new file mode 100644
index 0000000..1160d65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.policy.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Tracks state related to device provisioning. */
+interface DeviceProvisioningRepository {
+    /**
+     * Whether this device has been provisioned.
+     *
+     * @see android.provider.Settings.Global.DEVICE_PROVISIONED
+     */
+    val isDeviceProvisioned: Flow<Boolean>
+
+    /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */
+    val isFactoryResetProtectionActive: Flow<Boolean>
+}
+
+@Module
+interface DeviceProvisioningRepositoryModule {
+    @Binds fun bindsImpl(impl: DeviceProvisioningRepositoryImpl): DeviceProvisioningRepository
+}
+
+class DeviceProvisioningRepositoryImpl
+@Inject
+constructor(
+    private val deviceProvisionedController: DeviceProvisionedController,
+) : DeviceProvisioningRepository {
+
+    override val isDeviceProvisioned: Flow<Boolean> = conflatedCallbackFlow {
+        val listener =
+            object : DeviceProvisionedController.DeviceProvisionedListener {
+                override fun onDeviceProvisionedChanged() {
+                    trySend(deviceProvisionedController.isDeviceProvisioned)
+                }
+            }
+        deviceProvisionedController.addCallback(listener)
+        trySend(deviceProvisionedController.isDeviceProvisioned)
+        awaitClose { deviceProvisionedController.removeCallback(listener) }
+    }
+
+    override val isFactoryResetProtectionActive: Flow<Boolean> = conflatedCallbackFlow {
+        val listener =
+            object : DeviceProvisionedController.DeviceProvisionedListener {
+                override fun onFrpActiveChanged() {
+                    trySend(deviceProvisionedController.isFrpActive)
+                }
+            }
+        deviceProvisionedController.addCallback(listener)
+        trySend(deviceProvisionedController.isFrpActive)
+        awaitClose { deviceProvisionedController.removeCallback(listener) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index ce9d6fd..dbc3bf3 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -325,6 +325,7 @@
                             addAction(Intent.ACTION_USER_SWITCHED)
                             addAction(Intent.ACTION_USER_STOPPED)
                             addAction(Intent.ACTION_USER_UNLOCKED)
+                            addAction(Intent.ACTION_LOCALE_CHANGED)
                         },
                     user = UserHandle.SYSTEM,
                     map = { intent, _ -> intent },
@@ -615,6 +616,7 @@
     ) {
         val shouldRefreshAllUsers =
             when (intent.action) {
+                Intent.ACTION_LOCALE_CHANGED -> true
                 Intent.ACTION_USER_SWITCHED -> {
                     dismissDialog()
                     val selectedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
@@ -644,6 +646,11 @@
     }
 
     private fun restartSecondaryService(@UserIdInt userId: Int) {
+        // Do not start service for user that is marked for deletion.
+        if (!manager.aliveUsers.map { it.id }.contains(userId)) {
+            return
+        }
+
         val intent = Intent(applicationContext, SystemUISecondaryUserService::class.java)
         // Disconnect from the old secondary user's service
         val secondaryUserId = repository.secondaryUserId
@@ -657,6 +664,7 @@
 
         // Connect to the new secondary user's service (purely to ensure that a persistent
         // SystemUI application is created for that user)
+
         if (userId != Process.myUserHandle().identifier) {
             applicationContext.startServiceAsUser(
                 intent,
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index d3f83b1..20f0fa8c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -68,6 +68,7 @@
 
     private val hasCancelButtonBeenClicked = MutableStateFlow(false)
     private val isFinishRequiredDueToExecutedAction = MutableStateFlow(false)
+    private val userSwitched = MutableStateFlow(false)
 
     /**
      * Whether the observer should finish the experience. Once consumed, [onFinished] must be called
@@ -89,6 +90,7 @@
     fun onFinished() {
         hasCancelButtonBeenClicked.value = false
         isFinishRequiredDueToExecutedAction.value = false
+        userSwitched.value = false
     }
 
     /** Notifies that the user has clicked the "open menu" button. */
@@ -121,8 +123,9 @@
             hasCancelButtonBeenClicked,
             // If an executed action told us to finish, we should finish,
             isFinishRequiredDueToExecutedAction,
-        ) { cancelButtonClicked, executedActionFinish ->
-            cancelButtonClicked || executedActionFinish
+            userSwitched,
+        ) { cancelButtonClicked, executedActionFinish, userSwitched ->
+            cancelButtonClicked || executedActionFinish || userSwitched
         }
 
     private fun toViewModel(
@@ -191,7 +194,10 @@
         return if (!model.isSelectable) {
             null
         } else {
-            { userInteractor.selectUser(model.id) }
+            {
+                userInteractor.selectUser(model.id)
+                userSwitched.value = true
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
new file mode 100644
index 0000000..ac04d31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.util
+
+import java.lang.ref.SoftReference
+import java.lang.ref.WeakReference
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+/**
+ * Creates a Kotlin idiomatic weak reference.
+ *
+ * Usage:
+ * ```
+ * var weakReferenceObj: Object? by weakReference(null)
+ * weakReferenceObj = Object()
+ * ```
+ */
+fun <T> weakReference(obj: T? = null): ReadWriteProperty<Any?, T?> {
+    return object : ReadWriteProperty<Any?, T?> {
+        var weakRef = WeakReference<T?>(obj)
+        override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+            return weakRef.get()
+        }
+
+        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
+            weakRef = WeakReference(value)
+        }
+    }
+}
+
+/**
+ * Creates a Kotlin idiomatic soft reference.
+ *
+ * Usage:
+ * ```
+ * var softReferenceObj: Object? by softReference(null)
+ * softReferenceObj = Object()
+ * ```
+ */
+fun <T> softReference(obj: T? = null): ReadWriteProperty<Any?, T?> {
+    return object : ReadWriteProperty<Any?, T?> {
+        var softRef = SoftReference<T?>(obj)
+        override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+            return softRef.get()
+        }
+
+        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
+            softRef = SoftReference(value)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 47d505e..83ff789 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -18,19 +18,24 @@
 
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.util.time.SystemClockImpl
-import kotlinx.coroutines.CoroutineStart
 import java.util.concurrent.atomic.AtomicReference
+import kotlin.math.max
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
-import kotlin.math.max
 
 /**
  * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -44,8 +49,7 @@
     var previousValue: Any? = noVal
     collect { newVal ->
         if (previousValue != noVal) {
-            @Suppress("UNCHECKED_CAST")
-            emit(transform(previousValue as T, newVal))
+            @Suppress("UNCHECKED_CAST") emit(transform(previousValue as T, newVal))
         }
         previousValue = newVal
     }
@@ -60,13 +64,11 @@
 fun <T, R> Flow<T>.pairwiseBy(
     initialValue: T,
     transform: suspend (previousValue: T, newValue: T) -> R,
-): Flow<R> =
-    onStart { emit(initialValue) }.pairwiseBy(transform)
+): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform)
 
 /**
  * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
  *
- *
  * The output of [getInitialValue] will be used as the "old" value for the first emission. As
  * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before
  * returning the initial value.
@@ -76,8 +78,7 @@
 fun <T, R> Flow<T>.pairwiseBy(
     getInitialValue: suspend () -> T,
     transform: suspend (previousValue: T, newValue: T) -> R,
-): Flow<R> =
-    onStart { emit(getInitialValue()) }.pairwiseBy(transform)
+): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform)
 
 /**
  * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new
@@ -88,8 +89,8 @@
 fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev)
 
 /**
- * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue]
- * will be used as the "old" value for the first emission.
+ * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] will
+ * be used as the "old" value for the first emission.
  *
  * Useful for code that needs to compare the current value to the previous value.
  */
@@ -102,9 +103,9 @@
  * Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
  * [transform].
  *
- * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
- * a change event to be emitted that contains no removals, and all elements from that first [Set]
- * as additions.
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a
+ * change event to be emitted that contains no removals, and all elements from that first [Set] as
+ * additions.
  *
  * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
  * until a second [Set] has been emitted from the upstream [Flow].
@@ -112,22 +113,23 @@
 fun <T, R> Flow<Set<T>>.setChangesBy(
     transform: suspend (removed: Set<T>, added: Set<T>) -> R,
     emitFirstEvent: Boolean = true,
-): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
-    .distinctUntilChanged()
-    .pairwiseBy { old: Set<T>, new: Set<T> ->
-        // If an element was present in the old set, but not the new one, then it was removed
-        val removed = old - new
-        // If an element is present in the new set, but on the old one, then it was added
-        val added = new - old
-        transform(removed, added)
-    }
+): Flow<R> =
+    (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
+        .distinctUntilChanged()
+        .pairwiseBy { old: Set<T>, new: Set<T> ->
+            // If an element was present in the old set, but not the new one, then it was removed
+            val removed = old - new
+            // If an element is present in the new set, but on the old one, then it was added
+            val added = new - old
+            transform(removed, added)
+        }
 
 /**
  * Returns a new [Flow] that produces the [Set] changes between each emission from [this].
  *
- * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
- * a change event to be emitted that contains no removals, and all elements from that first [Set]
- * as additions.
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a
+ * change event to be emitted that contains no removals, and all elements from that first [Set] as
+ * additions.
  *
  * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
  * until a second [Set] has been emitted from the upstream [Flow].
@@ -153,14 +155,11 @@
     coroutineScope {
         val noVal = Any()
         val sampledRef = AtomicReference(noVal)
-        val job = launch(Dispatchers.Unconfined) {
-            other.collect { sampledRef.set(it) }
-        }
+        val job = launch(Dispatchers.Unconfined) { other.collect { sampledRef.set(it) } }
         collect {
             val sampled = sampledRef.get()
             if (sampled != noVal) {
-                @Suppress("UNCHECKED_CAST")
-                emit(transform(it, sampled as B))
+                @Suppress("UNCHECKED_CAST") emit(transform(it, sampled as B))
             }
         }
         job.cancel()
@@ -181,7 +180,6 @@
  * latest value is emitted.
  *
  * Example:
- *
  * ```kotlin
  * flow {
  *     emit(1)     // t=0ms
@@ -210,7 +208,6 @@
  * during this period, only The latest value is emitted.
  *
  * Example:
- *
  * ```kotlin
  * flow {
  *     emit(1)     // t=0ms
@@ -248,10 +245,11 @@
                 // We create delayJob to allow cancellation during the delay period
                 delayJob = launch {
                     delay(timeUntilNextEmit)
-                    sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
-                        send(it)
-                        previousEmitTimeMs = clock.elapsedRealtime()
-                    }
+                    sendJob =
+                        outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
+                            send(it)
+                            previousEmitTimeMs = clock.elapsedRealtime()
+                        }
                 }
             } else {
                 send(it)
@@ -259,4 +257,15 @@
             }
         }
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Returns a [StateFlow] launched in the surrounding [CoroutineScope]. This [StateFlow] gets its
+ * value by invoking [getValue] whenever an event is emitted from [changedSignals]. It will also
+ * immediately invoke [getValue] to establish its initial value.
+ */
+inline fun <T> CoroutineScope.stateFlow(
+    changedSignals: Flow<Unit>,
+    crossinline getValue: () -> T,
+): StateFlow<T> =
+    changedSignals.map { getValue() }.stateIn(this, SharingStarted.Eagerly, getValue())
diff --git a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
new file mode 100644
index 0000000..51d2afa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.util.ui
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.transformLatest
+
+/**
+ * A state comprised of a [value] of type [T] paired with a boolean indicating whether or not the
+ * [value] [isAnimating] in the UI.
+ */
+data class AnimatedValue<T>(
+    val value: T,
+    val isAnimating: Boolean,
+)
+
+/**
+ * An event comprised of a [value] of type [T] paired with a [boolean][startAnimating] indicating
+ * whether or not this event should start an animation.
+ */
+data class AnimatableEvent<T>(
+    val value: T,
+    val startAnimating: Boolean,
+)
+
+/**
+ * Returns a [Flow] that tracks an [AnimatedValue] state. The input [Flow] is used to update the
+ * [AnimatedValue.value], as well as [AnimatedValue.isAnimating] if the event's
+ * [AnimatableEvent.startAnimating] value is `true`. When [completionEvents] emits a value, the
+ * [AnimatedValue.isAnimating] will flip to `false`.
+ */
+fun <T> Flow<AnimatableEvent<T>>.toAnimatedValueFlow(
+    completionEvents: Flow<Any?>,
+): Flow<AnimatedValue<T>> = transformLatest { (value, startAnimating) ->
+    emit(AnimatedValue(value, isAnimating = startAnimating))
+    if (startAnimating) {
+        // Wait for a completion now that we've started animating
+        completionEvents
+            .map { Unit } // replace the event so that it's never `null`
+            .firstOrNull() // `null` indicates an empty flow
+            // emit the new state if the flow was not empty.
+            ?.run { emit(AnimatedValue(value, isAnimating = false)) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 750b6f9..2132904 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -40,12 +40,13 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -68,6 +69,7 @@
     private final Executor mExecutor;
     private final Handler mHandler;
     private final FalsingManager mFalsingManager;
+    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
     private FalsingCollector mFalsingCollector;
     private final UserTracker mUserTracker;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -91,7 +93,8 @@
             UserTracker userTracker,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarKeyguardViewManager keyguardViewManager,
-            UiEventLogger uiEventLogger) {
+            UiEventLogger uiEventLogger,
+            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
         mKeyguardStateController = keyguardStateController;
         mKeyguardDismissUtil = keyguardDismissUtil;
         mActivityStarter = activityStarter;
@@ -103,6 +106,7 @@
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewManager = keyguardViewManager;
         mUiEventLogger = uiEventLogger;
+        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
     }
 
     @Override
@@ -209,6 +213,7 @@
                 true,
                 Utils.getColorAttrDefaultColor(
                         this, com.android.internal.R.attr.colorAccentPrimary));
+        mKeyguardFaceAuthInteractor.onWalletLaunched();
         mKeyguardViewManager.requestFace(true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
index 8990583..167e341 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
@@ -18,61 +18,94 @@
 import android.app.ActivityManager
 import android.app.admin.DevicePolicyManager
 import android.os.UserManager
+import android.util.DisplayMetrics
+import com.android.internal.logging.MetricsLogger
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.GuestResumeSessionReceiver
 import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.dagger.BroadcastDispatcherLog
 import com.android.systemui.log.dagger.SceneFrameworkLog
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.mockito.mock
 import com.android.wm.shell.bubbles.Bubbles
+import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
 
-@Module
+@Module(includes = [TestMocksModule.Bindings::class])
 data class TestMocksModule(
     @get:Provides val activityStarter: ActivityStarter = mock(),
+    @get:Provides val ambientState: AmbientState = mock(),
     @get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()),
-    @get:Provides val configurationController: ConfigurationController = mock(),
     @get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(),
     @get:Provides val demoModeController: DemoModeController = mock(),
     @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
     @get:Provides val dozeParameters: DozeParameters = mock(),
+    @get:Provides val dumpManager: DumpManager = mock(),
     @get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
     @get:Provides val keyguardBypassController: KeyguardBypassController = mock(),
     @get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(),
     @get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(),
-    @get:Provides val notifListener: NotificationListener = mock(),
-    @get:Provides val notifMediaManager: NotificationMediaManager = mock(),
-    @get:Provides val screenOffAnimController: ScreenOffAnimationController = mock(),
-    @get:Provides val splitShadeStateController: SplitShadeStateController = mock(),
-    @get:Provides val statusBarStateController: StatusBarStateController = mock(),
+    @get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(),
+    @get:Provides val notificationListener: NotificationListener = mock(),
+    @get:Provides val notificationLockscreenUserManager: NotificationLockscreenUserManager = mock(),
+    @get:Provides val notificationMediaManager: NotificationMediaManager = mock(),
+    @get:Provides val notificationShadeDepthController: NotificationShadeDepthController = mock(),
+    @get:Provides
+    val notificationStackScrollLayoutController: NotificationStackScrollLayoutController = mock(),
+    @get:Provides val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock(),
+    @get:Provides val notificationWakeUpCoordinator: NotificationWakeUpCoordinator = mock(),
+    @get:Provides val screenLifecycle: ScreenLifecycle = mock(),
+    @get:Provides val screenOffAnimationController: ScreenOffAnimationController = mock(),
+    @get:Provides val scrimController: ScrimController = mock(),
+    @get:Provides val statusBarStateController: SysuiStatusBarStateController = mock(),
     @get:Provides val statusBarWindowController: StatusBarWindowController = mock(),
-    @get:Provides val wakeUpCoordinator: NotificationWakeUpCoordinator = mock(),
+    @get:Provides val wakefulnessLifecycle: WakefulnessLifecycle = mock(),
 
     // log buffers
     @get:[Provides BroadcastDispatcherLog]
     val broadcastDispatcherLogger: LogBuffer = mock(),
     @get:[Provides SceneFrameworkLog]
     val sceneLogger: LogBuffer = mock(),
+    @get:Provides val lsShadeTransitionLogger: LSShadeTransitionLogger = mock(),
 
     // framework mocks
     @get:Provides val activityManager: ActivityManager = mock(),
     @get:Provides val devicePolicyManager: DevicePolicyManager = mock(),
+    @get:Provides val displayMetrics: DisplayMetrics = mock(),
+    @get:Provides val metricsLogger: MetricsLogger = mock(),
     @get:Provides val userManager: UserManager = mock(),
-)
+) {
+    @Module
+    interface Bindings {
+        @Binds
+        fun bindStatusBarStateController(
+            sysuiStatusBarStateController: SysuiStatusBarStateController,
+        ): StatusBarStateController
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index f943acd..d8a2c5f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -25,7 +25,6 @@
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
@@ -47,7 +46,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class KeyguardPasswordViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index e090093..dc1618d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -25,7 +25,6 @@
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorFake
@@ -53,7 +52,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class KeyguardPatternViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 8322b37..4a24e4a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -31,14 +31,13 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.SingleTapClassifier;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,7 +46,6 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper
 public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 2f08804..9df4dd4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -26,7 +26,6 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorFake
@@ -53,7 +52,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class KeyguardPinViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index f3a1b68..5102957 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -69,7 +69,8 @@
         when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
         when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
         when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView);
-        when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class)))
+        when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class),
+                any(Display.class)))
                 .thenReturn(mKeyguardStatusViewComponent);
         when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
                 .thenReturn(mKeyguardClockSwitchController);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index d54843d3..62f9a9d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -36,7 +36,6 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
 import com.android.systemui.biometrics.SideFpsController
@@ -100,7 +99,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
@@ -812,6 +810,7 @@
                     SceneKey.Bouncer,
                     flowOf(.5f),
                     false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -827,7 +826,8 @@
                     SceneKey.Bouncer,
                     SceneKey.Gone,
                     flowOf(.5f),
-                    false
+                    false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -844,7 +844,8 @@
                     SceneKey.Gone,
                     SceneKey.Bouncer,
                     flowOf(.5f),
-                    false
+                    false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -862,7 +863,8 @@
                     SceneKey.Bouncer,
                     SceneKey.Gone,
                     flowOf(.5f),
-                    false
+                    false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -878,6 +880,7 @@
                     SceneKey.Lockscreen,
                     flowOf(.5f),
                     false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -895,6 +898,7 @@
                     SceneKey.Gone,
                     flowOf(.5f),
                     false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index aad11d9..156e068 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -56,11 +56,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.user.data.source.UserRecord;
 import com.android.systemui.util.settings.GlobalSettings;
@@ -76,7 +75,6 @@
 import java.util.ArrayList;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper()
 public class KeyguardSecurityContainerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index b02b1f9..7bb6ef1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -35,10 +35,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -50,7 +49,6 @@
 import org.mockito.junit.MockitoRule;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper()
 public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index d0bb5a9..4290b8b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -25,7 +25,6 @@
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
@@ -44,7 +43,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class KeyguardSimPinViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 59cd26c..31ee641 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -25,7 +25,6 @@
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
@@ -40,7 +39,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class KeyguardSimPukViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index e4e2b0a..9a908d7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -29,9 +29,9 @@
 import android.testing.TestableLooper;
 import android.view.View;
 
-import com.android.systemui.res.R;
 import com.android.systemui.plugins.ClockConfig;
 import com.android.systemui.plugins.ClockController;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 
@@ -81,7 +81,7 @@
     public void updatePosition_primaryClockAnimation() {
         ClockController mockClock = mock(ClockController.class);
         when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
-        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", false, true));
+        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true));
 
         mController.updatePosition(10, 15, 20f, true);
 
@@ -96,7 +96,7 @@
     public void updatePosition_alternateClockAnimation() {
         ClockController mockClock = mock(ClockController.class);
         when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
-        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", true, true));
+        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true));
 
         mController.updatePosition(10, 15, 20f, true);
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 21a2822..d61ca69 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -39,10 +39,10 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.doze.util.BurnInHelperKt;
 import com.android.systemui.dump.DumpManager;
@@ -51,6 +51,8 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -75,6 +77,8 @@
 
     protected MockitoSession mStaticMockSession;
 
+    protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
+    protected @Mock BouncerInteractor mBouncerInteractor;
     protected @Mock LockIconView mLockIconView;
     protected @Mock AnimatedStateListDrawable mIconDrawable;
     protected @Mock Context mContext;
@@ -93,6 +97,7 @@
     protected @Mock AuthRippleController mAuthRippleController;
     protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
     protected FakeFeatureFlags mFeatureFlags;
+
     protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
 
     protected LockIconViewController mUnderTest;
@@ -148,6 +153,7 @@
         mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
         mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
         mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
+
         mUnderTest = new LockIconViewController(
                 mStatusBarStateController,
                 mKeyguardUpdateMonitor,
@@ -168,7 +174,9 @@
                 KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
                 mFeatureFlags,
                 mPrimaryBouncerInteractor,
-                mContext
+                mContext,
+                () -> mBouncerInteractor,
+                mSceneTestUtils.getSceneContainerFlags()
         );
     }
 
@@ -227,8 +235,8 @@
         setupLockIconViewMocks();
     }
 
-    protected void init(boolean useMigrationFlag) {
-        mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag);
+    protected void init(boolean useDozeMigrationFlag) {
+        mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag);
         mUnderTest.setLockIconView(mLockIconView);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index a2dc776..4bacc3d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -17,13 +17,16 @@
 package com.android.keyguard;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -41,6 +44,7 @@
 import com.android.systemui.biometrics.UdfpsController;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
 import com.android.systemui.doze.util.BurnInHelperKt;
+import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -180,13 +184,14 @@
     }
 
     @Test
-    public void testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() {
+    public void testLockIcon_clearsIconWhenUnlocked() {
         // GIVEN udfps not enrolled
         setupUdfps();
         when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
 
         // GIVEN starting state for the lock icon
         setupShowLockIcon();
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
         // GIVEN lock icon controller is initialized and view is attached
         init(/* useMigrationFlag= */false);
@@ -194,7 +199,7 @@
         reset(mLockIconView);
 
         // WHEN the dozing state changes
-        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
+        mStatusBarStateListener.onDozingChanged(false /* isDozing */);
 
         // THEN the icon is cleared
         verify(mLockIconView).clearIcon();
@@ -400,6 +405,49 @@
 
         // THEN uses perform haptic feedback
         verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
+    }
 
+    @Test
+    public void longPress_showBouncer_sceneContainerNotEnabled() {
+        init(/* useMigrationFlag= */ false);
+        mSceneTestUtils.getSceneContainerFlags().setEnabled(false);
+        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
+
+        // WHEN longPress
+        mUnderTest.onLongPress();
+
+        // THEN show primary bouncer via keyguard view controller, not scene container
+        verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
+        verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
+    }
+
+    @Test
+    public void longPress_showBouncer() {
+        init(/* useMigrationFlag= */ false);
+        mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
+        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
+
+        // WHEN longPress
+        mUnderTest.onLongPress();
+
+        // THEN show primary bouncer
+        verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
+        verify(mBouncerInteractor).showOrUnlockDevice(any());
+    }
+
+    @Test
+    public void longPress_falsingTriggered_doesNotShowBouncer() {
+        init(/* useMigrationFlag= */ false);
+        mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
+        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
+
+        // WHEN longPress
+        mUnderTest.onLongPress();
+
+        // THEN don't show primary bouncer
+        verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt
index c372f45..1213518 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
@@ -42,7 +43,7 @@
 
     /** After migration, replaces LockIconViewControllerTest version */
     @Test
-    fun testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() =
+    fun testLockIcon_clearsIconWhenUnlocked() =
         runBlocking(IMMEDIATE) {
             // GIVEN udfps not enrolled
             setupUdfps()
@@ -50,14 +51,14 @@
 
             // GIVEN starting state for the lock icon
             setupShowLockIcon()
+            whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE)
 
             // GIVEN lock icon controller is initialized and view is attached
             init(/* useMigrationFlag= */ true)
             reset(mLockIconView)
 
             // WHEN the dozing state changes
-            mUnderTest.mIsDozingCallback.accept(true)
-
+            mUnderTest.mIsDozingCallback.accept(false)
             // THEN the icon is cleared
             verify(mLockIconView).clearIcon()
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 59c7e76..8faf715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -166,6 +166,9 @@
         waitForIdleSync()
         verify(controller).onLaunchAnimationCancelled()
         verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+        verify(listener).onLaunchAnimationCancelled()
+        verify(listener, never()).onLaunchAnimationStart()
+        assertNull(runner.delegate)
     }
 
     @Test
@@ -176,6 +179,9 @@
         waitForIdleSync()
         verify(controller).onLaunchAnimationCancelled()
         verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+        verify(listener).onLaunchAnimationCancelled()
+        verify(listener, never()).onLaunchAnimationStart()
+        assertNull(runner.delegate)
     }
 
     @Test
@@ -194,6 +200,15 @@
         }
     }
 
+    @Test
+    fun disposeRunner_delegateDereferenced() {
+        val runner = activityLaunchAnimator.createRunner(controller)
+        assertNotNull(runner.delegate)
+        runner.dispose()
+        waitForIdleSync()
+        assertNull(runner.delegate)
+    }
+
     private fun fakeWindow(): RemoteAnimationTarget {
         val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
         val taskInfo = ActivityManager.RunningTaskInfo()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index 212dad7..c2e6db3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -242,6 +242,40 @@
     }
 
     @Test
+    fun animatesRootOnly() {
+        setUpRootWithChildren()
+
+        val success = ViewHierarchyAnimator.animate(
+                rootView,
+                animateChildren = false
+        )
+        // Change all bounds.
+        rootView.measure(
+                View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+        )
+        rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
+
+        assertTrue(success)
+        assertNotNull(rootView.getTag(R.id.tag_animator))
+        assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator))
+        assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator))
+        // The initial values for the root view should be those of the previous layout, while the
+        // children views should be at the final values from the beginning.
+        checkBounds(rootView, l = 0, t = 0, r = 200, b = 100)
+        checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100)
+        checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100)
+        endAnimation(rootView)
+        assertNull(rootView.getTag(R.id.tag_animator))
+        assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator))
+        assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator))
+        // The end values should be those of the latest layout.
+        checkBounds(rootView, l = 10, t = 20, r = 200, b = 120)
+        checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100)
+        checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100)
+    }
+
+    @Test
     fun animatesInvisibleViews() {
         rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
         rootView.visibility = View.INVISIBLE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 0283382..6ac84bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.KeyguardSecurityModel
@@ -40,13 +41,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class AuthenticationRepositoryTest : SysuiTestCase() {
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index a102890..2f5f460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.authentication.domain.interactor
 
 import android.app.admin.DevicePolicyManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
@@ -35,11 +36,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class AuthenticationInteractorTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 2bc0171..885abcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -17,12 +17,15 @@
 package com.android.systemui.biometrics;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
 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.assertNotSame;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -79,7 +82,6 @@
 import com.android.internal.R;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
@@ -114,7 +116,6 @@
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
-@RoboPilotTest
 public class AuthControllerTest extends SysuiTestCase {
 
     private static final long REQUEST_ID = 22;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 7365460..d68a313 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -16,36 +16,18 @@
 
 package com.android.systemui.biometrics
 
-import android.app.ActivityManager
-import android.os.UserManager
 import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.SysUITestModule
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.plugins.ActivityStarter
-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.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
-import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.UserDomainLayerModule
+import dagger.BindsInstance
+import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -58,91 +40,27 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val testDispatcher = utils.testDispatcher
-    private val disableFlagsRepository = FakeDisableFlagsRepository()
-    private val featureFlags = FakeFeatureFlags()
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val shadeRepository = FakeShadeRepository()
-    private val sceneContainerFlags = FakeSceneContainerFlags()
-    private val sceneInteractor = utils.sceneInteractor()
-    private val userSetupRepository = FakeUserSetupRepository()
-    private val userRepository = FakeUserRepository()
-    private val configurationRepository = FakeConfigurationRepository()
-    private val sharedNotificationContainerInteractor =
-        SharedNotificationContainerInteractor(
-            configurationRepository,
-            mContext,
-            ResourcesSplitShadeStateController()
-        )
 
-    private lateinit var detector: AuthDialogPanelInteractionDetector
-    private lateinit var shadeInteractor: ShadeInteractor
-    private lateinit var userInteractor: UserInteractor
+    private val testComponent: TestComponent =
+        DaggerAuthDialogPanelInteractionDetectorTest_TestComponent.factory()
+            .create(
+                test = this,
+                featureFlags =
+                    FakeFeatureFlagsClassicModule {
+                        set(Flags.FACE_AUTH_REFACTOR, false)
+                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+                    },
+            )
 
     @Mock private lateinit var action: Runnable
-    @Mock private lateinit var activityManager: ActivityManager
-    @Mock private lateinit var activityStarter: ActivityStarter
-    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
-    @Mock private lateinit var guestInteractor: GuestUserInteractor
-    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
-    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var manager: UserManager
-    @Mock private lateinit var uiEventLogger: UiEventLogger
+
+    private val testScope = testComponent.testScope
+    private val shadeRepository = testComponent.shadeRepository
+    private val detector = testComponent.detector
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
-        featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
-        featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-
-        val refreshUsersScheduler =
-            RefreshUsersScheduler(
-                applicationScope = testScope.backgroundScope,
-                mainDispatcher = testDispatcher,
-                repository = userRepository,
-            )
-        userInteractor =
-            UserInteractor(
-                applicationContext = context,
-                repository = userRepository,
-                activityStarter = activityStarter,
-                keyguardInteractor =
-                    KeyguardInteractorFactory.create(featureFlags = featureFlags)
-                        .keyguardInteractor,
-                featureFlags = featureFlags,
-                manager = manager,
-                headlessSystemUserMode = headlessSystemUserMode,
-                applicationScope = testScope.backgroundScope,
-                telephonyInteractor =
-                    TelephonyInteractor(
-                        repository = FakeTelephonyRepository(),
-                    ),
-                broadcastDispatcher = fakeBroadcastDispatcher,
-                keyguardUpdateMonitor = keyguardUpdateMonitor,
-                backgroundDispatcher = testDispatcher,
-                activityManager = activityManager,
-                refreshUsersScheduler = refreshUsersScheduler,
-                guestUserInteractor = guestInteractor,
-                uiEventLogger = uiEventLogger,
-                userRestrictionChecker = mock(),
-            )
-        shadeInteractor =
-            ShadeInteractor(
-                testScope.backgroundScope,
-                disableFlagsRepository,
-                sceneContainerFlags,
-                { sceneInteractor },
-                keyguardRepository,
-                userSetupRepository,
-                deviceProvisionedController,
-                userInteractor,
-                sharedNotificationContainerInteractor,
-                shadeRepository,
-            )
-        detector = AuthDialogPanelInteractionDetector(testScope, { shadeInteractor })
     }
 
     @Test
@@ -215,4 +133,27 @@
             // Clean up job
             detector.disable()
         }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val detector: AuthDialogPanelInteractionDetector
+        val shadeRepository: FakeShadeRepository
+        val testScope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+            ): TestComponent
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
index 8547fa3..714461b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
@@ -38,7 +38,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 
 import kotlin.Unit;
@@ -53,7 +52,6 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 public class BiometricDisplayListenerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index ab5d8bea5..39f0d57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -19,7 +19,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.logging.BiometricMessageDeferralLogger
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import org.junit.Assert.assertEquals
@@ -34,7 +33,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class FaceHelpMessageDeferralTest : SysuiTestCase() {
     val threshold = .75f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 57cf834..ef06e0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -53,7 +53,6 @@
 import com.airbnb.lottie.LottieAnimationView
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.SysuiTestableContext
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -98,7 +97,6 @@
 private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class SideFpsControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
index 469f65a..e2aa984 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
@@ -19,7 +19,6 @@
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
@@ -35,7 +34,6 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class UdfpsBpViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 21e614f..e9e9624 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -38,7 +38,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -83,7 +82,6 @@
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 class UdfpsControllerOverlayTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index ee3bd0d..a36f4e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -20,12 +20,15 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
+
 import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -74,8 +77,6 @@
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -94,6 +95,7 @@
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -125,7 +127,6 @@
 import javax.inject.Provider;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 public class UdfpsControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
index 280bfdf..cd9189b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
@@ -27,7 +27,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Test;
@@ -38,7 +37,6 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@RoboPilotTest
 public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
     @Test
     public void testUdfpsBottomSpacerHeightForPortrait() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
index 1afb223..5239966 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.concurrency.FakeExecution;
 
@@ -41,7 +40,6 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 public class UdfpsDisplayModeTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
index 8508f45..b018a3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
@@ -31,14 +31,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4.class)
 
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 17f435b..da4548b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -58,7 +57,6 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-@RoboPilotTest
 @TestableLooper.RunWithLooper
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
index 6d55254..8b374ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
@@ -21,7 +21,6 @@
 import android.view.MotionEvent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.UdfpsController.UdfpsOverlayController
 import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -40,7 +39,6 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class UdfpsShellTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index ebadfc7..0c8e7a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -26,7 +26,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.util.mockito.any
@@ -50,7 +49,6 @@
 private const val SENSOR_RADIUS = 10
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class UdfpsViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 0d17270..2d8adca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -19,7 +19,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
@@ -43,7 +42,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class AlternateBouncerInteractorTest : SysuiTestCase() {
     private lateinit var underTest: AlternateBouncerInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index a9ba36a..915661b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -38,11 +39,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BouncerInteractorTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index a81ca86..4aea4f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
@@ -19,7 +19,6 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Before
 import org.junit.Test
@@ -29,7 +28,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() {
     private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index cb0b74f..2018e61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.ui.BouncerView
@@ -43,7 +42,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
     private lateinit var repository: FakeKeyguardBouncerRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index b5177e1..8e1f5ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -29,10 +30,9 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class AuthMethodBouncerViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index b75355a..2c97809 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
@@ -35,11 +36,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BouncerViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 333bd21..802f8e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -50,7 +49,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class KeyguardBouncerViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 0926399..ba8dcef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -35,11 +36,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PasswordBouncerViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 2e7c9aa..bfaa6ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -38,11 +39,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PatternBouncerViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 255bbe3..7873899 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -37,11 +38,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PinBouncerViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
@@ -92,7 +92,7 @@
 
             underTest.onShown()
 
-            assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
+            assertThat(message?.text).ignoringCase().isEqualTo(ENTER_YOUR_PIN)
             assertThat(pin).isEmpty()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
             assertThat(underTest.authenticationMethod)
@@ -209,7 +209,7 @@
             underTest.onAuthenticateButtonClicked()
 
             assertThat(pin).isEmpty()
-            assertThat(message?.text).isEqualTo(WRONG_PIN)
+            assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
         }
 
@@ -227,7 +227,7 @@
             underTest.onPinButtonClicked(4)
             underTest.onPinButtonClicked(5) // PIN is now wrong!
             underTest.onAuthenticateButtonClicked()
-            assertThat(message?.text).isEqualTo(WRONG_PIN)
+            assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
             assertThat(pin).isEmpty()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
@@ -273,7 +273,7 @@
             ) // PIN is now wrong!
 
             assertThat(pin).isEmpty()
-            assertThat(message?.text).isEqualTo(WRONG_PIN)
+            assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
index 4c279ea..55016bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.bouncer.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.ui.viewmodel.EntryToken.ClearAll
@@ -16,14 +17,13 @@
 import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 /**
  * This test uses a mnemonic code to create and verify PinInput instances: strings of digits [0-9]
  * for [Digit] tokens, as well as a `C` for the [ClearAll] token.
  */
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PinInputViewModelTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 7fa828f..3df9cbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -9,7 +9,6 @@
 import android.os.UserManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.coroutines.collectLastValue
@@ -40,7 +39,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
     @Mock private lateinit var appWidgetManager: AppWidgetManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index ddf788e..cdc42e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -19,7 +19,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -37,7 +36,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 class CommunalInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index a10eb29..41a8be9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -5,7 +5,6 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
 import org.junit.Before
@@ -15,7 +14,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 8e8cbe4..2c80035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.deviceentry.data.repository
 
 import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
@@ -11,6 +12,7 @@
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.runBlocking
@@ -19,14 +21,14 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DeviceEntryRepositoryTest : SysuiTestCase() {
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
@@ -54,6 +56,7 @@
                 keyguardBypassController = keyguardBypassController,
                 keyguardStateController = keyguardStateController,
             )
+        testScope.runCurrent()
     }
 
     @Test
@@ -66,8 +69,7 @@
             assertThat(isUnlocked).isFalse()
 
             val captor = argumentCaptor<KeyguardStateController.Callback>()
-            Mockito.verify(keyguardStateController, Mockito.atLeastOnce())
-                .addCallback(captor.capture())
+            verify(keyguardStateController, Mockito.atLeastOnce()).addCallback(captor.capture())
 
             whenever(keyguardStateController.isUnlocked).thenReturn(true)
             captor.value.onUnlockedChanged()
@@ -98,7 +100,12 @@
         testScope.runTest {
             whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false }
             whenever(keyguardBypassController.bypassEnabled).thenAnswer { false }
-            assertThat(underTest.isBypassEnabled()).isFalse()
+            withArgCaptor {
+                    verify(keyguardBypassController).registerOnBypassStateChangedListener(capture())
+                }
+                .onBypassStateChanged(false)
+            runCurrent()
+            assertThat(underTest.isBypassEnabled.value).isFalse()
         }
 
     @Test
@@ -106,7 +113,12 @@
         testScope.runTest {
             whenever(keyguardBypassController.isBypassEnabled).thenAnswer { true }
             whenever(keyguardBypassController.bypassEnabled).thenAnswer { true }
-            assertThat(underTest.isBypassEnabled()).isTrue()
+            withArgCaptor {
+                    verify(keyguardBypassController).registerOnBypassStateChangedListener(capture())
+                }
+                .onBypassStateChanged(true)
+            runCurrent()
+            assertThat(underTest.isBypassEnabled.value).isTrue()
         }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 55582e1..c13fde7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.deviceentry.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -14,11 +15,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DeviceEntryInteractorTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
@@ -218,14 +218,14 @@
     fun isBypassEnabled_enabledInRepository_true() =
         testScope.runTest {
             utils.deviceEntryRepository.setBypassEnabled(true)
-            assertThat(underTest.isBypassEnabled()).isTrue()
+            assertThat(underTest.isBypassEnabled.value).isTrue()
         }
 
     @Test
     fun isBypassEnabled_disabledInRepository_false() =
         testScope.runTest {
             utils.deviceEntryRepository.setBypassEnabled(false)
-            assertThat(underTest.isBypassEnabled()).isFalse()
+            assertThat(underTest.isBypassEnabled.value).isFalse()
         }
 
     private fun switchToScene(sceneKey: SceneKey) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 781ad6b..8a35ef1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -6,7 +6,6 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
@@ -30,7 +29,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
index 21192fa..2c6c793 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
@@ -17,7 +17,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -30,7 +29,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DreamOverlayCallbackControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 7c36642..2af6566 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -38,7 +38,6 @@
 
 import com.android.dream.lowlight.LowLightTransitionCoordinator;
 import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -53,7 +52,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
index be7638e..d379dc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
@@ -26,7 +26,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
@@ -38,7 +37,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 8379f73..e5f9972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -49,7 +49,6 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.complication.ComplicationLayoutEngine;
 import com.android.systemui.dreams.complication.HideComplicationTouchHandler;
@@ -72,7 +71,6 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamOverlayServiceTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 2ef227c..365f67b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -29,7 +29,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.complication.Complication;
 import com.android.systemui.flags.FeatureFlags;
@@ -48,7 +47,6 @@
 
 import java.util.Collection;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamOverlayStateControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
index 12cb332..7ff345c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
@@ -24,7 +24,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -36,7 +35,6 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamOverlayStatusBarItemsProviderTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 9566e81..39db2be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -46,11 +46,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.core.FakeLogBuffer;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -72,7 +71,6 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
index d32788d..315a24b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.complication.Complication;
 import com.android.systemui.dreams.DreamOverlayStateController;
@@ -49,7 +48,6 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class HideComplicationTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
index 4a7700f..e0c6ab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
@@ -26,7 +26,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener;
@@ -41,7 +40,6 @@
 
 import kotlinx.coroutines.CoroutineScope;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class AssistantAttentionConditionTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
index cd2efde..480754c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -31,7 +31,6 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.shared.condition.Condition;
 
@@ -44,7 +43,6 @@
 
 import kotlinx.coroutines.CoroutineScope;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamConditionTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index ffcaeee..3d1efa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams.touch;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -40,7 +41,6 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
 import com.android.systemui.dreams.touch.scrim.ScrimController;
@@ -64,7 +64,6 @@
 import java.util.Collections;
 import java.util.Optional;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
index ff6d97d..6aa821f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
@@ -27,7 +27,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.system.InputChannelCompat;
@@ -43,7 +42,6 @@
 
 import java.util.Optional;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ShadeTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
index da39381..017fdbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
@@ -26,7 +26,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -38,7 +37,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BouncerlessScrimControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
index 81f6fe3..4ee4a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
@@ -25,7 +25,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.RoboPilotTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -38,7 +37,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ScrimManagerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 291dda20..a646823 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard;
 
-import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
 import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT;
@@ -635,29 +634,6 @@
     }
 
     @Test
-    public void lockAfterSpecifiedAfterDreamStarted() {
-        int currentUserId = 99;
-        int userSpecificTimeout = 5999;
-        KeyguardUpdateMonitor.setCurrentUser(currentUserId);
-
-        // set mDeviceInteractive to true
-        mViewMediator.onStartedWakingUp(WAKE_REASON_WAKE_MOTION, false);
-        mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false);
-        when(mLockPatternUtils.isSecure(currentUserId)).thenReturn(true);
-        when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L);
-        when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
-                KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, currentUserId)).thenReturn(userSpecificTimeout);
-        mSystemClock.setElapsedRealtime(0L);
-        ArgumentCaptor<PendingIntent> pendingIntent = ArgumentCaptor.forClass(PendingIntent.class);
-
-        mViewMediator.onDreamingStarted();
-
-        verify(mAlarmManager).setExactAndAllowWhileIdle(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
-                eq(Long.valueOf(userSpecificTimeout)), pendingIntent.capture());
-        assertEquals(DELAYED_KEYGUARD_ACTION, pendingIntent.getValue().getIntent().getAction());
-    }
-
-    @Test
     public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() {
         mViewMediator.hideSurfaceBehindKeyguard();
 
@@ -1023,7 +999,6 @@
         mViewMediator.setShowingLocked(false);
         when(mKeyguardStateController.isShowing()).thenReturn(false);
 
-        mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false);
         mViewMediator.onDreamingStarted();
         assertFalse(mViewMediator.isShowingAndNotOccluded());
     }
@@ -1034,7 +1009,6 @@
         mViewMediator.setShowingLocked(true);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
 
-        mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, true);
         mViewMediator.onDreamingStarted();
         assertTrue(mViewMediator.isShowingAndNotOccluded());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index cfee3b8..e20d3af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -23,7 +23,6 @@
 import android.content.pm.PackageManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.camera.CameraGestureHelper
 import com.android.systemui.settings.UserTracker
@@ -43,7 +42,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class CameraQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 49168d0..faf9751 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -26,7 +26,6 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.notification.EnableZenModeDialog
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
@@ -61,7 +60,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index c85c7f6..548b564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.keyguard.data.quickaffordance
 
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.animation.Expandable
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import kotlinx.coroutines.flow.Flow
@@ -25,7 +24,6 @@
 import kotlinx.coroutines.yield
 
 /** Fake implementation of a quick affordance data source. */
-@RoboPilotTest
 class FakeKeyguardQuickAffordanceConfig(
     override val key: String,
     private val pickerName: String = key,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index c3e28ae..4ae144c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.statusbar.policy.FlashlightController
@@ -42,7 +41,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index ef56a98..7d68cc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Expandable
 import com.android.systemui.controls.controller.ControlsController
@@ -41,7 +40,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 4f071bd..1e80fb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
@@ -49,7 +48,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index bd0b71d..99a0185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.backup.BackupHelper
 import com.android.systemui.settings.FakeUserTracker
@@ -53,7 +52,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index 0797d07..a1c9f87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -21,7 +21,6 @@
 import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
@@ -44,7 +43,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index d8c0341..b15352b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -21,7 +21,6 @@
 import android.media.AudioManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
@@ -46,7 +45,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class MuteQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 9d983b8..521dea3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -20,7 +20,6 @@
 import android.content.Intent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
@@ -40,7 +39,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 613c4ce..02db0d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -24,7 +24,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.animation.Expandable
@@ -51,7 +50,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index 1414bac..a9b9c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.camera.CameraIntentsWrapper
 import com.android.systemui.coroutines.collectLastValue
@@ -45,7 +44,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 944b059..d8cdf29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -32,7 +32,6 @@
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.data.repository.FaceSensorInfo
@@ -79,7 +78,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidJUnit4::class)
 class BiometricSettingsRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 307204da..819d08a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -44,7 +44,6 @@
 import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -122,7 +121,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index def016a..a58bc52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.coroutines.collectLastValue
@@ -49,7 +48,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
index 7eb8a26..9be5558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.shared.model.DevicePosture
@@ -39,7 +38,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DevicePostureRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: DevicePostureRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 126b841..567e0a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -56,7 +55,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
 
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 7983e30..b9119e1 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
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.common.shared.model.Position
@@ -64,7 +63,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardRepositoryImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index 4942cf8..799bd5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -20,7 +20,6 @@
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.coroutines.collectLastValue
@@ -46,7 +45,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 class LightRevealScrimRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index 7f784d8..ee47c58f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.logging.TrustRepositoryLogger
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
@@ -46,7 +45,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class TrustRepositoryTest : SysuiTestCase() {
     @Mock private lateinit var trustManager: TrustManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 181cc88..d6e19cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -19,7 +19,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -43,7 +42,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardDismissActionInteractorTest : SysuiTestCase() {
     private lateinit var keyguardRepository: FakeKeyguardRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
index c407b14..a5cfbbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.TrustGrantFlags
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.shared.model.DismissAction
@@ -40,7 +39,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardDismissInteractorTest : SysuiTestCase() {
     private lateinit var dispatcher: TestDispatcher
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index b527510..06eb0dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -28,8 +28,10 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.biometrics.shared.model.LockoutMode
+import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -156,7 +158,6 @@
                 fakeDeviceEntryFingerprintAuthRepository,
                 fakeUserRepository,
                 facePropertyRepository,
-                fakeKeyguardRepository,
                 faceWakeUpTriggersConfig,
                 powerInteractor,
             )
@@ -440,6 +441,43 @@
         }
 
     @Test
+    fun faceAuthIsRequestedWhenWalletIsLaunchedAndIfFaceAuthIsStrong() =
+        testScope.runTest {
+            underTest.start()
+            facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
+
+            underTest.onWalletLaunched()
+
+            runCurrent()
+            assertThat(faceAuthRepository.runningAuthRequest.value)
+                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true))
+        }
+
+    @Test
+    fun faceAuthIsNotTriggeredIfFaceAuthIsWeak() =
+        testScope.runTest {
+            underTest.start()
+            facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.WEAK))
+
+            underTest.onWalletLaunched()
+
+            runCurrent()
+            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+        }
+
+    @Test
+    fun faceAuthIsNotTriggeredIfFaceAuthIsConvenience() =
+        testScope.runTest {
+            underTest.start()
+            facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE))
+
+            underTest.onWalletLaunched()
+
+            runCurrent()
+            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+        }
+
+    @Test
     fun faceUnlockIsDisabledWhenFpIsLockedOut() =
         testScope.runTest {
             underTest.start()
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 ca52cdb..9ee22c8 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
@@ -20,7 +20,6 @@
 import android.app.StatusBarManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
@@ -47,7 +46,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardInteractorTest : SysuiTestCase() {
 
@@ -206,7 +204,8 @@
                     fromScene = SceneKey.Gone,
                     toScene = SceneKey.Lockscreen,
                     progress = flowOf(0f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             assertThat(isAnimate).isFalse()
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 bbe6823..900413c 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() {
@@ -132,58 +134,73 @@
     }
 
     @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)
+        whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
 
-        // 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(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_awakeKeyguard_primaryBouncerAlreadyShowing() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(true)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+        verifyActionsDoNothing(KeyEvent.KEYCODE_ENTER)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
     }
 
     @Test
@@ -253,4 +270,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/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 13025a0..0c74a38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
@@ -51,7 +50,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardLongPressInteractorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 8c13bb4..347d580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.common.shared.model.ContentDescription
@@ -73,7 +72,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
index fdcc66b..71fcf6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
@@ -41,7 +40,6 @@
 import org.mockito.MockitoAnnotations.initMocks
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 9e9c25e..29b546b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -19,7 +19,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -42,7 +41,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class KeyguardTransitionInteractorTest : SysuiTestCase() {
@@ -230,4 +228,432 @@
 
         assertThat(startedSteps).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f))
     }
+
+    @Test
+    fun isInTransitionToState() = testScope.runTest {
+        val results by collectValues(underTest.isInTransitionToState(GONE))
+
+        sendSteps(
+                TransitionStep(AOD, DOZING, 0f, STARTED),
+                TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                TransitionStep(AOD, DOZING, 1f, FINISHED),
+        )
+
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(GONE, DOZING, 0f, STARTED),
+                TransitionStep(GONE, DOZING, 0f, RUNNING),
+                TransitionStep(GONE, DOZING, 1f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+                true,
+        ))
+    }
+
+    @Test
+    fun isInTransitionFromState() = testScope.runTest {
+        val results by collectValues(underTest.isInTransitionFromState(DOZING))
+
+        sendSteps(
+                TransitionStep(AOD, DOZING, 0f, STARTED),
+                TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                TransitionStep(AOD, DOZING, 1f, FINISHED),
+        )
+
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(GONE, DOZING, 0f, STARTED),
+                TransitionStep(GONE, DOZING, 0f, RUNNING),
+                TransitionStep(GONE, DOZING, 1f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+                true,
+        ))
+    }
+
+    @Test
+    fun isInTransitionFromStateWhere() = testScope.runTest {
+        val results by collectValues(underTest.isInTransitionFromStateWhere {
+            it == DOZING
+        })
+
+        sendSteps(
+                TransitionStep(AOD, DOZING, 0f, STARTED),
+                TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                TransitionStep(AOD, DOZING, 1f, FINISHED),
+        )
+
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(GONE, DOZING, 0f, STARTED),
+                TransitionStep(GONE, DOZING, 0f, RUNNING),
+                TransitionStep(GONE, DOZING, 1f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+                true,
+        ))
+    }
+
+    @Test
+    fun isInTransitionWhere() = testScope.runTest {
+        val results by collectValues(underTest.isInTransitionWhere(
+            fromStatePredicate = { it == DOZING },
+            toStatePredicate = { it == GONE },
+        ))
+
+        sendSteps(
+                TransitionStep(AOD, DOZING, 0f, STARTED),
+                TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                TransitionStep(AOD, DOZING, 1f, FINISHED),
+        )
+
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(GONE, DOZING, 0f, STARTED),
+                TransitionStep(GONE, DOZING, 0f, RUNNING),
+                TransitionStep(GONE, DOZING, 1f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+                true,
+        ))
+    }
+
+    @Test
+    fun isFinishedInStateWhere() = testScope.runTest {
+        val results by collectValues(underTest.isFinishedInStateWhere { it == GONE } )
+
+        sendSteps(
+                TransitionStep(AOD, DOZING, 0f, STARTED),
+                TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                TransitionStep(AOD, DOZING, 1f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false, // Finished in DOZING, not GONE.
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(GONE, DOZING, 0f, STARTED),
+                TransitionStep(GONE, DOZING, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(TransitionStep(GONE, DOZING, 1f, FINISHED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+                true,
+        ))
+    }
+
+    @Test
+    fun isFinishedInState() = testScope.runTest {
+        val results by collectValues(underTest.isFinishedInState(GONE))
+
+        sendSteps(
+                TransitionStep(AOD, DOZING, 0f, STARTED),
+                TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                TransitionStep(AOD, DOZING, 1f, FINISHED),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false, // Finished in DOZING, not GONE.
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(
+                TransitionStep(GONE, DOZING, 0f, STARTED),
+                TransitionStep(GONE, DOZING, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+        ))
+
+        sendSteps(TransitionStep(GONE, DOZING, 1f, FINISHED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(
+                TransitionStep(DOZING, GONE, 0f, STARTED),
+                TransitionStep(DOZING, GONE, 0f, RUNNING),
+        )
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+        ))
+
+        sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
+
+        assertThat(results).isEqualTo(listOf(
+                false,
+                true,
+                false,
+                true,
+        ))
+    }
+
+    private suspend fun sendSteps(vararg steps: TransitionStep) {
+        steps.forEach {
+            repository.sendTransitionStep(it)
+            testScope.runCurrent()
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 906d948..c02add1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
@@ -45,7 +44,6 @@
 import org.mockito.Spy
 
 @SmallTest
-@RoboPilotTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 class LightRevealScrimInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 73ecae5..16f2fa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -38,7 +37,6 @@
 import org.mockito.MockitoAnnotations.initMocks
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
index a22f603..5b29a86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
@@ -20,7 +20,6 @@
 import android.view.RemoteAnimationTarget
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardViewController
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
@@ -41,7 +40,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWithLooper(setAsMainLooper = true)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class KeyguardSurfaceBehindParamsApplierTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 7a17435..9b2db3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -19,7 +19,6 @@
 import android.app.IActivityTaskManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -35,7 +34,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
new file mode 100644
index 0000000..1768f8c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -0,0 +1,197 @@
+/*
+ * 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 androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@RunWith(JUnit4::class)
+@SmallTest
+class AlternateBouncerViewModelTest : SysuiTestCase() {
+
+    private lateinit var testScope: TestScope
+
+    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+    @Mock private lateinit var falsingManager: FalsingManager
+
+    private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+    private lateinit var transitionInteractor: KeyguardTransitionInteractor
+    private lateinit var underTest: AlternateBouncerViewModel
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        testScope = TestScope()
+
+        val transitionInteractorWithDependencies =
+            KeyguardTransitionInteractorFactory.create(testScope.backgroundScope)
+        transitionInteractor = transitionInteractorWithDependencies.keyguardTransitionInteractor
+        transitionRepository = transitionInteractorWithDependencies.repository
+        underTest =
+            AlternateBouncerViewModel(
+                statusBarKeyguardViewManager,
+                transitionInteractor,
+                falsingManager,
+            )
+    }
+
+    @Test
+    fun transitionToAlternateBouncer_scrimAlphaUpdate() =
+        runTest(UnconfinedTestDispatcher()) {
+            val scrimAlphas by collectValues(underTest.scrimAlpha)
+
+            transitionRepository.sendTransitionStep(
+                stepToAlternateBouncer(0f, TransitionState.STARTED)
+            )
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(.4f))
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f))
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f))
+
+            assertThat(scrimAlphas.size).isEqualTo(4)
+            scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+        }
+
+    @Test
+    fun transitionFromAlternateBouncer_scrimAlphaUpdate() =
+        runTest(UnconfinedTestDispatcher()) {
+            val scrimAlphas by collectValues(underTest.scrimAlpha)
+
+            transitionRepository.sendTransitionStep(
+                stepFromAlternateBouncer(0f, TransitionState.STARTED)
+            )
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.4f))
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f))
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f))
+
+            assertThat(scrimAlphas.size).isEqualTo(4)
+            scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+        }
+
+    @Test
+    fun clickListenerUpdate() =
+        runTest(UnconfinedTestDispatcher()) {
+            val clickListener by collectLastValue(underTest.onClickListener)
+
+            // keyguard state => ALTERNATE_BOUNCER
+            transitionRepository.sendTransitionStep(
+                stepToAlternateBouncer(0f, TransitionState.STARTED)
+            )
+            assertThat(clickListener).isNull()
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f))
+            assertThat(clickListener).isNull()
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f))
+            assertThat(clickListener).isNull()
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f))
+            assertThat(clickListener).isNotNull()
+
+            // ALTERNATE_BOUNCER -> keyguard state
+            transitionRepository.sendTransitionStep(
+                stepFromAlternateBouncer(0f, TransitionState.STARTED)
+            )
+            assertThat(clickListener).isNotNull()
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f))
+            assertThat(clickListener).isNull()
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f))
+            assertThat(clickListener).isNull()
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f))
+            assertThat(clickListener).isNull()
+        }
+
+    @Test
+    fun forcePluginOpen() =
+        runTest(UnconfinedTestDispatcher()) {
+            val forcePluginOpen by collectLastValue(underTest.forcePluginOpen)
+            transitionRepository.sendTransitionStep(
+                stepToAlternateBouncer(0f, TransitionState.STARTED)
+            )
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f))
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f))
+            transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f))
+            assertThat(forcePluginOpen).isTrue()
+
+            transitionRepository.sendTransitionStep(
+                stepFromAlternateBouncer(0f, TransitionState.STARTED)
+            )
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f))
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f))
+            transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f))
+            assertThat(forcePluginOpen).isFalse()
+        }
+
+    private fun stepToAlternateBouncer(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return step(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.ALTERNATE_BOUNCER,
+            value = value,
+            transitionState = state,
+        )
+    }
+
+    private fun stepFromAlternateBouncer(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return step(
+            from = KeyguardState.ALTERNATE_BOUNCER,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+        )
+    }
+
+    private fun step(
+        from: KeyguardState,
+        to: KeyguardState,
+        value: Float,
+        transitionState: TransitionState
+    ): TransitionStep {
+        return TransitionStep(
+            from = from,
+            to = to,
+            value = value,
+            transitionState = transitionState,
+            ownerName = "AlternateBouncerViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index bfc6f31..6d47aed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -46,7 +45,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: DreamingToLockscreenTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 75c8bff..cf20129 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: GoneToDreamingTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 6e94691..7de28de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -28,10 +29,9 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class LockscreenSceneViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 12fe07f..89a1d2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: LockscreenToDreamingTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 83ae631..41f8856 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: LockscreenToOccludedTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 8860399..ec95cb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: OccludedToLockscreenTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index da372ea..9364097 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.coroutines.collectValues
@@ -46,7 +45,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 9ab9b3d..32acefe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
@@ -46,7 +45,6 @@
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class UdfpsAodViewModelTest : SysuiTestCase() {
     private val defaultPadding = 12
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index d57765c..c8c134a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs;
 
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
@@ -41,6 +43,7 @@
 import android.os.Bundle;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -49,7 +52,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
@@ -60,6 +62,7 @@
 import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.CommandQueue;
@@ -110,6 +113,9 @@
     @Mock private FeatureFlagsClassic mFeatureFlags;
     private View mQsView;
 
+    private final CommandQueue mCommandQueue =
+            new CommandQueue(mContext, new FakeDisplayTracker(mContext));
+
     private QSImpl mUnderTest;
 
 
@@ -120,6 +126,16 @@
         mUnderTest.onComponentCreated(mQsComponent, null);
     }
 
+    /*
+     * Regression test for b/303180152.
+     */
+    @Test
+    public void testDisableCallbackOnDisabledQuickSettingsUponCreationDoesntCrash() {
+        QSImpl other = instantiate();
+        mCommandQueue.disable(Display.DEFAULT_DISPLAY, 0, DISABLE2_QUICK_SETTINGS);
+
+        other.onComponentCreated(mQsComponent, null);
+    }
 
     @Test
     public void testSaveState() {
@@ -473,7 +489,6 @@
 
     private QSImpl instantiate() {
         MockitoAnnotations.initMocks(this);
-        CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
 
         setupQsComponent();
         setUpViews();
@@ -484,11 +499,11 @@
         return new QSImpl(
                 new RemoteInputQuickSettingsDisabler(
                         mContext,
-                        commandQueue,
+                        mCommandQueue,
                         new ResourcesSplitShadeStateController(),
                         mock(ConfigurationController.class)),
                 mStatusBarStateController,
-                commandQueue,
+                mCommandQueue,
                 mQSMediaHost,
                 mQQSMediaHost,
                 mBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index e2ac7bc..b825c08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -42,7 +43,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.ui.MediaHost;
@@ -50,6 +50,7 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.util.animation.DisappearParameters;
 
@@ -341,11 +342,92 @@
     }
 
     @Test
-    public void onViewDetached_removesJustTheAssociatedCallback() {
+    public void onDestroy_removesJustTheAssociatedCallback() {
+        QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+        mController.destroy();
+        verify(mQSTile).removeCallback(record.callback);
+        verify(mQSTile, never()).removeCallbacks();
+
+        assertThat(mController.mRecords).isEmpty();
+    }
+
+    @Test
+    public void onViewDettached_callbackNotRemoved() {
         QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
 
         mController.onViewDetached();
-        verify(mQSTile).removeCallback(record.callback);
+        verify(mQSTile, never()).removeCallback(record.callback);
         verify(mQSTile, never()).removeCallbacks();
     }
+
+    @Test
+    public void onInit_qsHostCallbackAdded() {
+        verify(mQSHost).addCallback(any());
+    }
+
+    @Test
+    public void onViewDettached_qsHostCallbackNotRemoved() {
+        mController.onViewDetached();
+        verify(mQSHost, never()).removeCallback(any());
+    }
+
+    @Test
+    public void onDestroy_qsHostCallbackRemoved() {
+        mController.destroy();
+        verify(mQSHost).removeCallback(any());
+    }
+
+    @Test
+    public void setTiles_sameTiles_doesntRemoveAndReaddViews() {
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+        mController.setTiles();
+
+        clearInvocations(mQSPanel);
+
+        mController.setTiles();
+        verify(mQSPanel, never()).removeTile(any());
+        verify(mQSPanel, never()).addTile(any());
+    }
+
+    @Test
+    public void setTiles_differentTiles_allTilesRemovedAndNewTilesAdded() {
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+        mController.setTiles();
+
+        clearInvocations(mQSPanel);
+
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile));
+        mController.setTiles();
+
+        verify(mQSPanel, times(2)).removeTile(any());
+        verify(mQSPanel).addTile(any());
+    }
+
+    @Test
+    public void detachAndReattach_sameTiles_doesntRemoveAndReAddViews() {
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+        mController.setTiles();
+
+        clearInvocations(mQSPanel);
+
+        mController.onViewDetached();
+        mController.onViewAttached();
+        verify(mQSPanel, never()).removeTile(any());
+        verify(mQSPanel, never()).addTile(any());
+    }
+
+    @Test
+    public void setTiles_sameTilesDifferentOrder_removesAndReadds() {
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+        mController.setTiles();
+
+        clearInvocations(mQSPanel);
+
+        when(mQSHost.getTiles()).thenReturn(List.of(mOtherTile, mQSTile));
+        mController.setTiles();
+
+        verify(mQSPanel, times(2)).removeTile(any());
+        verify(mQSPanel, times(2)).addTile(any());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index 9a55f72..d277fca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
@@ -19,7 +19,6 @@
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -39,7 +38,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class AutoAddSettingsRepositoryTest : SysuiTestCase() {
     private val secureSettings = FakeSettings()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
index 30f5811..3db676d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
@@ -20,7 +20,6 @@
 import android.content.SharedPreferences
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.util.FakeSharedPreferences
@@ -30,7 +29,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 995de66..070e07a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -32,7 +32,6 @@
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.util.mockito.any
@@ -62,7 +61,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
index dc09a33..ff8a9bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -4,7 +4,6 @@
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.FakeBroadcastDispatcher
 import com.android.systemui.coroutines.collectLastValue
@@ -24,7 +23,6 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
-@RoboPilotTest
 class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
     private val dispatcher = StandardTestDispatcher()
     private val testScope = TestScope(dispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 08adebb..f7c3b21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -40,7 +39,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class TileSpecSettingsRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
index 2087623..9516c21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
@@ -2,14 +2,12 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 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
 
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class TilesSettingConverterTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
index 81fd72b..36e860e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
@@ -3,7 +3,6 @@
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.data.model.RestoreData
@@ -24,7 +23,6 @@
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
-@RoboPilotTest
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class UserAutoAddRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
index 389580c1..d4a9fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
@@ -3,7 +3,6 @@
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.data.model.RestoreData
@@ -23,7 +22,6 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RoboPilotTest
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
index 15e401d..4454a3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.util.mockito.mock
@@ -29,7 +28,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class AutoAddableSettingListTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index 7c6dd24..d153e9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
@@ -36,7 +35,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class AutoAddableSettingTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
index 469eee3..ec139e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -36,7 +35,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class CallbackControllerAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index b6eaa39..4fae532 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -42,7 +41,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class CastAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
index a755fbb..9e2d1f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +40,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DataSaverAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
index daacca51..0116bd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -44,7 +43,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DeviceControlsAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
index 4b5f7f6..e7ea9a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +40,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class HotspotAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
index 32d9db2..20fd360 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
@@ -19,7 +19,6 @@
 import android.hardware.display.NightDisplayListener
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dagger.NightDisplayListenerModule
@@ -50,7 +49,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
index fb513a6..19ac63c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.ReduceBrightColorsController
@@ -44,7 +43,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
index 5ce15fa..d645ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
@@ -52,7 +51,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class SafetyCenterAutoAddableTest : SysuiTestCase() {
     private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
index 1c8cb54..83ff35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -38,7 +37,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class WalletAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index de1d29fd..adccc84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -23,7 +23,6 @@
 import android.content.pm.UserInfo.FLAG_PROFILE
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +40,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class WorkTileAutoAddableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index bb18115..41a7ec0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dump.DumpManager
@@ -47,7 +46,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class AutoAddInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index a750524..a89338a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -24,7 +24,6 @@
 import android.service.quicksettings.Tile
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dump.nano.SystemUIProtoDump
@@ -71,7 +70,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class CurrentTilesInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
index 151b256..0d97115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
@@ -17,7 +17,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.shade.ShadeController
 import org.junit.Before
@@ -27,7 +26,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class PanelInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
index 2e6b50b..f73cab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -2,7 +2,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.data.model.RestoreData
@@ -20,7 +19,6 @@
 import org.junit.runner.RunWith
 import org.mockito.MockitoAnnotations
 
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class RestoreReconciliationInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
index 34c4c98..558e769 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
@@ -19,14 +19,12 @@
 import android.content.ComponentName
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class TileSpecTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
index 06b7a9f..5659f01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
@@ -19,7 +19,6 @@
 import android.content.Intent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
 import org.junit.Before
@@ -32,7 +31,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
index 2c4e10e..9861606 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.QSEvent
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
@@ -34,7 +33,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class QSTileAnalyticsTest : SysuiTestCase() {
 
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 4f25d12..a6199c2 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
@@ -24,7 +24,6 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.RestrictedLockUtils
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.util.mockito.any
@@ -46,7 +45,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DisabledByPolicyInteractorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 4401e0d..f1fcee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -39,7 +38,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class QSTileLoggerTest : SysuiTestCase() {
 
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 4760dfa..2084aeb 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
@@ -20,7 +20,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.android.internal.logging.InstanceId
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.common.shared.model.ContentDescription
@@ -49,7 +48,6 @@
 
 // TODO(b/299909368): Add more tests
 @MediumTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index f1c99d7..58e36be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -38,11 +39,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class QuickSettingsSceneViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 91f3865..2e16577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.scene
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
@@ -51,6 +52,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -58,7 +60,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 /**
  * Integration test cases for the Scene Framework.
@@ -80,7 +81,7 @@
  */
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SceneFrameworkIntegrationTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
@@ -462,7 +463,8 @@
                 fromScene = getCurrentSceneInUi(),
                 toScene = to.key,
                 progress = progressFlow,
-                isUserInputDriven = false,
+                isInitiatedByUserInput = false,
+                isUserInputOngoing = flowOf(false),
             )
         runCurrent()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index ff28d2d..740c6d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.scene.data.repository
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -28,13 +29,13 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SceneContainerRepositoryTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
@@ -119,7 +120,8 @@
                     fromScene = SceneKey.Lockscreen,
                     toScene = SceneKey.Shade,
                     progress = progress,
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
index 2187f36..7ae501d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.data.repository
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.SysuiTestCase
@@ -24,9 +25,11 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class WindowRootViewVisibilityRepositoryTest : SysuiTestCase() {
     private val iStatusBarService = mock<IStatusBarService>()
     private val executor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index afc0e69..31d26c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -26,16 +25,14 @@
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SceneInteractorTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
@@ -86,7 +83,8 @@
                     fromScene = SceneKey.Lockscreen,
                     toScene = SceneKey.Shade,
                     progress = progress,
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
@@ -124,7 +122,8 @@
                     fromScene = underTest.desiredScene.value.key,
                     toScene = SceneKey.Shade,
                     progress = progress,
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(transitionTo).isEqualTo(SceneKey.Shade)
 
@@ -161,7 +160,8 @@
                         fromScene = SceneKey.Gone,
                         toScene = SceneKey.Lockscreen,
                         progress = flowOf(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             val transitioning by
@@ -180,7 +180,8 @@
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.QuickSettings,
                         progress = flowOf(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             underTest.setTransitionState(transitionState)
@@ -197,7 +198,8 @@
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.Lockscreen,
                         progress = flowOf(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             val transitioning by
@@ -225,7 +227,8 @@
                     fromScene = SceneKey.Shade,
                     toScene = SceneKey.Lockscreen,
                     progress = flowOf(0.5f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(transitioning).isTrue()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index 3785fd7..8be4eeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.SysuiTestCase
@@ -36,16 +37,16 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 
 @SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
 class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
 
     private val testScope = TestScope()
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 0216a0a..3b9621e 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
@@ -20,6 +20,7 @@
 
 import android.os.PowerManager
 import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
@@ -46,14 +47,13 @@
 import org.junit.Assume.assumeTrue
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SceneContainerStartableTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
@@ -109,7 +109,8 @@
                     fromScene = SceneKey.Gone,
                     toScene = SceneKey.Shade,
                     progress = flowOf(0.5f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(isVisible).isTrue()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -122,7 +123,8 @@
                     fromScene = SceneKey.Shade,
                     toScene = SceneKey.Gone,
                     progress = flowOf(0.5f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(isVisible).isTrue()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 0b56a59..c89cd9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.scene.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -29,10 +30,9 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SceneContainerViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 1bb2ff8..263b001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -42,6 +42,7 @@
         @Parameterized.Parameters
         fun data(): Iterable<String> =
             listOf(
+                Intent.ACTION_LOCALE_CHANGED,
                 Intent.ACTION_USER_INFO_CHANGED,
                 Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 52b25f8..c32d259 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -177,7 +177,8 @@
         verify(context)
                 .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
         with(captor.value) {
-            assertThat(countActions()).isEqualTo(6)
+            assertThat(countActions()).isEqualTo(7)
+            assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
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 7b12931..8d8c70e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -79,7 +79,6 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
 import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -120,6 +119,7 @@
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragmentLegacy;
+import com.android.systemui.res.R;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -153,7 +153,6 @@
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
@@ -170,6 +169,7 @@
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
@@ -182,6 +182,8 @@
 import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
+import dagger.Lazy;
+
 import org.junit.After;
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
@@ -193,7 +195,6 @@
 import java.util.List;
 import java.util.Optional;
 
-import dagger.Lazy;
 import kotlinx.coroutines.CoroutineDispatcher;
 
 public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@@ -208,7 +209,7 @@
     @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
     @Mock protected ViewPropertyAnimator mViewPropertyAnimator;
     @Mock protected KeyguardBottomAreaView mQsFrame;
-    @Mock protected HeadsUpManagerPhone mHeadsUpManager;
+    @Mock protected HeadsUpManager mHeadsUpManager;
     @Mock protected NotificationShelfController mNotificationShelfController;
     @Mock protected NotificationGutsManager mGutsManager;
     @Mock protected KeyguardStatusBarView mKeyguardStatusBar;
@@ -527,7 +528,7 @@
         NotificationWakeUpCoordinator coordinator =
                 new NotificationWakeUpCoordinator(
                         mDumpManager,
-                        mock(HeadsUpManagerPhone.class),
+                        mock(HeadsUpManager.class),
                         new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
                                 mInteractionJankMonitor, mShadeExpansionStateManager),
                         mKeyguardBypassController,
@@ -547,7 +548,7 @@
                 mLockscreenShadeTransitionController,
                 new FalsingCollectorFake(),
                 mDumpManager);
-        when(mKeyguardStatusViewComponentFactory.build(any()))
+        when(mKeyguardStatusViewComponentFactory.build(any(), any()))
                 .thenReturn(mKeyguardStatusViewComponent);
         when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
                 .thenReturn(mKeyguardClockSwitchController);
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 2ed2090..eb00610 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -47,18 +47,34 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.colorextraction.ColorExtractor;
+import com.android.keyguard.KeyguardSecurityModel;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+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;
+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.power.data.repository.FakePowerRepository;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.FakeWindowRootViewComponent;
 import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.data.repository.SceneContainerRepository;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.logger.SceneLogger;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
@@ -71,9 +87,9 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.user.domain.interactor.UserInteractor;
 
 import com.google.common.util.concurrent.MoreExecutors;
@@ -109,6 +125,7 @@
     @Mock private SysuiColorExtractor mColorExtractor;
     @Mock ColorExtractor.GradientColors mGradientColors;
     @Mock private DumpManager mDumpManager;
+    @Mock private KeyguardSecurityModel mKeyguardSecurityModel;
     @Mock private KeyguardStateController mKeyguardStateController;
     @Mock private ScreenOffAnimationController mScreenOffAnimationController;
     @Mock private AuthController mAuthController;
@@ -123,6 +140,8 @@
 
     private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
     private float mPreferredRefreshRate = -1;
+    private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+    private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
 
     @Before
     public void setUp() {
@@ -137,22 +156,86 @@
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
 
+        FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
+        FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
+        FakeShadeRepository shadeRepository = new FakeShadeRepository();
+        FakePowerRepository powerRepository = new FakePowerRepository();
+
+        PowerInteractor powerInteractor = new PowerInteractor(
+                powerRepository,
+                new FalsingCollectorFake(),
+                mScreenOffAnimationController,
+                mStatusBarStateController);
+
+        SceneInteractor sceneInteractor = new SceneInteractor(
+                mTestScope.getBackgroundScope(),
+                new SceneContainerRepository(
+                        mTestScope.getBackgroundScope(),
+                        mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())),
+                powerRepository,
+                mock(SceneLogger.class));
+
+        FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
+        FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
+        KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
+                keyguardRepository,
+                new FakeCommandQueue(),
+                powerInteractor,
+                featureFlags,
+                sceneContainerFlags,
+                new FakeDeviceEntryRepository(),
+                new FakeKeyguardBouncerRepository(),
+                configurationRepository,
+                shadeRepository,
+                () -> sceneInteractor);
+
+        FakeKeyguardTransitionRepository keyguardTransitionRepository =
+                new FakeKeyguardTransitionRepository();
+
+        KeyguardTransitionInteractor keyguardTransitionInteractor =
+                new KeyguardTransitionInteractor(
+                        mTestScope.getBackgroundScope(),
+                        keyguardTransitionRepository,
+                        () -> keyguardInteractor,
+                        () -> mFromLockscreenTransitionInteractor,
+                        () -> mFromPrimaryBouncerTransitionInteractor);
+
+        mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                mTestScope.getBackgroundScope(),
+                keyguardInteractor,
+                featureFlags,
+                shadeRepository,
+                powerInteractor);
+
+        mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                mTestScope.getBackgroundScope(),
+                keyguardInteractor,
+                featureFlags,
+                mKeyguardSecurityModel,
+                powerInteractor);
+
         mShadeInteractor =
                 new ShadeInteractor(
                         mTestScope.getBackgroundScope(),
+                        new FakeDeviceProvisioningRepository(),
                         new FakeDisableFlagsRepository(),
-                        new FakeSceneContainerFlags(),
-                        mUtils::sceneInteractor,
-                        new FakeKeyguardRepository(),
+                        mock(DozeParameters.class),
+                        sceneContainerFlags,
+                        () -> sceneInteractor,
+                        keyguardRepository,
+                        keyguardTransitionInteractor,
+                        powerInteractor,
                         new FakeUserSetupRepository(),
-                        mock(DeviceProvisionedController.class),
                         mock(UserInteractor.class),
                         new SharedNotificationContainerInteractor(
-                                new FakeConfigurationRepository(),
+                                configurationRepository,
                                 mContext,
                                 new ResourcesSplitShadeStateController()),
-                        new FakeShadeRepository()
-                );
+                        shadeRepository);
 
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
                 mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 9651072..b4f9e8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
 import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -83,7 +84,6 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
@@ -97,8 +97,9 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -144,6 +145,8 @@
     @Mock
     lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
     @Mock lateinit var keyEventInteractor: KeyEventInteractor
+    @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+    @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     private val notificationExpansionRepository = NotificationExpansionRepository()
 
     private lateinit var fakeClock: FakeSystemClock
@@ -176,6 +179,7 @@
         featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
         featureFlags.set(Flags.MIGRATE_NSSL, false)
+        featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
 
         testScope = TestScope()
         fakeClock = FakeSystemClock()
@@ -248,6 +252,8 @@
                 ),
                 BouncerLogger(logcatLogBuffer("BouncerLog")),
                 keyEventInteractor,
+                primaryBouncerInteractor,
+                alternateBouncerInteractor,
             )
         underTest.setupExpandedStatusBar()
         underTest.setDragDownHelper(dragDownHelper)
@@ -425,29 +431,37 @@
     }
 
     @Test
-    fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() {
-        // GIVEN not dozing
-        whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false)
+    fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() {
+        // GIVEN dozing
+        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
         // AND alternate bouncer doesn't want the touch
         whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
             .thenReturn(false)
-        // AND the lock icon doesn't want the touch
-        whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
-        // AND the notification panel can accept touches
-        whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true)
-        whenever(dragDownHelper.isDragDownEnabled).thenReturn(true)
-        whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
-
-        // AND the drag down helper doesn't want the touch (to pull the shade down)
-        whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+        // AND the lock icon wants the touch
+        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
+                .thenReturn(true)
 
         featureFlags.set(Flags.MIGRATE_NSSL, true)
 
-        // WHEN asked if should intercept touch
-        interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
+        // THEN touch should NOT be intercepted by NotificationShade
+        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
+    }
 
-        // Verify that NPVC gets a chance to use the touch
-        verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
+    @Test
+    fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
+        // GIVEN dozing
+        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
+        // AND alternate bouncer doesn't want the touch
+        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+                .thenReturn(false)
+        // AND the lock icon does NOT want the touch
+        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
+                .thenReturn(false)
+
+        featureFlags.set(Flags.MIGRATE_NSSL, true)
+
+        // THEN touch should NOT be intercepted by NotificationShade
+        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 0023020..189c9e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
 import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -141,6 +142,8 @@
         Optional<UnfoldTransitionProgressProvider>
     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+    @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     @Mock
     private lateinit var primaryBouncerToGoneTransitionViewModel:
         PrimaryBouncerToGoneTransitionViewModel
@@ -180,6 +183,7 @@
         featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
         featureFlags.set(Flags.MIGRATE_NSSL, false)
+        featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
         testScope = TestScope()
         controller =
             NotificationShadeWindowViewController(
@@ -250,6 +254,8 @@
                 ),
                 BouncerLogger(logcatLogBuffer("BouncerLog")),
                 Mockito.mock(KeyEventInteractor::class.java),
+                primaryBouncerInteractor,
+                alternateBouncerInteractor,
             )
 
         controller.setupExpandedStatusBar()
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 8138b32..65174ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -32,23 +32,39 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+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;
 import com.android.systemui.fragments.FragmentHostManager;
+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.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 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.power.data.repository.FakePowerRepository;
+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.data.repository.SceneContainerRepository;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.logger.SceneLogger;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -64,19 +80,21 @@
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.user.domain.interactor.UserInteractor;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
@@ -103,8 +121,7 @@
     protected SceneTestUtils mUtils = new SceneTestUtils(this);
     protected TestScope mTestScope = mUtils.getTestScope();
 
-    @Mock
-    protected Resources mResources;
+    @Mock protected Resources mResources;
     @Mock protected KeyguardBottomAreaView mQsFrame;
     @Mock protected KeyguardStatusBarView mKeyguardStatusBar;
     @Mock protected QS mQs;
@@ -126,6 +143,7 @@
     @Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
     @Mock protected ShadeHeaderController mShadeHeaderController;
     @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+    @Mock protected DozeParameters mDozeParameters;
     @Mock protected KeyguardStateController mKeyguardStateController;
     @Mock protected KeyguardBypassController mKeyguardBypassController;
     @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -144,7 +162,6 @@
     @Mock protected DumpManager mDumpManager;
     @Mock protected UiEventLogger mUiEventLogger;
     @Mock protected CastController mCastController;
-    @Mock protected DeviceProvisionedController mDeviceProvisionedController;
     @Mock protected UserInteractor mUserInteractor;
     protected FakeDisableFlagsRepository mDisableFlagsRepository =
             new FakeDisableFlagsRepository();
@@ -161,6 +178,8 @@
             new ShadeExpansionStateManager();
 
     protected FragmentHostManager.FragmentListener mFragmentListener;
+    private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+    private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
 
     @Before
     public void setup() {
@@ -169,21 +188,89 @@
         mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
                 mInteractionJankMonitor, mShadeExpansionStateManager);
 
-        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+        FakeDeviceProvisioningRepository deviceProvisioningRepository =
+                new FakeDeviceProvisioningRepository();
+        deviceProvisioningRepository.setDeviceProvisioned(true);
+        FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
+        FakePowerRepository powerRepository = new FakePowerRepository();
+        FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
+
+        PowerInteractor powerInteractor = new PowerInteractor(
+                powerRepository,
+                new FalsingCollectorFake(),
+                mock(ScreenOffAnimationController.class),
+                mStatusBarStateController);
+
+        SceneInteractor sceneInteractor = new SceneInteractor(
+                mTestScope.getBackgroundScope(),
+                new SceneContainerRepository(
+                        mTestScope.getBackgroundScope(),
+                        mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())),
+                powerRepository,
+                mock(SceneLogger.class));
+
+        FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
+        KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
+                mKeyguardRepository,
+                new FakeCommandQueue(),
+                powerInteractor,
+                featureFlags,
+                sceneContainerFlags,
+                new FakeDeviceEntryRepository(),
+                new FakeKeyguardBouncerRepository(),
+                configurationRepository,
+                mShadeRepository,
+                () -> sceneInteractor);
+
+        FakeKeyguardTransitionRepository keyguardTransitionRepository =
+                new FakeKeyguardTransitionRepository();
+
+        KeyguardTransitionInteractor keyguardTransitionInteractor =
+                new KeyguardTransitionInteractor(
+                        mTestScope.getBackgroundScope(),
+                        keyguardTransitionRepository,
+                        () -> keyguardInteractor,
+                        () -> mFromLockscreenTransitionInteractor,
+                        () -> mFromPrimaryBouncerTransitionInteractor);
+
+        mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                mTestScope.getBackgroundScope(),
+                keyguardInteractor,
+                featureFlags,
+                mShadeRepository,
+                powerInteractor);
+
+        mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                mTestScope.getBackgroundScope(),
+                keyguardInteractor,
+                featureFlags,
+                mock(KeyguardSecurityModel.class),
+                powerInteractor);
+
+        ResourcesSplitShadeStateController splitShadeStateController =
+                new ResourcesSplitShadeStateController();
+
         mShadeInteractor =
                 new ShadeInteractor(
                         mTestScope.getBackgroundScope(),
+                        deviceProvisioningRepository,
                         mDisableFlagsRepository,
-                        new FakeSceneContainerFlags(),
-                        () -> mUtils.sceneInteractor(),
+                        mDozeParameters,
+                        sceneContainerFlags,
+                        () -> sceneInteractor,
                         mKeyguardRepository,
+                        keyguardTransitionInteractor,
+                        powerInteractor,
                         new FakeUserSetupRepository(),
-                        mDeviceProvisionedController,
                         mUserInteractor,
                         new SharedNotificationContainerInteractor(
-                                new FakeConfigurationRepository(),
+                                configurationRepository,
                                 mContext,
-                                new ResourcesSplitShadeStateController()),
+                                splitShadeStateController),
                         mShadeRepository
                 );
 
@@ -262,7 +349,7 @@
                 mShadeInteractor,
                 new JavaAdapter(mTestScope.getBackgroundScope()),
                 mCastController,
-                new ResourcesSplitShadeStateController()
+                splitShadeStateController
         );
         mQsController.init();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index b5841a9..215f8b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
 import android.view.Display
 import android.view.WindowManager
 import androidx.test.filters.SmallTest
@@ -54,6 +55,7 @@
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class ShadeControllerImplTest : SysuiTestCase() {
     private val executor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 2be1c09..bcb060d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -16,50 +16,53 @@
 
 package com.android.systemui.shade.data.repository
 
-import android.app.ActivityManager
 import android.app.StatusBarManager.DISABLE2_NONE
 import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
 import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
 import android.content.pm.UserInfo
 import android.os.UserManager
 import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.SysUITestModule
+import com.android.TestMocksModule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 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.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
-import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.UserDomainLayerModule
 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.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -70,50 +73,55 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 class ShadeInteractorTest : SysuiTestCase() {
+
+    @Mock private lateinit var dozeParameters: DozeParameters
+
+    private lateinit var testComponent: TestComponent
+
+    private val configurationRepository
+        get() = testComponent.configurationRepository
+    private val deviceProvisioningRepository
+        get() = testComponent.deviceProvisioningRepository
+    private val disableFlagsRepository
+        get() = testComponent.disableFlagsRepository
+    private val keyguardRepository
+        get() = testComponent.keyguardRepository
+    private val keyguardTransitionRepository
+        get() = testComponent.keygaurdTransitionRepository
+    private val powerRepository
+        get() = testComponent.powerRepository
+    private val sceneInteractor
+        get() = testComponent.sceneInteractor
+    private val shadeRepository
+        get() = testComponent.shadeRepository
+    private val testScope
+        get() = testComponent.testScope
+    private val userRepository
+        get() = testComponent.userRepository
+    private val userSetupRepository
+        get() = testComponent.userSetupRepository
+
     private lateinit var underTest: ShadeInteractor
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val featureFlags = FakeFeatureFlags()
-    private val sceneContainerFlags = FakeSceneContainerFlags()
-    private val sceneInteractor = utils.sceneInteractor()
-    private val userSetupRepository = FakeUserSetupRepository()
-    private val userRepository = FakeUserRepository()
-    private val disableFlagsRepository = FakeDisableFlagsRepository()
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val shadeRepository = FakeShadeRepository()
-    private val configurationRepository = FakeConfigurationRepository()
-    private val sharedNotificationContainerInteractor =
-        SharedNotificationContainerInteractor(
-            configurationRepository,
-            mContext,
-            ResourcesSplitShadeStateController()
-        )
-
-    @Mock private lateinit var manager: UserManager
-    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
-    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
-    @Mock private lateinit var activityStarter: ActivityStarter
-    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var activityManager: ActivityManager
-    @Mock private lateinit var uiEventLogger: UiEventLogger
-    @Mock private lateinit var guestInteractor: GuestUserInteractor
-
-    private lateinit var userInteractor: UserInteractor
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
-        featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-
-        val refreshUsersScheduler =
-            RefreshUsersScheduler(
-                applicationScope = testScope.backgroundScope,
-                mainDispatcher = utils.testDispatcher,
-                repository = userRepository,
-            )
+        testComponent =
+            DaggerShadeInteractorTest_TestComponent.factory()
+                .create(
+                    test = this,
+                    featureFlags =
+                        FakeFeatureFlagsClassicModule {
+                            set(Flags.FACE_AUTH_REFACTOR, false)
+                            set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+                        },
+                    mocks =
+                        TestMocksModule(
+                            dozeParameters = dozeParameters,
+                        ),
+                )
+        underTest = testComponent.underTest
 
         runBlocking {
             val userInfos =
@@ -131,44 +139,6 @@
             userRepository.setUserInfos(userInfos)
             userRepository.setSelectedUserInfo(userInfos[0])
         }
-        userInteractor =
-            UserInteractor(
-                applicationContext = context,
-                repository = userRepository,
-                activityStarter = activityStarter,
-                keyguardInteractor =
-                    KeyguardInteractorFactory.create(featureFlags = featureFlags)
-                        .keyguardInteractor,
-                featureFlags = featureFlags,
-                manager = manager,
-                headlessSystemUserMode = headlessSystemUserMode,
-                applicationScope = testScope.backgroundScope,
-                telephonyInteractor =
-                    TelephonyInteractor(
-                        repository = FakeTelephonyRepository(),
-                    ),
-                broadcastDispatcher = fakeBroadcastDispatcher,
-                keyguardUpdateMonitor = keyguardUpdateMonitor,
-                backgroundDispatcher = utils.testDispatcher,
-                activityManager = activityManager,
-                refreshUsersScheduler = refreshUsersScheduler,
-                guestUserInteractor = guestInteractor,
-                uiEventLogger = uiEventLogger,
-                userRestrictionChecker = mock(),
-            )
-        underTest =
-            ShadeInteractor(
-                testScope.backgroundScope,
-                disableFlagsRepository,
-                sceneContainerFlags,
-                { sceneInteractor },
-                keyguardRepository,
-                userSetupRepository,
-                deviceProvisionedController,
-                userInteractor,
-                sharedNotificationContainerInteractor,
-                shadeRepository,
-            )
     }
 
     @Test
@@ -188,7 +158,7 @@
     @Test
     fun isExpandToQsEnabled_deviceNotProvisioned_false() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+            deviceProvisioningRepository.setDeviceProvisioned(false)
 
             val actual by collectLastValue(underTest.isExpandToQsEnabled)
 
@@ -198,7 +168,7 @@
     @Test
     fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
 
             userSetupRepository.setUserSetup(false)
             userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true))
@@ -211,7 +181,7 @@
     @Test
     fun isExpandToQsEnabled_shadeNotEnabled_false() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             userSetupRepository.setUserSetup(true)
 
             disableFlagsRepository.disableFlags.value =
@@ -227,7 +197,7 @@
     @Test
     fun isExpandToQsEnabled_quickSettingsNotEnabled_false() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             userSetupRepository.setUserSetup(true)
 
             disableFlagsRepository.disableFlags.value =
@@ -242,7 +212,7 @@
     @Test
     fun isExpandToQsEnabled_dozing_false() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             userSetupRepository.setUserSetup(true)
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(
@@ -259,7 +229,7 @@
     @Test
     fun isExpandToQsEnabled_userSetup_true() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             keyguardRepository.setIsDozing(false)
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(
@@ -276,7 +246,7 @@
     @Test
     fun isExpandToQsEnabled_notSimpleUserSwitcher_true() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             keyguardRepository.setIsDozing(false)
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(
@@ -293,7 +263,7 @@
     @Test
     fun isExpandToQsEnabled_respondsToDozingUpdates() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             keyguardRepository.setIsDozing(false)
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(
@@ -321,7 +291,7 @@
     @Test
     fun isExpandToQsEnabled_respondsToDisableUpdates() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             keyguardRepository.setIsDozing(false)
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(
@@ -353,7 +323,7 @@
     @Test
     fun isExpandToQsEnabled_respondsToUserUpdates() =
         testScope.runTest {
-            whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+            deviceProvisioningRepository.setDeviceProvisioned(true)
             keyguardRepository.setIsDozing(false)
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(
@@ -625,7 +595,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -662,7 +633,8 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -698,7 +670,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = SceneKey.Shade,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -974,7 +947,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -1011,7 +985,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
-                        isUserInputDriven = true,
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -1048,7 +1023,8 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -1085,7 +1061,8 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
-                        isUserInputDriven = true,
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -1120,8 +1097,9 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Lockscreen,
                         toScene = SceneKey.QuickSettings,
-                        progress = progress,
-                        isUserInputDriven = true,
+                        progress = MutableStateFlow(0f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -1129,4 +1107,168 @@
             // THEN interacting is false
             assertThat(interacting).isFalse()
         }
+
+    @Test
+    fun isShadeTouchable_isFalse_whenFrpIsActive() =
+        testScope.runTest {
+            deviceProvisioningRepository.setFactoryResetProtectionActive(true)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+            runCurrent()
+            assertThat(isShadeTouchable).isFalse()
+        }
+
+    @Test
+    fun isShadeTouchable_isFalse_whenDeviceAsleepAndNotPulsing() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            // goingToSleep == false
+            // TODO: remove?
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    to = DozeStateModel.DOZE_AOD,
+                )
+            )
+            val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+            runCurrent()
+            assertThat(isShadeTouchable).isFalse()
+        }
+
+    @Test
+    fun isShadeTouchable_isTrue_whenDeviceAsleepAndPulsing() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            // goingToSleep == false
+            // TODO: remove?
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    to = DozeStateModel.DOZE_PULSING,
+                )
+            )
+            val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+            runCurrent()
+            assertThat(isShadeTouchable).isTrue()
+        }
+
+    @Test
+    fun isShadeTouchable_isFalse_whenStartingToSleepAndNotControlScreenOff() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            // goingToSleep == true
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            whenever(dozeParameters.shouldControlScreenOff()).thenReturn(false)
+            val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+            runCurrent()
+            assertThat(isShadeTouchable).isFalse()
+        }
+
+    @Test
+    fun isShadeTouchable_isTrue_whenStartingToSleepAndControlScreenOff() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            // goingToSleep == true
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            whenever(dozeParameters.shouldControlScreenOff()).thenReturn(true)
+            val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+            runCurrent()
+            assertThat(isShadeTouchable).isTrue()
+        }
+
+    @Test
+    fun isShadeTouchable_isTrue_whenNotAsleep() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.AWAKE,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+            runCurrent()
+            assertThat(isShadeTouchable).isTrue()
+        }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val underTest: ShadeInteractor
+
+        val configurationRepository: FakeConfigurationRepository
+        val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+        val disableFlagsRepository: FakeDisableFlagsRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val keygaurdTransitionRepository: FakeKeyguardTransitionRepository
+        val powerRepository: FakePowerRepository
+        val sceneInteractor: SceneInteractor
+        val shadeRepository: FakeShadeRepository
+        val testScope: TestScope
+        val userRepository: FakeUserRepository
+        val userSetupRepository: FakeUserSetupRepository
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 7463e65..6eeafefd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionStateManager
@@ -43,7 +42,6 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class ShadeRepositoryImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index 0925858..bb20d94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.shade.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -16,15 +17,15 @@
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class ShadeHeaderViewModelTest : SysuiTestCase() {
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
@@ -84,7 +85,8 @@
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.QuickSettings,
                         progress = MutableStateFlow(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             )
@@ -102,7 +104,8 @@
                         fromScene = SceneKey.QuickSettings,
                         toScene = SceneKey.Shade,
                         progress = MutableStateFlow(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             )
@@ -120,7 +123,8 @@
                         fromScene = SceneKey.Gone,
                         toScene = SceneKey.Shade,
                         progress = MutableStateFlow(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 602bd5f..c423782 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
@@ -37,11 +38,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class ShadeSceneViewModelTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index e714736..ae659f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -131,11 +131,10 @@
 
         fun addClock(
             id: ClockId,
-            name: String,
             create: (ClockId) -> ClockController = ::failFactory,
             getThumbnail: (ClockId) -> Drawable? = ::failThumbnail
         ): FakeClockPlugin {
-            metadata.add(ClockMetadata(id, name))
+            metadata.add(ClockMetadata(id))
             createCallbacks[id] = create
             thumbnailCallbacks[id] = getThumbnail
             return this
@@ -149,7 +148,7 @@
         scope = TestScope(dispatcher)
 
         fakeDefaultProvider = FakeClockPlugin()
-            .addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail })
+            .addClock(DEFAULT_CLOCK_ID, { mockDefaultClock }, { mockThumbnail })
         whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
 
         val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>()
@@ -183,13 +182,13 @@
     @Test
     fun pluginRegistration_CorrectState() {
         val plugin1 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1")
-            .addClock("clock_2", "clock 2")
+            .addClock("clock_1")
+            .addClock("clock_2")
         val lifecycle1 = FakeLifecycle("1", plugin1)
 
         val plugin2 = FakeClockPlugin()
-            .addClock("clock_3", "clock 3")
-            .addClock("clock_4", "clock 4")
+            .addClock("clock_3")
+            .addClock("clock_4")
         val lifecycle2 = FakeLifecycle("2", plugin2)
 
         pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
@@ -198,11 +197,11 @@
         assertEquals(
             list.toSet(),
             setOf(
-                ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
-                ClockMetadata("clock_1", "clock 1"),
-                ClockMetadata("clock_2", "clock 2"),
-                ClockMetadata("clock_3", "clock 3"),
-                ClockMetadata("clock_4", "clock 4")
+                ClockMetadata(DEFAULT_CLOCK_ID),
+                ClockMetadata("clock_1"),
+                ClockMetadata("clock_2"),
+                ClockMetadata("clock_3"),
+                ClockMetadata("clock_4")
             )
         )
     }
@@ -216,13 +215,13 @@
     @Test
     fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() {
         val plugin1 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
-            .addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
+            .addClock("clock_1", { mockClock }, { mockThumbnail })
+            .addClock("clock_2", { mockClock }, { mockThumbnail })
         val lifecycle1 = spy(FakeLifecycle("1", plugin1))
 
         val plugin2 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1")
-            .addClock("clock_2", "clock 2")
+            .addClock("clock_1")
+            .addClock("clock_2")
         val lifecycle2 = spy(FakeLifecycle("2", plugin2))
 
         pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
@@ -231,9 +230,9 @@
         assertEquals(
             list.toSet(),
             setOf(
-                ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
-                ClockMetadata("clock_1", "clock 1"),
-                ClockMetadata("clock_2", "clock 2")
+                ClockMetadata(DEFAULT_CLOCK_ID),
+                ClockMetadata("clock_1"),
+                ClockMetadata("clock_2")
             )
         )
 
@@ -248,13 +247,13 @@
     @Test
     fun createCurrentClock_pluginConnected() {
         val plugin1 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1")
-            .addClock("clock_2", "clock 2")
+            .addClock("clock_1")
+            .addClock("clock_2")
         val lifecycle1 = spy(FakeLifecycle("1", plugin1))
 
         val plugin2 = FakeClockPlugin()
-            .addClock("clock_3", "clock 3", { mockClock })
-            .addClock("clock_4", "clock 4")
+            .addClock("clock_3", { mockClock })
+            .addClock("clock_4")
         val lifecycle2 = spy(FakeLifecycle("2", plugin2))
 
         registry.applySettings(ClockSettings("clock_3", null))
@@ -268,13 +267,13 @@
     @Test
     fun activeClockId_changeAfterPluginConnected() {
         val plugin1 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1")
-            .addClock("clock_2", "clock 2")
+            .addClock("clock_1")
+            .addClock("clock_2")
         val lifecycle1 = spy(FakeLifecycle("1", plugin1))
 
         val plugin2 = FakeClockPlugin()
-            .addClock("clock_3", "clock 3", { mockClock })
-            .addClock("clock_4", "clock 4")
+            .addClock("clock_3", { mockClock })
+            .addClock("clock_4")
         val lifecycle2 = spy(FakeLifecycle("2", plugin2))
 
         registry.applySettings(ClockSettings("clock_3", null))
@@ -289,13 +288,13 @@
     @Test
     fun createDefaultClock_pluginDisconnected() {
         val plugin1 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1")
-            .addClock("clock_2", "clock 2")
+            .addClock("clock_1")
+            .addClock("clock_2")
         val lifecycle1 = spy(FakeLifecycle("1", plugin1))
 
         val plugin2 = FakeClockPlugin()
-            .addClock("clock_3", "clock 3")
-            .addClock("clock_4", "clock 4")
+            .addClock("clock_3")
+            .addClock("clock_4")
         val lifecycle2 = spy(FakeLifecycle("2", plugin2))
 
         registry.applySettings(ClockSettings("clock_3", null))
@@ -310,13 +309,13 @@
     @Test
     fun pluginRemoved_clockAndListChanged() {
         val plugin1 = FakeClockPlugin()
-            .addClock("clock_1", "clock 1")
-            .addClock("clock_2", "clock 2")
+            .addClock("clock_1")
+            .addClock("clock_2")
         val lifecycle1 = spy(FakeLifecycle("1", plugin1))
 
         val plugin2 = FakeClockPlugin()
-            .addClock("clock_3", "clock 3", { mockClock })
-            .addClock("clock_4", "clock 4")
+            .addClock("clock_3", { mockClock })
+            .addClock("clock_4")
         val lifecycle2 = spy(FakeLifecycle("2", plugin2))
 
         var changeCallCount = 0
@@ -415,13 +414,13 @@
 
     @Test
     fun pluginAddRemove_concurrentModification() {
-        val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+        val plugin1 = FakeClockPlugin().addClock("clock_1")
         val lifecycle1 = FakeLifecycle("1", plugin1)
-        val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+        val plugin2 = FakeClockPlugin().addClock("clock_2")
         val lifecycle2 = FakeLifecycle("2", plugin2)
-        val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+        val plugin3 = FakeClockPlugin().addClock("clock_3")
         val lifecycle3 = FakeLifecycle("3", plugin3)
-        val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
+        val plugin4 = FakeClockPlugin().addClock("clock_4")
         val lifecycle4 = FakeLifecycle("4", plugin4)
 
         // Set the current clock to the final clock to load
@@ -450,10 +449,10 @@
 
         // Verify all plugins were correctly loaded into the registry
         assertEquals(registry.getClocks().toSet(), setOf(
-            ClockMetadata("DEFAULT", "Default Clock"),
-            ClockMetadata("clock_2", "clock 2"),
-            ClockMetadata("clock_3", "clock 3"),
-            ClockMetadata("clock_4", "clock 4")
+            ClockMetadata("DEFAULT"),
+            ClockMetadata("clock_2"),
+            ClockMetadata("clock_3"),
+            ClockMetadata("clock_4")
         ))
     }
 
@@ -527,8 +526,8 @@
         featureFlags.set(TRANSIT_CLOCK, flag)
         registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
         val plugin = FakeClockPlugin()
-                .addClock("clock_1", "clock 1")
-                .addClock("DIGITAL_CLOCK_METRO", "metro clock")
+                .addClock("clock_1")
+                .addClock("DIGITAL_CLOCK_METRO")
         val lifecycle = FakeLifecycle("metro", plugin)
         pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)
 
@@ -536,17 +535,17 @@
         if (flag) {
             assertEquals(
                     setOf(
-                            ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
-                            ClockMetadata("clock_1", "clock 1"),
-                            ClockMetadata("DIGITAL_CLOCK_METRO", "metro clock")
+                            ClockMetadata(DEFAULT_CLOCK_ID),
+                            ClockMetadata("clock_1"),
+                            ClockMetadata("DIGITAL_CLOCK_METRO")
                     ),
                     list.toSet()
             )
         } else {
             assertEquals(
                     setOf(
-                            ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
-                            ClockMetadata("clock_1", "clock 1")
+                            ClockMetadata(DEFAULT_CLOCK_ID),
+                            ClockMetadata("clock_1")
                     ),
                     list.toSet()
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
index 7a2d122..b8fe2f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -33,7 +32,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class BcSmartspaceConfigProviderTest : SysuiTestCase() {
     @Mock private lateinit var featureFlags: FeatureFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index f1c181f..e093859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -26,7 +26,6 @@
 import android.widget.FrameLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController
 import com.android.systemui.plugins.BcSmartspaceConfigPlugin
@@ -53,7 +52,6 @@
 import org.mockito.Spy
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class DreamSmartspaceControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
index 7af29ba..886c61a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
@@ -25,7 +25,6 @@
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
@@ -52,7 +51,6 @@
 
 @SmallTest
 @TestableLooper.RunWithLooper
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
index a7c223d..0b5aea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
@@ -19,7 +19,6 @@
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -36,7 +35,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class LockscreenPreconditionTest : SysuiTestCase() {
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 14e58e5..e8923a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,41 +5,32 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
 import com.android.systemui.ExpandHelper
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+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.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+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
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
-import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.policy.FakeConfigurationController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.UserDomainLayerModule
+import dagger.BindsInstance
+import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import org.junit.After
 import org.junit.Assert.assertFalse
@@ -60,9 +51,8 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
 
 private fun <T> anyObject(): T {
     return Mockito.anyObject<T>()
@@ -73,68 +63,38 @@
 @RunWith(AndroidTestingRunner::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
 
-    lateinit var transitionController: LockscreenShadeTransitionController
+    private lateinit var testComponent: TestComponent
+
+    private val transitionController
+        get() = testComponent.transitionController
+    private val configurationController
+        get() = testComponent.configurationController
+    private val disableFlagsRepository
+        get() = testComponent.disableFlagsRepository
+    private val testScope
+        get() = testComponent.testScope
+
     lateinit var row: ExpandableNotificationRow
-    @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
-    @Mock lateinit var logger: LSShadeTransitionLogger
-    @Mock lateinit var dumpManager: DumpManager
+
+    @Mock lateinit var centralSurfaces: CentralSurfaces
+    @Mock lateinit var depthController: NotificationShadeDepthController
+    @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
     @Mock lateinit var keyguardBypassController: KeyguardBypassController
     @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
-    @Mock lateinit var falsingCollector: FalsingCollector
-    @Mock lateinit var ambientState: AmbientState
-    @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
-    @Mock lateinit var scrimController: ScrimController
-    @Mock lateinit var falsingManager: FalsingManager
-    @Mock lateinit var shadeViewController: ShadeViewController
     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
-    @Mock lateinit var depthController: NotificationShadeDepthController
-    @Mock lateinit var stackscroller: NotificationStackScrollLayout
-    @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
-    @Mock lateinit var mCentralSurfaces: CentralSurfaces
     @Mock lateinit var qS: QS
-    @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
-    @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
-    @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
-    @Mock lateinit var activityStarter: ActivityStarter
+    @Mock lateinit var scrimController: ScrimController
+    @Mock lateinit var shadeViewController: ShadeViewController
+    @Mock lateinit var stackscroller: NotificationStackScrollLayout
+    @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
     @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
-    private val sceneContainerFlags = FakeSceneContainerFlags()
-    private val sceneInteractor = utils.sceneInteractor()
-    private val disableFlagsRepository = FakeDisableFlagsRepository()
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val configurationRepository = FakeConfigurationRepository()
-    private val sharedNotificationContainerInteractor = SharedNotificationContainerInteractor(
-        configurationRepository,
-        mContext,
-            ResourcesSplitShadeStateController()
-    )
-    private val shadeInteractor =
-        ShadeInteractor(
-            testScope.backgroundScope,
-            disableFlagsRepository,
-            sceneContainerFlags,
-            { sceneInteractor },
-            keyguardRepository,
-            userSetupRepository = FakeUserSetupRepository(),
-            deviceProvisionedController = mock(),
-            userInteractor = mock(),
-            sharedNotificationContainerInteractor,
-            repository = FakeShadeRepository(),
-        )
-    private val powerInteractor = PowerInteractorFactory.create().powerInteractor
-    @JvmField @Rule val mockito = MockitoJUnit.rule()
 
-    private val configurationController = FakeConfigurationController()
+    @JvmField @Rule val mockito = MockitoJUnit.rule()
 
     @Before
     fun setup() {
-        // By default, have the shade enabled
-        disableFlagsRepository.disableFlags.value = DisableFlagsModel()
-        testScope.runCurrent()
-
         val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
         row = helper.createRow()
         context
@@ -143,55 +103,9 @@
         context
             .getOrCreateTestableResources()
             .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
-        transitionController =
-            LockscreenShadeTransitionController(
-                statusBarStateController = statusbarStateController,
-                logger = logger,
-                keyguardBypassController = keyguardBypassController,
-                lockScreenUserManager = lockScreenUserManager,
-                falsingCollector = falsingCollector,
-                ambientState = ambientState,
-                mediaHierarchyManager = mediaHierarchyManager,
-                depthController = depthController,
-                wakefulnessLifecycle = wakefulnessLifecycle,
-                context = context,
-                configurationController = configurationController,
-                falsingManager = falsingManager,
-                dumpManager = dumpManager,
-                splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
-                singleShadeOverScrollerFactory = { singleShadeOverScroller },
-                scrimTransitionController =
-                    LockscreenShadeScrimTransitionController(
-                        scrimController,
-                        context,
-                        configurationController,
-                        dumpManager,
-                            ResourcesSplitShadeStateController()
-                    ),
-                keyguardTransitionControllerFactory = { notificationPanelController ->
-                    LockscreenShadeKeyguardTransitionController(
-                        mediaHierarchyManager,
-                        notificationPanelController,
-                        context,
-                        configurationController,
-                        dumpManager,
-                            ResourcesSplitShadeStateController()
-                    )
-                },
-                qsTransitionControllerFactory = { qsTransitionController },
-                activityStarter = activityStarter,
-                shadeRepository = FakeShadeRepository(),
-                shadeInteractor = shadeInteractor,
-                powerInteractor = powerInteractor,
-                splitShadeStateController = ResourcesSplitShadeStateController()
-            )
-        transitionController.addCallback(transitionControllerCallback)
+
         whenever(nsslController.view).thenReturn(stackscroller)
         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
-        transitionController.shadeViewController = shadeViewController
-        transitionController.centralSurfaces = mCentralSurfaces
-        transitionController.qS = qS
-        transitionController.setStackScroller(nsslController)
         whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(nsslController.isInLockedDownShade).thenReturn(false)
         whenever(qS.isFullyCollapsed).thenReturn(true)
@@ -199,9 +113,36 @@
             .thenReturn(true)
         whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
         whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
-        whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
         whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
-        clearInvocations(mCentralSurfaces)
+
+        testComponent =
+            DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory()
+                .create(
+                    test = this,
+                    featureFlags =
+                        FakeFeatureFlagsClassicModule {
+                            set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+                        },
+                    mocks =
+                        TestMocksModule(
+                            notificationShadeDepthController = depthController,
+                            keyguardBypassController = keyguardBypassController,
+                            mediaHierarchyManager = mediaHierarchyManager,
+                            notificationLockscreenUserManager = lockScreenUserManager,
+                            notificationStackScrollLayoutController = nsslController,
+                            scrimController = scrimController,
+                            statusBarStateController = statusbarStateController,
+                        )
+                )
+
+        transitionController.addCallback(transitionControllerCallback)
+        transitionController.shadeViewController = shadeViewController
+        transitionController.centralSurfaces = centralSurfaces
+        transitionController.qS = qS
+        transitionController.setStackScroller(nsslController)
+        clearInvocations(centralSurfaces)
+
+        testScope.runCurrent()
     }
 
     @After
@@ -282,7 +223,7 @@
         transitionController.goToLockedShade(null)
         verify(statusbarStateController, never()).setState(anyInt())
         verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
-        verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+        verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
     }
 
     @Test
@@ -318,7 +259,7 @@
         verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
         verify(transitionControllerCallback, never())
             .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
-        verify(qsTransitionController, never()).dragDownAmount = anyFloat()
+        verify(qS, never()).setTransitionToFullShadeProgress(anyBoolean(), anyFloat(), anyFloat())
     }
 
     @Test
@@ -329,7 +270,7 @@
         verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
         verify(transitionControllerCallback)
             .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
-        verify(qsTransitionController).dragDownAmount = 10f
+        verify(qS).setTransitionToFullShadeProgress(eq(true), anyFloat(), anyFloat())
         verify(depthController).transitionToFullShadeProgress = anyFloat()
     }
 
@@ -532,8 +473,8 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(singleShadeOverScroller).expansionDragDownAmount = 10f
-        verifyZeroInteractions(splitShadeOverScroller)
+        verify(nsslController).setOverScrollAmount(0)
+        verify(scrimController, never()).setNotificationsOverScrollAmount(anyInt())
     }
 
     @Test
@@ -542,8 +483,8 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(splitShadeOverScroller).expansionDragDownAmount = 10f
-        verifyZeroInteractions(singleShadeOverScroller)
+        verify(nsslController).setOverScrollAmount(0)
+        verify(scrimController).setNotificationsOverScrollAmount(0)
     }
 
     @Test
@@ -591,6 +532,32 @@
         progress: Float,
         lockScreenNotificationsProgress: Float
     ) {
-        scrimController.setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
+        setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
+    }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val transitionController: LockscreenShadeTransitionController
+
+        val configurationController: FakeConfigurationController
+        val disableFlagsRepository: FakeDisableFlagsRepository
+        val testScope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
index fbb8ebf..20e5c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -29,9 +29,9 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
@@ -52,7 +52,7 @@
     private val collapsedHeight = 300
     private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock()
     private val bypassController: KeyguardBypassController = mock()
-    private val headsUpManager: HeadsUpManagerPhone = mock()
+    private val headsUpManager: HeadsUpManager = mock()
     private val roundnessManager: NotificationRoundnessManager = mock()
     private val configurationController: ConfigurationController = mock()
     private val statusBarStateController: StatusBarStateController = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 5b919d9..3fef1d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -72,7 +72,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(wifiPickerTracker)
+        `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(wifiPickerTracker)
 
         `when`(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntryConnected)
         `when`(wifiPickerTracker.wifiEntries).thenReturn(ArrayList<WifiEntry>().apply {
@@ -165,7 +165,7 @@
 
     @Test
     fun testReturnEmptyListWhenNoWifiPickerTracker() {
-        `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(null)
+        `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(null)
         val otherController = AccessPointControllerImpl(
                 userManager,
                 userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index d86f8bb..235ac5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -8,15 +8,15 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.HeadsUpUtil
-import com.android.systemui.res.R
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import kotlinx.coroutines.test.TestScope
@@ -37,7 +37,7 @@
 @RunWithLooper
 class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
     @Mock lateinit var notificationListContainer: NotificationListContainer
-    @Mock lateinit var headsUpManager: HeadsUpManagerPhone
+    @Mock lateinit var headsUpManager: HeadsUpManager
     @Mock lateinit var jankMonitor: InteractionJankMonitor
     @Mock lateinit var onFinishAnimationCallback: Runnable
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 257cc5b..4f1581c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -43,8 +43,8 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
-import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -87,7 +87,7 @@
 
     private val notifPipeline: NotifPipeline = mock()
     private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
-    private val headsUpManager: HeadsUpManager = mock()
+    private val headsUpManager: HeadsUpManagerPhone = mock()
     private val headsUpViewBinder: HeadsUpViewBinder = mock()
     private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock()
     private val remoteInputManager: NotificationRemoteInputManager = mock()
@@ -435,7 +435,7 @@
 
     private fun addHUN(entry: NotificationEntry) {
         huns.add(entry)
-        whenever(headsUpManager.topEntry).thenReturn(entry)
+        whenever(headsUpManager.getTopEntry()).thenReturn(entry)
         onHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
         notifLifetimeExtender.cancelLifetimeExtension(entry)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index fbd61f4..546abd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -23,7 +23,6 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.advanceTimeBy
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -40,10 +39,10 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 import com.android.systemui.util.mockito.any
@@ -247,7 +246,7 @@
             unseenFilter.onCleanup()
 
             // THEN: The SeenNotificationProvider has been updated to reflect the suppression
-            assertThat(seenNotificationsProvider.hasFilteredOutSeenNotifications).isTrue()
+            assertThat(notificationListInteractor.hasFilteredOutSeenNotifications.value).isTrue()
         }
     }
 
@@ -598,7 +597,7 @@
             FakeSettings().apply {
                 putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
             }
-        val seenNotificationsProvider = SeenNotificationsProviderImpl()
+        val notificationListInteractor = NotificationListInteractor(NotificationListRepository())
         val keyguardCoordinator =
             KeyguardCoordinator(
                 testDispatcher,
@@ -611,7 +610,7 @@
                 testScope.backgroundScope,
                 sectionHeaderVisibilityProvider,
                 fakeSettings,
-                seenNotificationsProvider,
+                notificationListInteractor,
                 statusBarStateController,
             )
         keyguardCoordinator.attach(notifPipeline)
@@ -619,7 +618,7 @@
             KeyguardCoordinatorTestScope(
                     keyguardCoordinator,
                     testScope,
-                    seenNotificationsProvider,
+                    notificationListInteractor,
                     fakeSettings,
                 )
                 .testBlock()
@@ -629,7 +628,7 @@
     private inner class KeyguardCoordinatorTestScope(
         private val keyguardCoordinator: KeyguardCoordinator,
         private val scope: TestScope,
-        val seenNotificationsProvider: SeenNotificationsProvider,
+        val notificationListInteractor: NotificationListInteractor,
         private val fakeSettings: FakeSettings,
     ) : CoroutineScope by scope {
         val testScheduler: TestCoroutineScheduler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
new file mode 100644
index 0000000..2f8f3bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+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.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {
+
+    private val testComponent: TestComponent =
+        DaggerNotificationsKeyguardViewStateRepositoryTest_TestComponent.factory()
+            .create(test = this)
+
+    @Test
+    fun areNotifsFullyHidden_reflectsWakeUpCoordinator() =
+        with(testComponent) {
+            testScope.runTest {
+                whenever(mockWakeUpCoordinator.notificationsFullyHidden).thenReturn(false)
+                val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
+                runCurrent()
+
+                assertThat(notifsFullyHidden).isFalse()
+
+                withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
+                    .onFullyHiddenChanged(true)
+                runCurrent()
+
+                assertThat(notifsFullyHidden).isTrue()
+            }
+        }
+
+    @Test
+    fun isPulseExpanding_reflectsWakeUpCoordinator() =
+        with(testComponent) {
+            testScope.runTest {
+                whenever(mockWakeUpCoordinator.isPulseExpanding()).thenReturn(false)
+                val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
+                runCurrent()
+
+                assertThat(isPulseExpanding).isFalse()
+
+                withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
+                    .onPulseExpansionChanged(true)
+                runCurrent()
+
+                assertThat(isPulseExpanding).isTrue()
+            }
+        }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val underTest: NotificationsKeyguardViewStateRepositoryImpl
+
+        val mockWakeUpCoordinator: NotificationWakeUpCoordinator
+        val testScope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+            ): TestComponent
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
new file mode 100644
index 0000000..705a5a3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class NotificationsKeyguardInteractorTest : SysuiTestCase() {
+
+    private val testComponent: TestComponent =
+        DaggerNotificationsKeyguardInteractorTest_TestComponent.factory().create(test = this)
+
+    @Test
+    fun areNotifsFullyHidden_reflectsRepository() =
+        with(testComponent) {
+            testScope.runTest {
+                repository.setNotificationsFullyHidden(false)
+                val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
+                runCurrent()
+
+                assertThat(notifsFullyHidden).isFalse()
+
+                repository.setNotificationsFullyHidden(true)
+                runCurrent()
+
+                assertThat(notifsFullyHidden).isTrue()
+            }
+        }
+
+    @Test
+    fun isPulseExpanding_reflectsRepository() =
+        with(testComponent) {
+            testScope.runTest {
+                repository.setPulseExpanding(false)
+                val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
+                runCurrent()
+
+                assertThat(isPulseExpanding).isFalse()
+
+                repository.setPulseExpanding(true)
+                runCurrent()
+
+                assertThat(isPulseExpanding).isTrue()
+            }
+        }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val underTest: NotificationsKeyguardInteractor
+
+        val repository: FakeNotificationsKeyguardViewStateRepository
+        val testScope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(@BindsInstance test: SysuiTestCase): TestComponent
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
index 7caa5ccc..e57986d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
@@ -26,9 +26,7 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.NotificationIconContainer
 import com.android.systemui.user.domain.UserDomainLayerModule
-import com.android.systemui.util.mockito.whenever
 import dagger.BindsInstance
 import dagger.Component
 import org.junit.Assert.assertFalse
@@ -37,7 +35,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -46,7 +43,6 @@
 class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() {
 
     @Mock private lateinit var dozeParams: DozeParameters
-    @Mock private lateinit var aodIcons: NotificationIconContainer
 
     private lateinit var testComponent: TestComponent
     private val underTest
@@ -85,15 +81,6 @@
         assertTrue(underTest.shouldShowLowPriorityIcons())
     }
 
-    @Test
-    fun testAppearResetsTranslation() {
-        underTest.setupAodIcons(aodIcons)
-        whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
-        underTest.appearAodIcons()
-        verify(aodIcons).translationY = 0f
-        verify(aodIcons).alpha = 1.0f
-    }
-
     @SysUISingleton
     @Component(
         modules =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
new file mode 100644
index 0000000..31efebb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.ui.AnimatedValue
+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.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
+
+    @Mock private lateinit var dozeParams: DozeParameters
+    @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController
+
+    private lateinit var testComponent: TestComponent
+    private val underTest: NotificationIconContainerAlwaysOnDisplayViewModel
+        get() = testComponent.underTest
+    private val deviceEntryRepository: FakeDeviceEntryRepository
+        get() = testComponent.deviceEntryRepository
+    private val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+        get() = testComponent.deviceProvisioningRepository
+    private val keyguardRepository: FakeKeyguardRepository
+        get() = testComponent.keyguardRepository
+    private val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+        get() = testComponent.keyguardTransitionRepository
+    private val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
+        get() = testComponent.notifsKeyguardRepository
+    private val powerRepository: FakePowerRepository
+        get() = testComponent.powerRepository
+    private val scope: TestScope
+        get() = testComponent.scope
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        testComponent =
+            DaggerNotificationIconContainerAlwaysOnDisplayViewModelTest_TestComponent.factory()
+                .create(
+                    test = this,
+                    featureFlags =
+                        FakeFeatureFlagsClassicModule {
+                            setDefault(Flags.FACE_AUTH_REFACTOR)
+                            set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
+                            setDefault(Flags.NEW_AOD_TRANSITION)
+                        },
+                    mocks =
+                        TestMocksModule(
+                            dozeParameters = dozeParams,
+                            screenOffAnimationController = screenOffAnimController,
+                        ),
+                )
+
+        keyguardRepository.setKeyguardShowing(true)
+        keyguardRepository.setKeyguardOccluded(false)
+        deviceProvisioningRepository.setFactoryResetProtectionActive(false)
+        powerRepository.updateWakefulness(
+            rawState = WakefulnessState.AWAKE,
+            lastWakeReason = WakeSleepReason.OTHER,
+            lastSleepReason = WakeSleepReason.OTHER,
+        )
+    }
+
+    @Test
+    fun animationsEnabled_isFalse_whenFrpIsActive() =
+        scope.runTest {
+            deviceProvisioningRepository.setFactoryResetProtectionActive(true)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    to = DozeStateModel.DOZE_AOD,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    to = DozeStateModel.DOZE_PULSING,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @Test
+    fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenNotAsleep() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.AWAKE,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenKeyguardIsShowing() =
+        scope.runTest {
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            assertThat(animationsEnabled).isTrue()
+
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            assertThat(animationsEnabled).isFalse()
+
+            keyguardRepository.setKeyguardShowing(false)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun isDozing_startAodTransition() =
+        scope.runTest {
+            val isDozing by collectLastValue(underTest.isDozing)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+            assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true))
+        }
+
+    @Test
+    fun isDozing_startDozeTransition() =
+        scope.runTest {
+            val isDozing by collectLastValue(underTest.isDozing)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.DOZING,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+            assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = false))
+        }
+
+    @Test
+    fun isDozing_startDozeToAodTransition() =
+        scope.runTest {
+            val isDozing by collectLastValue(underTest.isDozing)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.DOZING,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+            assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true))
+        }
+
+    @Test
+    fun isNotDozing_startAodToGoneTransition() =
+        scope.runTest {
+            val isDozing by collectLastValue(underTest.isDozing)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.GONE,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+            assertThat(isDozing).isEqualTo(AnimatedValue(false, isAnimating = true))
+        }
+
+    @Test
+    fun isDozing_stopAnimation() =
+        scope.runTest {
+            val isDozing by collectLastValue(underTest.isDozing)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.GONE,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+
+            underTest.completeDozeAnimation()
+            runCurrent()
+
+            assertThat(isDozing?.isAnimating).isEqualTo(false)
+        }
+
+    @Test
+    fun isNotVisible_pulseExpanding() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            notifsKeyguardRepository.setPulseExpanding(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isFalse()
+        }
+
+    @Test
+    fun isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    to = KeyguardState.GONE,
+                    transitionState = TransitionState.FINISHED,
+                )
+            )
+            whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false)
+            runCurrent()
+
+            assertThat(isVisible).isEqualTo(AnimatedValue(false, isAnimating = false))
+        }
+
+    @Test
+    fun isVisible_bypassEnabled() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            deviceEntryRepository.setBypassEnabled(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isTrue()
+        }
+
+    @Test
+    fun isNotVisible_pulseExpanding_notBypassing() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            notifsKeyguardRepository.setPulseExpanding(true)
+            deviceEntryRepository.setBypassEnabled(false)
+            runCurrent()
+
+            assertThat(isVisible?.value).isEqualTo(false)
+        }
+
+    @Test
+    fun isVisible_notifsFullyHidden_bypassEnabled() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            runCurrent()
+            notifsKeyguardRepository.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(true)
+            notifsKeyguardRepository.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true))
+        }
+
+    @Test
+    fun isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            notifsKeyguardRepository.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParams.alwaysOn).thenReturn(false)
+            notifsKeyguardRepository.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false))
+        }
+
+    @Test
+    fun isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            notifsKeyguardRepository.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParams.alwaysOn).thenReturn(true)
+            whenever(dozeParams.displayNeedsBlanking).thenReturn(true)
+            notifsKeyguardRepository.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false))
+        }
+
+    @Test
+    fun isVisible_notifsFullyHidden_bypassDisabled() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            runCurrent()
+            notifsKeyguardRepository.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParams.alwaysOn).thenReturn(true)
+            whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
+            notifsKeyguardRepository.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true))
+        }
+
+    @Test
+    fun isVisible_stopAnimation() =
+        scope.runTest {
+            val isVisible by collectLastValue(underTest.isVisible)
+            notifsKeyguardRepository.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParams.alwaysOn).thenReturn(true)
+            whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
+            notifsKeyguardRepository.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            underTest.completeVisibilityAnimation()
+            runCurrent()
+
+            assertThat(isVisible?.isAnimating).isEqualTo(false)
+        }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                BiometricsDomainLayerModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val underTest: NotificationIconContainerAlwaysOnDisplayViewModel
+
+        val deviceEntryRepository: FakeDeviceEntryRepository
+        val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+        val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
+        val powerRepository: FakePowerRepository
+        val scope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                mocks: TestMocksModule,
+                featureFlags: FakeFeatureFlagsClassicModule,
+            ): TestComponent
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
new file mode 100644
index 0000000..e1e7f92
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+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.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() {
+
+    @Mock lateinit var dozeParams: DozeParameters
+
+    private lateinit var testComponent: TestComponent
+    private val underTest: NotificationIconContainerStatusBarViewModel
+        get() = testComponent.underTest
+    private val deviceProvisioningRepository
+        get() = testComponent.deviceProvisioningRepository
+    private val keyguardTransitionRepository
+        get() = testComponent.keyguardTransitionRepository
+    private val keyguardRepository
+        get() = testComponent.keyguardRepository
+    private val powerRepository
+        get() = testComponent.powerRepository
+    private val scope
+        get() = testComponent.scope
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        testComponent =
+            DaggerNotificationIconContainerStatusBarViewModelTest_TestComponent.factory()
+                .create(
+                    test = this,
+                    // Configurable bindings
+                    featureFlags =
+                        FakeFeatureFlagsClassicModule {
+                            set(Flags.FACE_AUTH_REFACTOR, value = false)
+                            set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
+                        },
+                    mocks =
+                        TestMocksModule(
+                            dozeParameters = dozeParams,
+                        ),
+                )
+
+        keyguardRepository.setKeyguardShowing(false)
+        deviceProvisioningRepository.setFactoryResetProtectionActive(false)
+        powerRepository.updateWakefulness(
+            rawState = WakefulnessState.AWAKE,
+            lastWakeReason = WakeSleepReason.OTHER,
+            lastSleepReason = WakeSleepReason.OTHER,
+        )
+    }
+
+    @Test
+    fun animationsEnabled_isFalse_whenFrpIsActive() =
+        scope.runTest {
+            deviceProvisioningRepository.setFactoryResetProtectionActive(true)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    to = DozeStateModel.DOZE_AOD,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    to = DozeStateModel.DOZE_PULSING,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @Test
+    fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isFalse()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenNotAsleep() =
+        scope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.AWAKE,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.OTHER,
+            )
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            runCurrent()
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @Test
+    fun animationsEnabled_isTrue_whenKeyguardIsNotShowing() =
+        scope.runTest {
+            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            keyguardRepository.setKeyguardShowing(true)
+            runCurrent()
+
+            assertThat(animationsEnabled).isFalse()
+
+            keyguardRepository.setKeyguardShowing(false)
+            runCurrent()
+
+            assertThat(animationsEnabled).isTrue()
+        }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                // Real impls
+                BiometricsDomainLayerModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val underTest: NotificationIconContainerStatusBarViewModel
+
+        val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+        val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val powerRepository: FakePowerRepository
+        val scope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                mocks: TestMocksModule,
+                featureFlags: FakeFeatureFlagsClassicModule,
+            ): TestComponent
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index c9b77c5..9c20e54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -54,9 +54,9 @@
 import android.widget.TextView;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -132,7 +132,8 @@
     public void testBindNotification_SetsTextApplicationName() {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(),
-                mMockNotificationRow, mAssistantFeedbackController);
+                mMockNotificationRow, mAssistantFeedbackController, mStatusBarService,
+                mNotificationGutsManager);
         final TextView textView = mFeedbackInfo.findViewById(R.id.pkg_name);
         assertTrue(textView.getText().toString().contains("App Name"));
     }
@@ -143,7 +144,8 @@
         when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
                 .thenReturn(iconDrawable);
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(),
-                mMockNotificationRow, mAssistantFeedbackController);
+                mMockNotificationRow, mAssistantFeedbackController, mStatusBarService,
+                mNotificationGutsManager);
         final ImageView iconView = mFeedbackInfo.findViewById(R.id.pkg_icon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -153,7 +155,7 @@
         when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
                 .thenReturn(STATUS_SILENCED);
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
         TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
         assertEquals("This notification was automatically demoted to Silent by the system. "
                         + "Let the developer know your feedback. Was this correct?",
@@ -165,7 +167,7 @@
         when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
                 .thenReturn(STATUS_PROMOTED);
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
         TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
         assertEquals("This notification was automatically ranked higher in your shade. "
                         + "Let the developer know your feedback. Was this correct?",
@@ -177,7 +179,7 @@
         when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
                 .thenReturn(STATUS_ALERTED);
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
         TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
         assertEquals("This notification was automatically promoted to Default by the system. "
                         + "Let the developer know your feedback. Was this correct?",
@@ -189,7 +191,7 @@
         when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
                 .thenReturn(STATUS_DEMOTED);
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
         TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
         assertEquals("This notification was automatically ranked lower in your shade. "
                         + "Let the developer know your feedback. Was this correct?",
@@ -199,7 +201,7 @@
     @Test
     public void testPositiveFeedback() {
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
 
         final View yes = mFeedbackInfo.findViewById(R.id.yes);
         yes.performClick();
@@ -216,7 +218,7 @@
                 .thenReturn(true);
 
         mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
 
         final View no = mFeedbackInfo.findViewById(R.id.no);
         no.performClick();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9f2afdf..8a730cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -33,7 +33,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -89,8 +88,8 @@
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -152,7 +151,7 @@
     @Mock private AssistantFeedbackController mAssistantFeedbackController;
     @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
     @Mock private StatusBarStateController mStatusBarStateController;
-    @Mock private HeadsUpManagerPhone mHeadsUpManagerPhone;
+    @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private ActivityStarter mActivityStarter;
 
     @Mock private UserManager mUserManager;
@@ -171,7 +170,7 @@
                 mTestScope.getBackgroundScope(),
                 new WindowRootViewVisibilityRepository(mBarService, mExecutor),
                 new FakeKeyguardRepository(),
-                mHeadsUpManagerPhone,
+                mHeadsUpManager,
                 PowerInteractorFactory.create().getPowerInteractor());
 
         mGutsManager = new NotificationGutsManager(
@@ -196,9 +195,10 @@
                 mWindowRootViewVisibilityInteractor,
                 mNotificationLockscreenUserManager,
                 mStatusBarStateController,
+                mBarService,
                 mDeviceProvisionedController,
                 mMetricsLogger,
-                mHeadsUpManagerPhone,
+                mHeadsUpManager,
                 mActivityStarter);
         mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
                 mOnSettingsClickListener);
@@ -239,7 +239,7 @@
                 anyInt(),
                 anyBoolean(),
                 any(Runnable.class));
-        verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), true);
+        verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), true);
 
         assertEquals(View.VISIBLE, guts.getVisibility());
         mGutsManager.closeAndSaveGuts(false, false, true, 0, 0, false);
@@ -247,7 +247,7 @@
         verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
         verify(row, times(1)).setGutsView(any());
         mTestableLooper.processAllMessages();
-        verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), false);
+        verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index ac680e6..cb73108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -61,6 +61,7 @@
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -81,14 +82,13 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
-import com.android.systemui.res.R;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.systemui.wmshell.BubblesTestActivity;
 
@@ -123,7 +123,7 @@
     private final GroupMembershipManager mGroupMembershipManager;
     private final GroupExpansionManager mGroupExpansionManager;
     private ExpandableNotificationRow mRow;
-    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManager mHeadsUpManager;
     private final NotifBindPipeline mBindPipeline;
     private final NotifCollectionListener mBindPipelineEntryListener;
     private final RowContentBindStage mBindStage;
@@ -161,7 +161,7 @@
         mKeyguardBypassController = mock(KeyguardBypassController.class);
         mGroupMembershipManager = mock(GroupMembershipManager.class);
         mGroupExpansionManager = mock(GroupExpansionManager.class);
-        mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+        mHeadsUpManager = mock(HeadsUpManager.class);
         mIconManager = new IconManager(
                 mock(CommonNotifCollection.class),
                 mock(LauncherApps.class),
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 ffe312b..20197e3 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
@@ -77,7 +77,6 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.NotifStats;
@@ -88,13 +87,15 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
 import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor;
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
@@ -124,7 +125,7 @@
     @Mock private NotificationGutsManager mNotificationGutsManager;
     @Mock private NotificationsController mNotificationsController;
     @Mock private NotificationVisibilityProvider mVisibilityProvider;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     @Mock private TunerService mTunerService;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
@@ -170,8 +171,8 @@
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
 
-    private final SeenNotificationsProviderImpl mSeenNotificationsProvider =
-            new SeenNotificationsProviderImpl();
+    private final NotificationListInteractor mNotificationListInteractor =
+            new NotificationListInteractor(new NotificationListRepository());
 
     private NotificationStackScrollLayoutController mController;
 
@@ -503,7 +504,7 @@
     @Test
     public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
         initController(/* viewIsAttached= */ true);
-        mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true);
+        mNotificationListInteractor.setHasFilteredOutSeenNotifications(true);
         mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
         verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true);
         verify(mNotificationStackScrollLayout).updateFooter();
@@ -703,7 +704,7 @@
                 mUiEventLogger,
                 mRemoteInputManager,
                 mVisibilityLocationProviderDelegator,
-                mSeenNotificationsProvider,
+                mNotificationListInteractor,
                 mShadeController,
                 mJankMonitor,
                 mStackLogger,
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 3a820e8..236bcb4 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
@@ -163,6 +163,7 @@
         mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
         mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
         mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
+        mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION);
 
         // Inject dependencies before initializing the layout
         mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
@@ -247,25 +248,7 @@
     }
 
     @Test
-    public void testUpdateStackHeight_withDozeAmount_whenDozeChanging() {
-        final float dozeAmount = 0.5f;
-        mAmbientState.setDozeAmount(dozeAmount);
-
-        final float endHeight = 8f;
-        final float expansionFraction = 1f;
-        float expected = MathUtils.lerp(
-                endHeight * StackScrollAlgorithm.START_FRACTION,
-                endHeight, dozeAmount);
-
-        mStackScroller.updateStackHeight(endHeight, expansionFraction);
-        assertThat(mAmbientState.getStackHeight()).isEqualTo(expected);
-    }
-
-    @Test
     public void testUpdateStackHeight_withExpansionAmount_whenDozeNotChanging() {
-        final float dozeAmount = 1f;
-        mAmbientState.setDozeAmount(dozeAmount);
-
         final float endHeight = 8f;
         final float expansionFraction = 0.5f;
         final float expected = MathUtils.lerp(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index a52466d..93faa77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -241,6 +241,8 @@
     fun resetViewStates_isOnKeyguard_viewBecomesTransparent() {
         ambientState.setStatusBarState(StatusBarState.KEYGUARD)
         ambientState.hideAmount = 0.25f
+        whenever(notificationRow.isHeadsUpState).thenReturn(true)
+
         stackScrollAlgorithm.initView(context)
 
         stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
@@ -283,17 +285,20 @@
         val row2 = mockExpandableNotificationRow()
         hostView.addView(row2)
 
+        whenever(row1.isHeadsUpState).thenReturn(true)
+        whenever(row2.isHeadsUpState).thenReturn(false)
+
         ambientState.setStatusBarState(StatusBarState.KEYGUARD)
         ambientState.hideAmount = 0.25f
+        ambientState.dozeAmount = 0.33f
         notificationShelf.viewState.hidden = true
         ambientState.shelf = notificationShelf
         stackScrollAlgorithm.initView(context)
 
         stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
 
-        val expected = 1f - ambientState.hideAmount
-        assertThat(row1.viewState.alpha).isEqualTo(expected)
-        assertThat(row2.viewState.alpha).isEqualTo(expected)
+        assertThat(row1.viewState.alpha).isEqualTo(1f - ambientState.hideAmount)
+        assertThat(row2.viewState.alpha).isEqualTo(1f - ambientState.dozeAmount)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index e254dd0..ac11ff2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,36 +19,35 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
+import com.android.SysUITestModule
+import com.android.TestMocksModule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.res.R
 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.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
 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.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.UserDomainLayerModule
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -60,29 +59,27 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SharedNotificationContainerViewModelTest : SysuiTestCase() {
-    private val utils = SceneTestUtils(this)
 
-    private val testScope = utils.testScope
+    private lateinit var testComponent: TestComponent
 
-    private val disableFlagsRepository = FakeDisableFlagsRepository()
-    private val userSetupRepository = FakeUserSetupRepository()
-    private val shadeRepository = FakeShadeRepository()
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val sceneContainerFlags = FakeSceneContainerFlags()
-    private val sceneInteractor = utils.sceneInteractor()
-
-    private lateinit var configurationRepository: FakeConfigurationRepository
-    private lateinit var sharedNotificationContainerInteractor:
-        SharedNotificationContainerInteractor
-    private lateinit var underTest: SharedNotificationContainerViewModel
-    private lateinit var keyguardInteractor: KeyguardInteractor
-    private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
-    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
-    private lateinit var shadeInteractor: ShadeInteractor
+    private val shadeRepository
+        get() = testComponent.shadeRepository
+    private val keyguardRepository
+        get() = testComponent.keyguardRepository
+    private val configurationRepository
+        get() = testComponent.configurationRepository
+    private val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor
+        get() = testComponent.sharedNotificationContainerInteractor
+    private val underTest: SharedNotificationContainerViewModel
+        get() = testComponent.underTest
+    private val keyguardInteractor: KeyguardInteractor
+        get() = testComponent.keyguardInteractor
+    private val keyguardTransitionRepository
+        get() = testComponent.keyguardTransitionRepository
+    private val testScope
+        get() = testComponent.testScope
 
     @Mock private lateinit var notificationStackSizeCalculator: NotificationStackSizeCalculator
-    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
-    @Mock private lateinit var userInteractor: UserInteractor
     @Mock
     private lateinit var notificationStackScrollLayoutController:
         NotificationStackScrollLayoutController
@@ -94,43 +91,21 @@
         whenever(notificationStackScrollLayoutController.getView()).thenReturn(mock())
         whenever(notificationStackScrollLayoutController.getShelfHeight()).thenReturn(0)
 
-        configurationRepository = FakeConfigurationRepository()
-        KeyguardTransitionInteractorFactory.create(
-                scope = testScope.backgroundScope,
-            )
-            .also {
-                keyguardInteractor = it.keyguardInteractor
-                keyguardTransitionInteractor = it.keyguardTransitionInteractor
-                keyguardTransitionRepository = it.repository
-            }
-        sharedNotificationContainerInteractor =
-            SharedNotificationContainerInteractor(
-                configurationRepository,
-                mContext,
-                ResourcesSplitShadeStateController()
-            )
-        shadeInteractor =
-            ShadeInteractor(
-                testScope.backgroundScope,
-                disableFlagsRepository,
-                sceneContainerFlags,
-                { sceneInteractor },
-                keyguardRepository,
-                userSetupRepository,
-                deviceProvisionedController,
-                userInteractor,
-                sharedNotificationContainerInteractor,
-                shadeRepository,
-            )
-        underTest =
-            SharedNotificationContainerViewModel(
-                sharedNotificationContainerInteractor,
-                keyguardInteractor,
-                keyguardTransitionInteractor,
-                notificationStackSizeCalculator,
-                notificationStackScrollLayoutController,
-                shadeInteractor
-            )
+        testComponent =
+            DaggerSharedNotificationContainerViewModelTest_TestComponent.factory()
+                .create(
+                    test = this,
+                    featureFlags =
+                        FakeFeatureFlagsClassicModule {
+                            set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+                        },
+                    mocks =
+                        TestMocksModule(
+                            notificationStackSizeCalculator = notificationStackSizeCalculator,
+                            notificationStackScrollLayoutController =
+                                notificationStackScrollLayoutController,
+                        )
+                )
     }
 
     @Test
@@ -404,4 +379,34 @@
             )
         )
     }
+
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent {
+
+        val underTest: SharedNotificationContainerViewModel
+
+        val configurationRepository: FakeConfigurationRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val keyguardInteractor: KeyguardInteractor
+        val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+        val shadeRepository: FakeShadeRepository
+        val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor
+        val testScope: TestScope
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index a5d3484..e7dad6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 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.statusbar.policy.RemoteInputQuickSettingsDisabler;
 
@@ -85,7 +86,7 @@
     private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private KeyguardStateController mKeyguardStateController;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
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 e33fa22..f18af61 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
@@ -85,7 +85,6 @@
 import com.android.keyguard.TestScopeProvider;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.InitController;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -117,6 +116,7 @@
 import com.android.systemui.plugins.PluginManager;
 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.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -219,7 +219,7 @@
     @Mock private KeyguardIndicationController mKeyguardIndicationController;
     @Mock private NotificationStackScrollLayout mStackScroller;
     @Mock private NotificationStackScrollLayoutController mStackScrollerController;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private ShadeLogger mShadeLogger;
     @Mock private NotificationPanelView mNotificationPanelView;
@@ -336,6 +336,7 @@
         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.
@@ -343,6 +344,7 @@
         mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
         // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
+        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
 
         IThermalService thermalService = mock(IThermalService.class);
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 ff6f40d5..593c587 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
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -72,7 +73,7 @@
 
     private DozeServiceHost mDozeServiceHost;
 
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private ScrimController mScrimController;
     @Mock private DozeScrimController mDozeScrimController;
     @Mock private StatusBarStateControllerImpl mStatusBarStateController;
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 ec6286b..d84bb72 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
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Assert;
@@ -72,7 +73,7 @@
     private ExpandableNotificationRow mRow;
     private NotificationEntry mEntry;
     private HeadsUpStatusBarView mHeadsUpStatusBarView;
-    private HeadsUpManagerPhone mHeadsUpManager;
+    private HeadsUpManager mHeadsUpManager;
     private View mOperatorNameView;
     private StatusBarStateController mStatusbarStateController;
     private PhoneStatusBarTransitions mPhoneStatusBarTransitions;
@@ -93,7 +94,7 @@
         mEntry = mRow.getEntry();
         mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
                 mock(TextView.class));
-        mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+        mHeadsUpManager = mock(HeadsUpManager.class);
         mOperatorNameView = new View(mContext);
         mStatusbarStateController = mock(StatusBarStateController.class);
         mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class);
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 1bc522d..cda2a74 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
@@ -32,8 +32,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 
 import org.junit.After;
@@ -71,7 +72,6 @@
     @Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
     @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
     @Mock private UiEventLogger mUiEventLogger;
-    private boolean mLivesPastNormalTime;
 
     private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
         TestableHeadsUpManagerPhone(
@@ -149,7 +149,7 @@
 
     @Test
     public void testSnooze() {
-        final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+        final HeadsUpManager hmp = createHeadsUpManagerPhone();
         final NotificationEntry entry = createEntry(/* id = */ 0);
 
         hmp.showNotification(entry);
@@ -160,7 +160,7 @@
 
     @Test
     public void testSwipedOutNotification() {
-        final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+        final HeadsUpManager hmp = createHeadsUpManagerPhone();
         final NotificationEntry entry = createEntry(/* id = */ 0);
 
         hmp.showNotification(entry);
@@ -176,7 +176,7 @@
 
     @Test
     public void testCanRemoveImmediately_swipedOut() {
-        final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+        final HeadsUpManager hmp = createHeadsUpManagerPhone();
         final NotificationEntry entry = createEntry(/* id = */ 0);
 
         hmp.showNotification(entry);
@@ -189,7 +189,7 @@
     @Ignore("b/141538055")
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
-        final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+        final HeadsUpManager hmp = createHeadsUpManagerPhone();
         final NotificationEntry earlierEntry = createEntry(/* id = */ 0);
         final NotificationEntry laterEntry = createEntry(/* id = */ 1);
         laterEntry.setRow(mRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bac8579..b36d09d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -18,6 +18,9 @@
 
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
+
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -33,7 +36,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
 import android.service.trust.TrustAgentService;
 import android.testing.AndroidTestingRunner;
@@ -175,6 +177,7 @@
         mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
         mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, true);
         mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
+        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
 
         when(mNotificationShadeWindowController.getWindowRootView())
                 .thenReturn(mNotificationShadeWindowView);
@@ -761,6 +764,30 @@
     }
 
     @Test
+    public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() {
+        mStatusBarKeyguardViewManager.addCallback(mCallback);
+
+        // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
+        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // THEN the touch is not acted upon
+        verify(mCallback, never()).onTouch(any());
+    }
+
+    @Test
+    public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
+        // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
+        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // THEN the touch is not intercepted
+        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
+                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+        ));
+    }
+
+    @Test
     public void handleDispatchTouchEvent_alternateBouncerNotVisible() {
         mStatusBarKeyguardViewManager.addCallback(mCallback);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 8013e5e..beac995 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -92,6 +92,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -218,7 +219,7 @@
                 mScreenOffAnimationController,
                 mStatusBarStateController).getPowerInteractor();
 
-        HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class);
+        HeadsUpManager headsUpManager = mock(HeadsUpManager.class);
         NotificationLaunchAnimatorControllerProvider notificationAnimationProvider =
                 new NotificationLaunchAnimatorControllerProvider(
                         new NotificationExpansionRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 233f407..ee4f208 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -59,6 +60,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
@@ -106,7 +108,7 @@
                 mContext,
                 shadeViewController,
                 mock(QuickSettingsController.class),
-                mock(HeadsUpManagerPhone.class),
+                mock(HeadsUpManager.class),
                 notificationShadeWindowView,
                 mock(ActivityStarter.class),
                 stackScrollLayoutController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index dbaa29b..d06a6e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -20,7 +20,6 @@
 import android.net.wifi.WifiManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
@@ -53,7 +52,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class WifiRepositorySwitcherTest : SysuiTestCase() {
     private lateinit var underTest: WifiRepositorySwitcher
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
index 206ac1d..ce00250 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -28,7 +27,6 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class DisabledWifiRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index c2e75aa..cf20ba8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -35,7 +35,6 @@
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.log.table.TableLogBuffer
@@ -73,7 +72,6 @@
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class WifiRepositoryImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
index afab623..c2f5665 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
@@ -105,7 +105,7 @@
     fun setUp() {
         featureFlags.set(Flags.INSTANT_TETHER, false)
         featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, false)
-        whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor)))
+        whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor), any()))
             .thenReturn(wifiPickerTracker)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 1db8065..7fbbfc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -19,7 +19,6 @@
 import android.net.wifi.WifiManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -43,7 +42,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class WifiInteractorImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 49a2648..2d1a27f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -19,7 +19,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
-import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.coroutines.collectLastValue
@@ -53,7 +52,6 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RoboPilotTest
 @RunWith(AndroidJUnit4::class)
 class WifiViewModelTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 64ebcd9..4f3f564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -41,10 +41,13 @@
 import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Region;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -73,7 +76,7 @@
     private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
     @Mock private AccessibilityManagerWrapper mAccessibilityMgr;
 
-    private final class TestableHeadsUpManager extends HeadsUpManager {
+    private final class TestableHeadsUpManager extends BaseHeadsUpManager {
         TestableHeadsUpManager(Context context,
                 HeadsUpManagerLogger logger,
                 Handler handler,
@@ -85,9 +88,78 @@
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
             mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME;
         }
+
+        // The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
+        @Override
+        public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addSwipedOutNotification(@NonNull String key) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void extendHeadsUp() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Nullable
+        @Override
+        public Region getTouchableRegion() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isHeadsUpGoingAway() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void onExpandingFinished() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
+                boolean animate) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setRemoteInputActive(@NonNull NotificationEntry entry,
+                boolean remoteInputActive) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setTrackingHeadsUp(boolean tracking) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean shouldSwallowClick(@NonNull String key) {
+            throw new UnsupportedOperationException();
+        }
     }
 
-    private HeadsUpManager createHeadsUpManager() {
+    private BaseHeadsUpManager createHeadsUpManager() {
         return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr,
                 mUiEventLoggerFake);
     }
@@ -165,9 +237,10 @@
 
     @Test
     public void testHunRemovedLogging() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createEntry(/* id = */ 0);
-        final HeadsUpManager.HeadsUpEntry headsUpEntry = mock(HeadsUpManager.HeadsUpEntry.class);
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock(
+                BaseHeadsUpManager.HeadsUpEntry.class);
         headsUpEntry.mEntry = notifEntry;
 
         hum.onAlertEntryRemoved(headsUpEntry);
@@ -177,35 +250,37 @@
 
     @Test
     public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
 
         // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
         hum.showNotification(notifEntry);
 
-        final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
-        headsUpEntry.wasUnpinned = false;
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+                notifEntry.getKey());
+        headsUpEntry.mWasUnpinned = false;
 
         assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry));
     }
 
     @Test
     public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
 
         // Add notifEntry to ANM mAlertEntries map and make it unpinned
         hum.showNotification(notifEntry);
 
-        final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
-        headsUpEntry.wasUnpinned = true;
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+                notifEntry.getKey());
+        headsUpEntry.mWasUnpinned = true;
 
         assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry));
     }
 
     @Test
     public void testShouldHeadsUpBecomePinned_noFSI_false() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
 
         assertFalse(hum.shouldHeadsUpBecomePinned(entry));
@@ -214,7 +289,7 @@
 
     @Test
     public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
         useAccessibilityTimeout(false);
 
@@ -228,7 +303,7 @@
 
     @Test
     public void testShowNotification_autoDismissesWithDefaultTimeout() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
         useAccessibilityTimeout(false);
 
@@ -242,7 +317,7 @@
 
     @Test
     public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
         useAccessibilityTimeout(false);
 
@@ -256,7 +331,7 @@
 
     @Test
     public void testShowNotification_sticky_neverAutoDismisses() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createStickyEntry(/* id = */ 0);
         useAccessibilityTimeout(false);
 
@@ -278,7 +353,7 @@
 
     @Test
     public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
         useAccessibilityTimeout(true);
 
@@ -292,7 +367,7 @@
 
     @Test
     public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
         useAccessibilityTimeout(true);
 
@@ -306,7 +381,7 @@
 
     @Test
     public void testRemoveNotification_beforeMinimumDisplayTime() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
         useAccessibilityTimeout(false);
 
@@ -329,7 +404,7 @@
 
     @Test
     public void testRemoveNotification_afterMinimumDisplayTime() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
         useAccessibilityTimeout(false);
 
@@ -366,7 +441,7 @@
 
     @Test
     public void testRemoveNotification_releaseImmediately() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createEntry(/* id = */ 0);
 
         hum.showNotification(entry);
@@ -382,14 +457,15 @@
 
     @Test
     public void testIsSticky_rowPinnedAndExpanded_true() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createEntry(/* id = */ 0);
         when(mRow.isPinned()).thenReturn(true);
         notifEntry.setRow(mRow);
 
         hum.showNotification(notifEntry);
 
-        final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+                notifEntry.getKey());
         headsUpEntry.setExpanded(true);
 
         assertTrue(hum.isSticky(notifEntry.getKey()));
@@ -397,20 +473,21 @@
 
     @Test
     public void testIsSticky_remoteInputActive_true() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createEntry(/* id = */ 0);
 
         hum.showNotification(notifEntry);
 
-        final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
-        headsUpEntry.remoteInputActive = true;
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+                notifEntry.getKey());
+        headsUpEntry.mRemoteInputActive = true;
 
         assertTrue(hum.isSticky(notifEntry.getKey()));
     }
 
     @Test
     public void testIsSticky_hasFullScreenIntent_true() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
 
         hum.showNotification(notifEntry);
@@ -421,7 +498,7 @@
 
     @Test
     public void testIsSticky_stickyForSomeTime_false() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
 
         hum.showNotification(entry);
@@ -432,21 +509,22 @@
 
     @Test
     public void testIsSticky_false() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createEntry(/* id = */ 0);
 
         hum.showNotification(notifEntry);
 
-        final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+                notifEntry.getKey());
         headsUpEntry.setExpanded(false);
-        headsUpEntry.remoteInputActive = false;
+        headsUpEntry.mRemoteInputActive = false;
 
         assertFalse(hum.isSticky(notifEntry.getKey()));
     }
 
     @Test
     public void testCompareTo_withNullEntries() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
 
         hum.showNotification(alertEntry);
@@ -458,7 +536,7 @@
 
     @Test
     public void testCompareTo_withNonAlertEntries() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
 
         final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag(
                 "nae1").build();
@@ -474,9 +552,9 @@
 
     @Test
     public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
 
-        final HeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry();
+        final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry();
         ongoingCall.setEntry(new NotificationEntryBuilder()
                 .setSbn(createSbn(/* id = */ 0,
                         new Notification.Builder(mContext, "")
@@ -484,9 +562,9 @@
                                 .setOngoing(true)))
                 .build());
 
-        final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
+        final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
         activeRemoteInput.setEntry(createEntry(/* id = */ 1));
-        activeRemoteInput.remoteInputActive = true;
+        activeRemoteInput.mRemoteInputActive = true;
 
         assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
         assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
@@ -494,9 +572,9 @@
 
     @Test
     public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
 
-        final HeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry();
+        final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry();
         final Person person = new Person.Builder().setName("person").build();
         final PendingIntent intent = mock(PendingIntent.class);
         incomingCall.setEntry(new NotificationEntryBuilder()
@@ -506,9 +584,9 @@
                                         .forIncomingCall(person, intent, intent))))
                 .build());
 
-        final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
+        final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
         activeRemoteInput.setEntry(createEntry(/* id = */ 1));
-        activeRemoteInput.remoteInputActive = true;
+        activeRemoteInput.mRemoteInputActive = true;
 
         assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0);
         assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0);
@@ -516,10 +594,10 @@
 
     @Test
     public void testPinEntry_logsPeek() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
 
         // Needs full screen intent in order to be pinned
-        final HeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
+        final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
         entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0));
 
         // Note: the standard way to show a notification would be calling showNotification rather
@@ -530,13 +608,13 @@
         hum.onAlertEntryAdded(entryToPin);
 
         assertEquals(1, mUiEventLoggerFake.numLogs());
-        assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
+        assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
                 mUiEventLoggerFake.eventId(0));
     }
 
     @Test
     public void testSetUserActionMayIndirectlyRemove() {
-        final HeadsUpManager hum = createHeadsUpManager();
+        final BaseHeadsUpManager hum = createHeadsUpManager();
         final NotificationEntry notifEntry = createEntry(/* id = */ 0);
 
         hum.showNotification(notifEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
new file mode 100644
index 0000000..12694ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.policy.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProvisioningRepositoryImplTest : SysuiTestCase() {
+
+    @Mock lateinit var deviceProvisionedController: DeviceProvisionedController
+
+    lateinit var underTest: DeviceProvisioningRepositoryImpl
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            DeviceProvisioningRepositoryImpl(
+                deviceProvisionedController,
+            )
+    }
+
+    @Test
+    fun isDeviceProvisioned_reflectsCurrentControllerState() = runTest {
+        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+        val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+        assertThat(deviceProvisioned).isTrue()
+    }
+
+    @Test
+    fun isDeviceProvisioned_updatesWhenControllerStateChanges_toTrue() = runTest {
+        val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+        runCurrent()
+        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+        withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+            .onDeviceProvisionedChanged()
+        assertThat(deviceProvisioned).isTrue()
+    }
+
+    @Test
+    fun isDeviceProvisioned_updatesWhenControllerStateChanges_toFalse() = runTest {
+        val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+        runCurrent()
+        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+        withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+            .onDeviceProvisionedChanged()
+        assertThat(deviceProvisioned).isFalse()
+    }
+
+    @Test
+    fun isFrpActive_reflectsCurrentControllerState() = runTest {
+        whenever(deviceProvisionedController.isFrpActive).thenReturn(true)
+        val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+        assertThat(frpActive).isTrue()
+    }
+
+    @Test
+    fun isFrpActive_updatesWhenControllerStateChanges_toTrue() = runTest {
+        val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+        runCurrent()
+        whenever(deviceProvisionedController.isFrpActive).thenReturn(true)
+        withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+            .onFrpActiveChanged()
+        assertThat(frpActive).isTrue()
+    }
+
+    @Test
+    fun isFrpActive_updatesWhenControllerStateChanges_toFalse() = runTest {
+        val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+        runCurrent()
+        whenever(deviceProvisionedController.isFrpActive).thenReturn(false)
+        withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+            .onFrpActiveChanged()
+        assertThat(frpActive).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index af941d0..c56266d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -155,6 +155,9 @@
 
     @Test
     fun createUserInteractor_nonProcessUser_startsSecondaryService() {
+        val userId = Process.myUserHandle().identifier + 1
+        whenever(manager.aliveUsers).thenReturn(listOf(createUserInfo(userId, "abc")))
+
         createUserInteractor(false /* startAsProcessUser */)
         verify(spyContext).startServiceAsUser(any(), any())
     }
@@ -655,9 +658,10 @@
 
     @Test
     fun userSwitchedBroadcast() {
-        createUserInteractor()
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
+            whenever(manager.aliveUsers).thenReturn(userInfos)
+            createUserInteractor()
             userRepository.setUserInfos(userInfos)
             userRepository.setSelectedUserInfo(userInfos[0])
             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
@@ -728,6 +732,26 @@
     }
 
     @Test
+    fun localeChanged_refreshUsers() {
+        createUserInteractor()
+        testScope.runTest {
+            val userInfos = createUserInfos(count = 2, includeGuest = false)
+            userRepository.setUserInfos(userInfos)
+            userRepository.setSelectedUserInfo(userInfos[0])
+            runCurrent()
+            val refreshUsersCallCount = userRepository.refreshUsersCallCount
+
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                spyContext,
+                Intent(Intent.ACTION_LOCALE_CHANGED)
+            )
+            runCurrent()
+
+            assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
+        }
+    }
+
+    @Test
     fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() {
         createUserInteractor()
         testScope.runTest {
@@ -985,6 +1009,13 @@
         }
     }
 
+    @Test
+    fun initWithNoAliveUsers() {
+        whenever(manager.aliveUsers).thenReturn(listOf())
+        createUserInteractor()
+        verify(spyContext, never()).startServiceAsUser(any(), any())
+    }
+
     private fun assertUsers(
         models: List<UserModel>?,
         count: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 6932f5e..c236b12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.GuestResumeSessionReceiver
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Text
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -347,6 +348,24 @@
         }
 
     @Test
+    fun isFinishRequested_finishesWhenUserButtonIsClicked() =
+        testScope.runTest {
+            setUsers(count = 2)
+            val isFinishRequested = mutableListOf<Boolean>()
+            val job =
+                launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
+
+            val userViewModels = collectLastValue(underTest.users)
+            assertThat(isFinishRequested.last()).isFalse()
+
+            userViewModels.invoke()?.firstOrNull()?.onClicked?.invoke()
+
+            assertThat(isFinishRequested.last()).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
     fun guestSelected_nameIsExitGuest() =
         testScope.runTest {
             val userInfos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
new file mode 100644
index 0000000..aaf8d07
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.ui
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AnimatedValueTest : SysuiTestCase() {
+
+    @Test
+    fun animatableEvent_updatesValue() = runTest {
+        val events = MutableSharedFlow<AnimatableEvent<Int>>()
+        val values = events.toAnimatedValueFlow(completionEvents = emptyFlow())
+        val value by collectLastValue(values)
+        runCurrent()
+
+        events.emit(AnimatableEvent(value = 1, startAnimating = false))
+
+        assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false))
+    }
+
+    @Test
+    fun animatableEvent_startAnimation() = runTest {
+        val events = MutableSharedFlow<AnimatableEvent<Int>>()
+        val values = events.toAnimatedValueFlow(completionEvents = emptyFlow())
+        val value by collectLastValue(values)
+        runCurrent()
+
+        events.emit(AnimatableEvent(value = 1, startAnimating = true))
+
+        assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = true))
+    }
+
+    @Test
+    fun animatableEvent_startAnimation_alreadyAnimating() = runTest {
+        val events = MutableSharedFlow<AnimatableEvent<Int>>()
+        val values = events.toAnimatedValueFlow(completionEvents = emptyFlow())
+        val value by collectLastValue(values)
+        runCurrent()
+
+        events.emit(AnimatableEvent(value = 1, startAnimating = true))
+        events.emit(AnimatableEvent(value = 2, startAnimating = true))
+
+        assertThat(value).isEqualTo(AnimatedValue(value = 2, isAnimating = true))
+    }
+
+    @Test
+    fun animatedValue_stopAnimating() = runTest {
+        val events = MutableSharedFlow<AnimatableEvent<Int>>()
+        val stopEvent = MutableSharedFlow<Unit>()
+        val values = events.toAnimatedValueFlow(completionEvents = stopEvent)
+        val value by collectLastValue(values)
+        runCurrent()
+
+        events.emit(AnimatableEvent(value = 1, startAnimating = true))
+        stopEvent.emit(Unit)
+
+        assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false))
+    }
+
+    @Test
+    fun animatedValue_stopAnimating_notAnimating() = runTest {
+        val events = MutableSharedFlow<AnimatableEvent<Int>>()
+        val stopEvent = MutableSharedFlow<Unit>()
+        val values = events.toAnimatedValueFlow(completionEvents = stopEvent)
+        values.launchIn(backgroundScope)
+        runCurrent()
+
+        events.emit(AnimatableEvent(value = 1, startAnimating = false))
+
+        assertThat(stopEvent.subscriptionCount.value).isEqualTo(0)
+    }
+}
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 409ba48..c832702 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -86,20 +86,36 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardSecurityModel;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+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;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+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.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.data.repository.FakePowerRepository;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.scene.FakeWindowRootViewComponent;
 import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.data.repository.SceneContainerRepository;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.logger.SceneLogger;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
@@ -139,6 +155,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.user.domain.interactor.UserInteractor;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
@@ -329,6 +346,8 @@
     private UserHandle mUser0;
 
     private FakeBubbleProperties mBubbleProperties;
+    private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+    private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
 
     @Before
     public void setUp() throws Exception {
@@ -350,21 +369,94 @@
         when(mNotificationShadeWindowView.getViewTreeObserver())
                 .thenReturn(mock(ViewTreeObserver.class));
 
-        mShadeInteractor = new ShadeInteractor(
+
+        FakeDeviceProvisioningRepository deviceProvisioningRepository =
+                new FakeDeviceProvisioningRepository();
+        deviceProvisioningRepository.setDeviceProvisioned(true);
+        FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
+        FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
+        FakeShadeRepository shadeRepository = new FakeShadeRepository();
+        FakePowerRepository powerRepository = new FakePowerRepository();
+        FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
+
+        PowerInteractor powerInteractor = new PowerInteractor(
+                powerRepository,
+                new FalsingCollectorFake(),
+                mock(ScreenOffAnimationController.class),
+                mStatusBarStateController);
+
+        SceneInteractor sceneInteractor = new SceneInteractor(
                 mTestScope.getBackgroundScope(),
-                new FakeDisableFlagsRepository(),
-                new FakeSceneContainerFlags(),
-                mUtils::sceneInteractor,
-                new FakeKeyguardRepository(),
-                new FakeUserSetupRepository(),
-                mock(DeviceProvisionedController.class),
-                mock(UserInteractor.class),
-                new SharedNotificationContainerInteractor(
-                        new FakeConfigurationRepository(),
-                        mContext,
-                        new ResourcesSplitShadeStateController()),
-                new FakeShadeRepository()
-        );
+                new SceneContainerRepository(
+                        mTestScope.getBackgroundScope(),
+                        mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())),
+                powerRepository,
+                mock(SceneLogger.class));
+
+        FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
+        KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
+                keyguardRepository,
+                new FakeCommandQueue(),
+                powerInteractor,
+                featureFlags,
+                sceneContainerFlags,
+                new FakeDeviceEntryRepository(),
+                new FakeKeyguardBouncerRepository(),
+                configurationRepository,
+                shadeRepository,
+                () -> sceneInteractor);
+
+        FakeKeyguardTransitionRepository keyguardTransitionRepository =
+                new FakeKeyguardTransitionRepository();
+
+        KeyguardTransitionInteractor keyguardTransitionInteractor =
+                new KeyguardTransitionInteractor(
+                        mTestScope.getBackgroundScope(),
+                        keyguardTransitionRepository,
+                        () -> keyguardInteractor,
+                        () -> mFromLockscreenTransitionInteractor,
+                        () -> mFromPrimaryBouncerTransitionInteractor);
+
+        mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                mTestScope.getBackgroundScope(),
+                keyguardInteractor,
+                featureFlags,
+                shadeRepository,
+                powerInteractor);
+
+        mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
+                keyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                mTestScope.getBackgroundScope(),
+                keyguardInteractor,
+                featureFlags,
+                mock(KeyguardSecurityModel.class),
+                powerInteractor);
+
+        ResourcesSplitShadeStateController splitShadeStateController =
+                new ResourcesSplitShadeStateController();
+
+        mShadeInteractor =
+                new ShadeInteractor(
+                        mTestScope.getBackgroundScope(),
+                        deviceProvisioningRepository,
+                        new FakeDisableFlagsRepository(),
+                        mDozeParameters,
+                        sceneContainerFlags,
+                        () -> sceneInteractor,
+                        keyguardRepository,
+                        keyguardTransitionInteractor,
+                        powerInteractor,
+                        new FakeUserSetupRepository(),
+                        mock(UserInteractor.class),
+                        new SharedNotificationContainerInteractor(
+                                configurationRepository,
+                                mContext,
+                                splitShadeStateController),
+                        new FakeShadeRepository()
+                );
 
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
                 mContext,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt
index 0e59496..dc5fd95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt
@@ -15,21 +15,29 @@
  */
 package com.android.systemui
 
+import com.android.systemui.classifier.FakeClassifierModule
 import com.android.systemui.data.FakeSystemUiDataLayerModule
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.log.FakeUiEventLoggerModule
 import com.android.systemui.scene.FakeSceneModule
 import com.android.systemui.settings.FakeSettingsModule
+import com.android.systemui.statusbar.policy.FakeConfigurationControllerModule
+import com.android.systemui.statusbar.policy.FakeSplitShadeStateControllerModule
 import com.android.systemui.util.concurrency.FakeExecutorModule
+import com.android.systemui.util.time.FakeSystemClockModule
 import dagger.Module
 
 @Module(
     includes =
         [
+            FakeClassifierModule::class,
+            FakeConfigurationControllerModule::class,
             FakeExecutorModule::class,
             FakeFeatureFlagsClassicModule::class,
-            FakeSettingsModule::class,
             FakeSceneModule::class,
+            FakeSettingsModule::class,
+            FakeSplitShadeStateControllerModule::class,
+            FakeSystemClockModule::class,
             FakeSystemUiDataLayerModule::class,
             FakeUiEventLoggerModule::class,
         ]
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
deleted file mode 100644
index 3fff136..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
+++ /dev/null
@@ -1,31 +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;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results
- * that runs on CI
- */
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface RoboPilotTest {
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt
new file mode 100644
index 0000000..8fa6695
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.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.authentication.data
+
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeAuthenticationRepositoryModule::class])
+object FakeAuthenticationDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 4fc3e3f..ddfe79a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -24,10 +24,17 @@
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
 import com.android.systemui.authentication.shared.model.AuthenticationResultModel
 import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.currentTime
 
 class FakeAuthenticationRepository(
     private val deviceEntryRepository: FakeDeviceEntryRepository,
@@ -201,3 +208,19 @@
         }
     }
 }
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@Module(includes = [FakeAuthenticationRepositoryModule.Bindings::class])
+object FakeAuthenticationRepositoryModule {
+    @Provides
+    @SysUISingleton
+    fun provideFake(
+        deviceEntryRepository: FakeDeviceEntryRepository,
+        scope: TestScope,
+    ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime })
+
+    @Module
+    interface Bindings {
+        @Binds fun bindFake(fake: FakeAuthenticationRepository): AuthenticationRepository
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt
new file mode 100644
index 0000000..23bad39
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.classifier
+
+import dagger.Module
+
+@Module(includes = [FakeFalsingCollectorModule::class, FakeFalsingManagerModule::class])
+object FakeClassifierModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt
new file mode 100644
index 0000000..92acc94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.classifier
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface FakeFalsingCollectorModule {
+    @Binds @FalsingCollectorActual fun bindFake(fake: FalsingCollectorFake): FalsingCollector
+    @Binds fun bindFakeLegacy(fake: FalsingCollectorFake): FalsingCollector
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt
new file mode 100644
index 0000000..554fc75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.classifier
+
+import com.android.systemui.plugins.FalsingManager
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface FakeFalsingManagerModule {
+    @Binds fun bindFake(fake: FalsingManagerFake): FalsingManager
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
index d47e88f..5038285 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -21,15 +21,19 @@
 import android.net.Uri;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.FalsingManager;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+
 /**
  * Simple Fake for testing where {@link FalsingManager} is required.
  */
+@SysUISingleton
 public class FalsingManagerFake implements FalsingManager {
     private boolean mIsFalseTouch;
     private boolean mIsSimpleTap;
@@ -46,6 +50,10 @@
     private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
     private final List<FalsingTapListener> mTapListeners = new ArrayList<>();
 
+    @Inject
+    public FalsingManagerFake() {
+    }
+
     @Override
     public void onSuccessfulUnlock() {
 
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 f866932..cffbf02 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,8 +15,10 @@
  */
 package com.android.systemui.data
 
+import com.android.systemui.authentication.data.FakeAuthenticationDataLayerModule
 import com.android.systemui.bouncer.data.repository.FakeBouncerDataLayerModule
 import com.android.systemui.common.ui.data.FakeCommonDataLayerModule
+import com.android.systemui.deviceentry.data.FakeDeviceEntryDataLayerModule
 import com.android.systemui.keyguard.data.FakeKeyguardDataLayerModule
 import com.android.systemui.power.data.FakePowerDataLayerModule
 import com.android.systemui.shade.data.repository.FakeShadeDataLayerModule
@@ -28,8 +30,10 @@
 @Module(
     includes =
         [
-            FakeCommonDataLayerModule::class,
+            FakeAuthenticationDataLayerModule::class,
             FakeBouncerDataLayerModule::class,
+            FakeCommonDataLayerModule::class,
+            FakeDeviceEntryDataLayerModule::class,
             FakeKeyguardDataLayerModule::class,
             FakePowerDataLayerModule::class,
             FakeShadeDataLayerModule::class,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
new file mode 100644
index 0000000..ef02bdd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.deviceentry.data
+
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt
new file mode 100644
index 0000000..f4feee1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.deviceentry.data.repository
+
+import dagger.Module
+
+@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index 5e60a09..f029348 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -1,22 +1,40 @@
+/*
+ * 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.deviceentry.data.repository
 
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
 /** Fake implementation of [DeviceEntryRepository] */
-class FakeDeviceEntryRepository : DeviceEntryRepository {
+@SysUISingleton
+class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
 
     private var isInsecureLockscreenEnabled = true
-    private var isBypassEnabled = false
+
+    private val _isBypassEnabled = MutableStateFlow(false)
+    override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled
 
     private val _isUnlocked = MutableStateFlow(false)
     override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
 
-    override fun isBypassEnabled(): Boolean {
-        return isBypassEnabled
-    }
-
     override suspend fun isInsecureLockscreenEnabled(): Boolean {
         return isInsecureLockscreenEnabled
     }
@@ -30,6 +48,11 @@
     }
 
     fun setBypassEnabled(isBypassEnabled: Boolean) {
-        this.isBypassEnabled = isBypassEnabled
+        _isBypassEnabled.value = isBypassEnabled
     }
 }
+
+@Module
+interface FakeDeviceEntryRepositoryModule {
+    @Binds fun bindFake(fake: FakeDeviceEntryRepository): DeviceEntryRepository
+}
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 766f748..a4881bc 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
@@ -80,7 +80,11 @@
 ) {
     val testDispatcher = StandardTestDispatcher()
     val testScope = TestScope(testDispatcher)
-    val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
+    val featureFlags =
+        FakeFeatureFlagsClassic().apply {
+            set(Flags.FACE_AUTH_REFACTOR, false)
+            set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+        }
     val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true }
     val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() }
     val authenticationRepository: FakeAuthenticationRepository by lazy {
@@ -205,7 +209,7 @@
         return BouncerInteractor(
             applicationScope = applicationScope(),
             applicationContext = context,
-            repository = BouncerRepository(),
+            repository = BouncerRepository(featureFlags),
             deviceEntryInteractor = deviceEntryInteractor,
             authenticationInteractor = authenticationInteractor,
             sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
index 1bec82b..e59f642 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
@@ -16,14 +16,18 @@
 package com.android.systemui.statusbar.data
 
 import com.android.systemui.statusbar.disableflags.data.FakeStatusBarDisableFlagsDataLayerModule
+import com.android.systemui.statusbar.notification.data.FakeStatusBarNotificationsDataLayerModule
 import com.android.systemui.statusbar.pipeline.data.FakeStatusBarPipelineDataLayerModule
+import com.android.systemui.statusbar.policy.data.FakeStatusBarPolicyDataLayerModule
 import dagger.Module
 
 @Module(
     includes =
         [
             FakeStatusBarDisableFlagsDataLayerModule::class,
+            FakeStatusBarNotificationsDataLayerModule::class,
             FakeStatusBarPipelineDataLayerModule::class,
+            FakeStatusBarPolicyDataLayerModule::class,
         ]
 )
 object FakeStatusBarDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt
new file mode 100644
index 0000000..788e3aa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.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.statusbar.notification.data
+
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardStateRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeNotificationsKeyguardStateRepositoryModule::class])
+object FakeStatusBarNotificationsDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt
new file mode 100644
index 0000000..5d3cb4d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.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.statusbar.notification.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class FakeNotificationsKeyguardViewStateRepository @Inject constructor() :
+    NotificationsKeyguardViewStateRepository {
+    private val _notificationsFullyHidden = MutableStateFlow(false)
+    override val areNotificationsFullyHidden: Flow<Boolean> = _notificationsFullyHidden
+
+    private val _isPulseExpanding = MutableStateFlow(false)
+    override val isPulseExpanding: Flow<Boolean> = _isPulseExpanding
+
+    fun setNotificationsFullyHidden(fullyHidden: Boolean) {
+        _notificationsFullyHidden.value = fullyHidden
+    }
+
+    fun setPulseExpanding(expanding: Boolean) {
+        _isPulseExpanding.value = expanding
+    }
+}
+
+@Module
+interface FakeNotificationsKeyguardStateRepositoryModule {
+    @Binds
+    fun bindFake(
+        fake: FakeNotificationsKeyguardViewStateRepository
+    ): NotificationsKeyguardViewStateRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index 16a3268..23477d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -1,9 +1,14 @@
 package com.android.systemui.statusbar.policy
 
 import android.content.res.Configuration
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 
 /** Fake implementation of [ConfigurationController] for tests. */
-class FakeConfigurationController : ConfigurationController {
+@SysUISingleton
+class FakeConfigurationController @Inject constructor() : ConfigurationController {
 
     private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>()
 
@@ -33,3 +38,8 @@
 
     override fun isLayoutRtl(): Boolean = false
 }
+
+@Module
+interface FakeConfigurationControllerModule {
+    @Binds fun bindFake(fake: FakeConfigurationController): ConfigurationController
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt
new file mode 100644
index 0000000..14bab84
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.policy
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface FakeSplitShadeStateControllerModule {
+    @Binds fun bindFake(fake: ResourcesSplitShadeStateController): SplitShadeStateController
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
new file mode 100644
index 0000000..5aece1b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.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.statusbar.policy.data
+
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeDeviceProvisioningRepositoryModule::class])
+object FakeStatusBarPolicyDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt
new file mode 100644
index 0000000..3002299
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.policy.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class FakeDeviceProvisioningRepository @Inject constructor() : DeviceProvisioningRepository {
+    private val _isDeviceProvisioned = MutableStateFlow(false)
+    override val isDeviceProvisioned: Flow<Boolean> = _isDeviceProvisioned
+    private val _isFactoryResetProtectionActive = MutableStateFlow(false)
+    override val isFactoryResetProtectionActive: Flow<Boolean> = _isFactoryResetProtectionActive
+    fun setDeviceProvisioned(isProvisioned: Boolean) {
+        _isDeviceProvisioned.value = isProvisioned
+    }
+    fun setFactoryResetProtectionActive(isActive: Boolean) {
+        _isFactoryResetProtectionActive.value = isActive
+    }
+}
+
+@Module
+interface FakeDeviceProvisioningRepositoryModule {
+    @Binds fun bindFake(fake: FakeDeviceProvisioningRepository): DeviceProvisioningRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
index 5de05c2..1f48d94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.util.concurrency
 
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.util.time.FakeSystemClock
 import dagger.Binds
@@ -22,14 +23,12 @@
 import dagger.Provides
 import java.util.concurrent.Executor
 
-@Module(includes = [FakeExecutorModule.Bindings::class])
-class FakeExecutorModule(
-    @get:Provides val clock: FakeSystemClock = FakeSystemClock(),
-) {
-    @get:Provides val executor = FakeExecutor(clock)
+@Module
+interface FakeExecutorModule {
+    @Binds @Main @SysUISingleton fun bindMainExecutor(executor: FakeExecutor): Executor
 
-    @Module
-    interface Bindings {
-        @Binds @Main fun bindMainExecutor(executor: FakeExecutor): Executor
+    companion object {
+        @Provides
+        fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock)
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt
new file mode 100644
index 0000000..3e3d7cb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.time
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface FakeSystemClockModule {
+    @Binds fun bindFake(fake: FakeSystemClock): SystemClock
+
+    companion object {
+        @Provides @SysUISingleton fun providesFake() = FakeSystemClock()
+    }
+}
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index 155dc1a..18f78314 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -49,7 +49,7 @@
         "androidx.test.core",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     resource_dirs: ["test/res"],
     certificate: "platform",
diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp
index b781602..0244c0f 100644
--- a/packages/overlays/tests/Android.bp
+++ b/packages/overlays/tests/Android.bp
@@ -34,7 +34,7 @@
         "androidx.test.rules",
         "androidx.test.espresso.core",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     dxflags: ["--multi-dex"],
 }
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 11189cf..6fa9c08 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -34,3 +34,10 @@
     description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock"
     bug: "297972548"
 }
+
+flag {
+    name: "pinch_zoom_zero_min_span"
+    namespace: "accessibility"
+    description: "Whether to set min span of ScaleGestureDetector to zero."
+    bug: "295327792"
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index c5495d9..9455628 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -28,8 +28,10 @@
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.ScaleGestureDetector;
+import android.view.ViewConfiguration;
 
 import com.android.internal.R;
+import com.android.server.accessibility.Flags;
 
 /**
  * Handles the behavior while receiving scaling and panning gestures if it's enabled.
@@ -70,7 +72,13 @@
         mMaxScale = maxScale;
         mMinScale = minScale;
         mBlockScroll = blockScroll;
-        mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
+        if (Flags.pinchZoomZeroMinSpan()) {
+            mScaleGestureDetector = new ScaleGestureDetector(context,
+                    ViewConfiguration.get(context).getScaledTouchSlop() * 2,
+                    /* minSpan= */ 0, Handler.getMain(), this);
+        } else {
+            mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
+        }
         mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
         mScaleGestureDetector.setQuickScaleEnabled(false);
         mMagnificationDelegate = magnificationDelegate;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7e09b5e..258820a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -24,6 +24,7 @@
 
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -1660,8 +1661,21 @@
         synchronized (mLock) {
             ensureGroupStateLoadedLocked(userId);
 
+            final String pkg = componentName.getPackageName();
+            final ProviderId id;
+            if (!mPackageManagerInternal.isSameApp(pkg, callingUid, userId)) {
+                // If the calling process is requesting to pin appwidgets from another process,
+                // check if the calling process has the necessary permission.
+                if (!injectHasAccessWidgetsPermission(Binder.getCallingPid(), callingUid)) {
+                    return false;
+                }
+                id = new ProviderId(mPackageManagerInternal.getPackageUid(
+                        pkg, 0 /* flags */, userId), componentName);
+            } else {
+                id = new ProviderId(callingUid, componentName);
+            }
             // Look for the widget associated with the caller.
-            Provider provider = lookupProviderLocked(new ProviderId(callingUid, componentName));
+            Provider provider = lookupProviderLocked(id);
             if (provider == null || provider.zombie) {
                 return false;
             }
@@ -1675,6 +1689,14 @@
                 .requestPinAppWidget(callingPackage, info, extras, resultSender, userId);
     }
 
+    /**
+     * Returns true if the caller has the proper permission to access app widgets.
+     */
+    private boolean injectHasAccessWidgetsPermission(int callingPid, int callingUid) {
+        return mContext.checkPermission(Manifest.permission.CLEAR_APP_USER_DATA,
+                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+    }
+
     @Override
     public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
             int profileId, String packageName) {
@@ -4131,7 +4153,7 @@
             return false;
         }
 
-        @GuardedBy("mLock")
+        @GuardedBy("AppWidgetServiceImpl.mLock")
         public AppWidgetProviderInfo getInfoLocked(Context context) {
             if (!mInfoParsed) {
                 // parse
@@ -4159,18 +4181,18 @@
          * be completely parsed and only contain placeHolder information like
          * {@link AppWidgetProviderInfo#providerInfo}
          */
-        @GuardedBy("mLock")
+        @GuardedBy("AppWidgetServiceImpl.mLock")
         public AppWidgetProviderInfo getPartialInfoLocked() {
             return info;
         }
 
-        @GuardedBy("mLock")
+        @GuardedBy("AppWidgetServiceImpl.mLock")
         public void setPartialInfoLocked(AppWidgetProviderInfo info) {
             this.info = info;
             mInfoParsed = false;
         }
 
-        @GuardedBy("mLock")
+        @GuardedBy("AppWidgetServiceImpl.mLock")
         public void setInfoLocked(AppWidgetProviderInfo info) {
             this.info = info;
             mInfoParsed = true;
diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp
index d43a219..eb23f2f 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -19,19 +19,4 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.autofill-sources"],
     libs: ["services.core"],
-    static_libs: ["autofill_flags_java_lib"],
-}
-
-aconfig_declarations {
-    name: "autofill_flags",
-    package: "android.service.autofill",
-    srcs: [
-        "bugfixes.aconfig",
-        "features.aconfig",
-    ],
-}
-
-java_aconfig_library {
-    name: "autofill_flags_java_lib",
-    aconfig_declarations: "autofill_flags",
 }
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ef23754..123b65c 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -5,4 +5,11 @@
   namespace: "autofill"
   description: "Test flag "
   bug: "297380045"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "relayout"
+  namespace: "autofill"
+  description: "Mitigation for relayout issue"
+  bug: "294330426"
+}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 55130e4..987507f 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -16,9 +16,6 @@
 # ServiceWatcher
 per-file ServiceWatcher.java = sooniln@google.com
 
-# Health
-per-file BatteryService.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
-
 per-file *Accessibility* = file:/services/accessibility/OWNERS
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
@@ -39,7 +36,7 @@
 per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
 per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS
 per-file PinnerService.java = file:/apct-tests/perftests/OWNERS
-per-file RescueParty.java = fdunlap@google.com, shuc@google.com, ancr@google.com, harshitmahajan@google.com
+per-file RescueParty.java = shuc@google.com, ancr@google.com, harshitmahajan@google.com
 per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS
 per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS
 per-file TelephonyRegistry.java = file:/telephony/OWNERS
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 9bab261..93dca2f 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -359,8 +359,10 @@
                     @Override
                     public void onChange(boolean selfChange, Uri uri) {
                         if (userSetupCompleteUri.equals(uri)) {
-                            sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
-                                    true /* force */);
+                            if (mConfiguredToPinHome) {
+                                sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
+                                        true /* force */);
+                            }
                         }
                     }
                 }, UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8cc2665..962f38f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -214,6 +214,9 @@
     // external storage service.
     public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
 
+     /** Extended timeout for the system server watchdog. */
+    private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000;
+
     @GuardedBy("mLock")
     private final Set<Integer> mFuseMountedUser = new ArraySet<>();
 
@@ -1230,6 +1233,8 @@
     private void onUserStopped(int userId) {
         Slog.d(TAG, "onUserStopped " + userId);
 
+        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+                SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
         try {
             mVold.onUserStopped(userId);
             mStoraged.onUserStopped(userId);
@@ -1312,6 +1317,8 @@
                 unlockedUsers.add(userId);
             }
         }
+        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+                SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
         for (Integer userId : unlockedUsers) {
             try {
                 mVold.onUserStopped(userId);
@@ -3600,6 +3607,8 @@
 
         @Override
         public ParcelFileDescriptor open() throws AppFuseMountException {
+            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+                SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
             try {
                 final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
                 mMounted = true;
@@ -3612,6 +3621,8 @@
         @Override
         public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
                 throws AppFuseMountException {
+            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+                SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
             try {
                 return new ParcelFileDescriptor(
                         mVold.openAppFuseFile(uid, mountId, fileId, flags));
@@ -3622,6 +3633,8 @@
 
         @Override
         public void close() throws Exception {
+            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+                SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
             if (mMounted) {
                 mVold.unmountAppFuse(uid, mountId);
                 mMounted = false;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index b05b397..55aa716 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -644,6 +644,16 @@
     }
 
     /**
+     * Sets a one-off timeout for the next run of the watchdog for the monitor thread.
+     *
+     * <p>Simiar to {@link setOneOffTimeoutForCurrentThread} but used for monitors added through
+     * {@link #addMonitor}
+     */
+    public void setOneOffTimeoutForMonitors(int oneOffTimeoutMillis, String reason) {
+        mMonitorChecker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason);
+    }
+
+    /**
      * Pauses Watchdog action for the currently running thread. Useful before executing long running
      * operations that could falsely trigger the watchdog. Each call to this will require a matching
      * call to {@link #resumeWatchingCurrentThread}.
diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
index 378a386..9ba49ce 100644
--- a/services/core/java/com/android/server/am/AnrTimer.java
+++ b/services/core/java/com/android/server/am/AnrTimer.java
@@ -108,6 +108,14 @@
     private static final boolean ENABLE_TRACING = false;
 
     /**
+     * Return true if the feature is enabled.  By default, the value is take from the Flags class
+     * but it can be changed for local testing.
+     */
+    private static boolean anrTimerServiceEnabled() {
+        return Flags.anrTimerServiceEnabled();
+    }
+
+    /**
      * The status of an ANR timer.  TIMER_INVALID status is returned when an error is detected.
      */
     private static final int TIMER_INVALID = 0;
@@ -327,18 +335,33 @@
      */
     @VisibleForTesting
     static class Injector {
-        /**
-         * Return a handler for the given Callback.
-         */
-        Handler getHandler(@NonNull Handler.Callback callback) {
-            return null;
-        };
+        private final Handler mReferenceHandler;
+
+        Injector(@NonNull Handler handler) {
+            mReferenceHandler = handler;
+        }
 
         /**
-         * Return a CpuTracker.
+         * Return a handler for the given Callback, based on the reference handler. The handler
+         * might be mocked, in which case it does not have a valid Looper.  In this case, use the
+         * main Looper.
          */
+        @NonNull
+        Handler getHandler(@NonNull Handler.Callback callback) {
+            Looper looper = mReferenceHandler.getLooper();
+            if (looper == null) looper = Looper.getMainLooper();
+            return new Handler(looper, callback);
+        };
+
+        /** Return a CpuTracker. */
+        @NonNull
         CpuTracker getTracker() {
-            return null;
+            return new CpuTracker();
+        }
+
+        /** Return true if the feature is enabled. */
+        boolean getFeatureEnabled() {
+            return anrTimerServiceEnabled();
         }
     }
 
@@ -375,12 +398,6 @@
         /** The interface to fetch process statistics that might extend an ANR timeout. */
         private final CpuTracker mCpu;
 
-        /** Create a HandlerTimerService based on the input handler. */
-        HandlerTimerService(@NonNull Handler handler) {
-            mHandler = new Handler(handler.getLooper(), this::expires);
-            mCpu = new CpuTracker();
-        }
-
         /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */
         @VisibleForTesting
         HandlerTimerService(@NonNull Injector injector) {
@@ -491,38 +508,56 @@
     private final boolean mLenientCancel = true;
 
     /**
+     * The top-level switch for the feature enabled or disabled.
+     */
+    private final FeatureSwitch mFeature;
+
+    /**
      * The common constructor.  A null injector results in a normal, production timer.
      */
     @VisibleForTesting
     AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend,
-            @Nullable Injector injector) {
+            @NonNull Injector injector) {
         mHandler = handler;
         mWhat = what;
         mLabel = label;
         mExtend = extend;
-        if (injector == null) {
-            mTimerService = new HandlerTimerService(handler);
+        boolean enabled = injector.getFeatureEnabled();
+        if (!enabled) {
+            mFeature = new FeatureDisabled();
+            mTimerService = null;
         } else {
+            mFeature = new FeatureEnabled();
             mTimerService = new HandlerTimerService(injector);
+
+            synchronized (sAnrTimerList) {
+                sAnrTimerList.add(new WeakReference(this));
+            }
         }
-        synchronized (sAnrTimerList) {
-            sAnrTimerList.add(new WeakReference(this));
-        }
-        Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService.toString(), label));
+        Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService, label));
     }
 
     /**
      * Create one timer instance for production.  The client can ask for extensible timeouts.
      */
     AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) {
-        this(handler, what, label, extend, null);
+        this(handler, what, label, extend, new Injector(handler));
     }
 
     /**
      * Create one timer instance for production.  There are no extensible timeouts.
      */
     AnrTimer(@NonNull Handler handler, int what, @NonNull String label) {
-        this(handler, what, label, false, null);
+        this(handler, what, label, false);
+    }
+
+    /**
+     * Return true if the service is enabled on this instance.  Clients should use this method to
+     * decide if the feature is enabled, and not read the flags directly.  This method should be
+     * deleted if and when the feature is enabled permanently.
+     */
+    boolean serviceEnabled() {
+        return mFeature.enabled();
     }
 
     /**
@@ -613,93 +648,186 @@
         Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg));
     }
 
-   /**
-     * Start a timer.
+    /**
+     * The FeatureSwitch class provides a quick switch between feature-enabled behavior and
+     * feature-disabled behavior.
      */
-    boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
-        final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, this);
-        synchronized (mLock) {
-            Timer old = mTimerMap.get(arg);
-            if (old != null) {
-                // There is an existing timer.  This is a protocol error in the client.  Record
-                // the error and then clean up by canceling running timers and discarding expired
-                // timers.
-                restartedLocked(old.status, arg);
-                if (old.status == TIMER_EXPIRED) {
-                    discard(arg);
+    private abstract class FeatureSwitch {
+        abstract boolean start(@NonNull V arg, int pid, int uid, long timeoutMs);
+        abstract boolean cancel(@NonNull V arg);
+        abstract boolean accept(@NonNull V arg);
+        abstract boolean discard(@NonNull V arg);
+        abstract boolean enabled();
+    }
+
+    /**
+     * The FeatureDisabled class bypasses almost all AnrTimer logic.  It is used when the AnrTimer
+     * service is disabled via Flags.anrTimerServiceEnabled.
+     */
+    private class FeatureDisabled extends FeatureSwitch {
+        /** Start a timer by sending a message to the client's handler. */
+        boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+            final Message msg = mHandler.obtainMessage(mWhat, arg);
+            mHandler.sendMessageDelayed(msg, timeoutMs);
+            return true;
+        }
+
+        /** Cancel a timer by removing the message from the client's handler. */
+        boolean cancel(@NonNull V arg) {
+            mHandler.removeMessages(mWhat, arg);
+            return true;
+        }
+
+        /** accept() is a no-op when the feature is disabled. */
+        boolean accept(@NonNull V arg) {
+            return true;
+        }
+
+        /** discard() is a no-op when the feature is disabled. */
+        boolean discard(@NonNull V arg) {
+            return true;
+        }
+
+        /** The feature is not enabled. */
+        boolean enabled() {
+            return false;
+        }
+    }
+
+    /**
+     * The FeatureEnabled class enables the AnrTimer logic.  It is used when the AnrTimer service
+     * is enabled via Flags.anrTimerServiceEnabled.
+     */
+    private class FeatureEnabled extends FeatureSwitch {
+
+        /**
+         * Start a timer.
+         */
+        boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+            final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, AnrTimer.this);
+            synchronized (mLock) {
+                Timer old = mTimerMap.get(arg);
+                if (old != null) {
+                    // There is an existing timer.  This is a protocol error in the client.
+                    // Record the error and then clean up by canceling running timers and
+                    // discarding expired timers.
+                    restartedLocked(old.status, arg);
+                    if (old.status == TIMER_EXPIRED) {
+                        discard(arg);
+                    } else {
+                        cancel(arg);
+                    }
+                }
+                if (mTimerService.start(timer)) {
+                    timer.status = TIMER_RUNNING;
+                    mTimerMap.put(arg, timer);
+                    mTotalStarted++;
+                    mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
+                    if (DEBUG) report(timer, "start");
+                    return true;
                 } else {
-                    cancel(arg);
+                    Log.e(TAG, "AnrTimer.start failed");
+                    return false;
                 }
             }
-            if (mTimerService.start(timer)) {
-                timer.status = TIMER_RUNNING;
-                mTimerMap.put(arg, timer);
-                mTotalStarted++;
-                mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
-                if (DEBUG) report(timer, "start");
+        }
+
+        /**
+         * Cancel a timer.  Return false if the timer was not found.
+         */
+        boolean cancel(@NonNull V arg) {
+            synchronized (mLock) {
+                Timer timer = removeLocked(arg);
+                if (timer == null) {
+                    if (!mLenientCancel) notFoundLocked("cancel", arg);
+                    return false;
+                }
+                mTimerService.cancel(timer);
+                // There may be an expiration message in flight.  Cancel it.
+                mHandler.removeMessages(mWhat, arg);
+                if (DEBUG) report(timer, "cancel");
+                timer.release();
                 return true;
-            } else {
-                Log.e(TAG, "AnrTimer.start failed");
-                return false;
             }
         }
+
+        /**
+         * Accept a timer in the framework-level handler.  The timeout has been accepted and the
+         * timeout handler is executing.  Return false if the timer was not found.
+         */
+        boolean accept(@NonNull V arg) {
+            synchronized (mLock) {
+                Timer timer = removeLocked(arg);
+                if (timer == null) {
+                    notFoundLocked("accept", arg);
+                    return false;
+                }
+                mTimerService.accept(timer);
+                traceEnd(timer);
+                if (DEBUG) report(timer, "accept");
+                timer.release();
+                return true;
+            }
+        }
+
+        /**
+         * Discard a timer in the framework-level handler.  For whatever reason, the timer is no
+         * longer interesting.  No statistics are collected.  Return false if the time was not
+         * found.
+         */
+        boolean discard(@NonNull V arg) {
+            synchronized (mLock) {
+                Timer timer = removeLocked(arg);
+                if (timer == null) {
+                    notFoundLocked("discard", arg);
+                    return false;
+                }
+                mTimerService.discard(timer);
+                traceEnd(timer);
+                if (DEBUG) report(timer, "discard");
+                timer.release();
+                return true;
+            }
+        }
+
+        /** The feature is enabled. */
+        boolean enabled() {
+            return true;
+        }
     }
 
     /**
-     * Cancel a timer.  Return false if the timer was not found.
+     * Start a timer associated with arg.  If a timer already exists with the same arg, then that
+     * timer is canceled and a new timer is created.  This returns false if the timer cannot be
+     * created.
+     */
+    boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+        return mFeature.start(arg, pid, uid, timeoutMs);
+    }
+
+    /**
+     * Cancel a running timer and remove it from any list.  This returns true if the timer was
+     * found and false otherwise.  It is not an error to cancel a non-existent timer.  It is also
+     * not an error to cancel an expired timer.
      */
     boolean cancel(@NonNull V arg) {
-        synchronized (mLock) {
-            Timer timer = removeLocked(arg);
-            if (timer == null) {
-                if (!mLenientCancel) notFoundLocked("cancel", arg);
-                return false;
-            }
-            mTimerService.cancel(timer);
-            // There may be an expiration message in flight.  Cancel it.
-            mHandler.removeMessages(mWhat, arg);
-            if (DEBUG) report(timer, "cancel");
-            timer.release();
-            return true;
-        }
+        return mFeature.cancel(arg);
     }
 
     /**
-     * Accept a timer in the framework-level handler.  The timeout has been accepted and the
-     * timeout handler is executing.  Return false if the timer was not found.
+     * Accept an expired timer.  This returns false if the timer was not found or if the timer was
+     * not expired.
      */
     boolean accept(@NonNull V arg) {
-        synchronized (mLock) {
-            Timer timer = removeLocked(arg);
-            if (timer == null) {
-                notFoundLocked("accept", arg);
-                return false;
-            }
-            mTimerService.accept(timer);
-            traceEnd(timer);
-            if (DEBUG) report(timer, "accept");
-            timer.release();
-            return true;
-        }
+        return mFeature.accept(arg);
     }
 
     /**
-     * Discard a timer in the framework-level handler.  For whatever reason, the timer is no
-     * longer interesting.  No statistics are collected.  Return false if the time was not found.
+     * Discard an expired timer.  This returns false if the timer was not found or if the timer was
+     * not expired.
      */
     boolean discard(@NonNull V arg) {
-        synchronized (mLock) {
-            Timer timer = removeLocked(arg);
-            if (timer == null) {
-                notFoundLocked("discard", arg);
-                return false;
-            }
-            mTimerService.discard(timer);
-            traceEnd(timer);
-            if (DEBUG) report(timer, "discard");
-            timer.release();
-            return true;
-        }
+        return mFeature.discard(arg);
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 907069d..147f8d1 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -580,7 +580,11 @@
                             batteryStatsInternal);
             curDuration += curStart - lastUidBatteryUsageStartTs;
             try {
-                statsCommit.close();
+                if (statsCommit != null) {
+                    statsCommit.close();
+                } else {
+                    Slog.w(TAG, "Stat was null");
+                }
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to close a stat");
             }
@@ -660,7 +664,11 @@
             }
         }
         try {
-            stats.close();
+            if (stats != null) {
+                stats.close();
+            } else {
+                Slog.w(TAG, "Stat was null");
+            }
         } catch (IOException e) {
             Slog.w(TAG, "Failed to close a stat");
         }
@@ -684,7 +692,11 @@
         final BatteryUsageStats stats = statsList.get(0);
         for (int i = 1; i < statsList.size(); i++) {
             try {
-                statsList.get(i).close();
+                if (statsList.get(i) != null) {
+                    statsList.get(i).close();
+                } else {
+                    Slog.w(TAG, "Stat was null");
+                }
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List");
             }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 5d31d15..e07c2bc 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -106,6 +106,14 @@
     private boolean mTimeoutScheduled;
 
     /**
+     * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically
+     * used when deciding if we should extend the soft ANR timeout.
+     *
+     * Required when Flags.anrTimerServiceEnabled is false.
+     */
+    long lastCpuDelayTime;
+
+     /**
      * Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before
      * dispatching the current broadcast to the receiver in this process.
      */
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index eb219a8..a428907 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -258,6 +258,9 @@
     private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6;
     private static final int MSG_UID_STATE_CHANGED = 7;
 
+    // Required when Flags.anrTimerServiceEnabled is false.
+    private static final int MSG_DELIVERY_TIMEOUT_SOFT = 8;
+
     private void enqueueUpdateRunningList() {
         mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
         mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
@@ -271,6 +274,13 @@
                 updateRunningList();
                 return true;
             }
+            // Required when Flags.anrTimerServiceEnabled is false.
+            case MSG_DELIVERY_TIMEOUT_SOFT: {
+                synchronized (mService) {
+                    deliveryTimeoutSoftLocked((BroadcastProcessQueue) msg.obj, msg.arg1);
+                    return true;
+                }
+            }
             case MSG_DELIVERY_TIMEOUT: {
                 deliveryTimeout((BroadcastProcessQueue) msg.obj);
                 return true;
@@ -1030,7 +1040,7 @@
             queue.setTimeoutScheduled(true);
             final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT
                     : mBgConstants.TIMEOUT);
-            mAnrTimer.start(queue, softTimeoutMillis);
+            startDeliveryTimeoutLocked(queue, softTimeoutMillis);
         } else {
             queue.setTimeoutScheduled(false);
         }
@@ -1110,7 +1120,7 @@
                 // If we were trying to deliver a manifest broadcast, throw the error as we need
                 // to try redelivering the broadcast to this receiver.
                 if (receiver instanceof ResolveInfo) {
-                    mAnrTimer.cancel(queue);
+                    cancelDeliveryTimeoutLocked(queue);
                     throw new BroadcastDeliveryFailedException(e);
                 }
                 finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
@@ -1159,6 +1169,41 @@
         r.resultTo = null;
     }
 
+    // Required when Flags.anrTimerServiceEnabled is false.
+    private void startDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue,
+            int softTimeoutMillis) {
+        if (mAnrTimer.serviceEnabled()) {
+            mAnrTimer.start(queue, softTimeoutMillis);
+        } else {
+            queue.lastCpuDelayTime = queue.app.getCpuDelayTime();
+            mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler,
+                    MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);
+        }
+    }
+
+    // Required when Flags.anrTimerServiceEnabled is false.
+    private void cancelDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) {
+        mAnrTimer.cancel(queue);
+        if (!mAnrTimer.serviceEnabled()) {
+            mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
+        }
+    }
+
+    // Required when Flags.anrTimerServiceEnabled is false.
+    private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue,
+            int softTimeoutMillis) {
+        if (queue.app != null) {
+            // Instead of immediately triggering an ANR, extend the timeout by
+            // the amount of time the process was runnable-but-waiting; we're
+            // only willing to do this once before triggering an hard ANR
+            final long cpuDelayTime = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime;
+            final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis);
+            mAnrTimer.start(queue, hardTimeoutMillis);
+        } else {
+            deliveryTimeoutLocked(queue);
+        }
+    }
+
     private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) {
         synchronized (mService) {
             deliveryTimeoutLocked(queue);
@@ -1292,7 +1337,7 @@
                 mAnrTimer.discard(queue);
             }
         } else if (queue.timeoutScheduled()) {
-            mAnrTimer.cancel(queue);
+            cancelDeliveryTimeoutLocked(queue);
         }
 
         // Given that a receiver just finished, check if the "waitingFor" conditions are met.
diff --git a/services/core/java/com/android/server/am/OomConnection.java b/services/core/java/com/android/server/am/OomConnection.java
new file mode 100644
index 0000000..17a4ce5
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomConnection.java
@@ -0,0 +1,63 @@
+/*
+ * 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.am;
+
+import android.os.OomKillRecord;
+import android.util.Slog;
+
+/** Connection to the out-of-memory (OOM) events' file */
+public final class OomConnection {
+    private static final String TAG = "OomConnection";
+
+    /** Connection listener interface */
+    public interface OomConnectionListener {
+
+        /**
+         * Callback function to handle the newest OOM kills.
+         *
+         * @param oomKills List of oom kills received from `waitOom()`
+         */
+        void handleOomEvent(OomKillRecord[] oomKills);
+    }
+
+    private final OomConnectionListener mOomListener;
+
+    private final OomConnectionThread mOomConnectionThread;
+
+    private static native OomKillRecord[] waitOom();
+
+    public OomConnection(OomConnectionListener listener) {
+        mOomListener = listener;
+        mOomConnectionThread = new OomConnectionThread();
+        mOomConnectionThread.start();
+    }
+
+    private final class OomConnectionThread extends Thread {
+        public void run() {
+            while (true) {
+                OomKillRecord[] oom_kills = null;
+                try {
+                    oom_kills = waitOom();
+                    mOomListener.handleOomEvent(oom_kills);
+                } catch (RuntimeException e) {
+                    Slog.e(TAG, "failed waiting for OOM events: " + e);
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a97675f..4572766 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -97,6 +97,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.OomKillRecord;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -412,6 +413,8 @@
 
     private static LmkdConnection sLmkdConnection = null;
 
+    private static OomConnection sOomConnection = null;
+
     private boolean mOomLevelsSet = false;
 
     private boolean mAppDataIsolationEnabled = false;
@@ -855,6 +858,21 @@
                     THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
             sKillThread.start();
             sKillHandler = new KillHandler(sKillThread.getLooper());
+            sOomConnection = new OomConnection(new OomConnection.OomConnectionListener() {
+                @Override
+                public void handleOomEvent(OomKillRecord[] oomKills) {
+                    for (OomKillRecord oomKill: oomKills) {
+                        synchronized (mProcLock) {
+                            noteAppKill(
+                                oomKill.getPid(),
+                                oomKill.getUid(),
+                                ApplicationExitInfo.REASON_LOW_MEMORY,
+                                ApplicationExitInfo.SUBREASON_OOM_KILL,
+                                "oom");
+                        }
+                    }
+                }
+            });
             sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(),
                     new LmkdConnection.LmkdConnectionListener() {
                         @Override
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4a0bc4b..1ba1f55 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -131,13 +131,17 @@
         "car_telemetry",
         "codec_fwk",
         "companion",
+        "content_protection",
         "context_hub",
         "core_experiments_team_internal",
         "core_graphics",
         "haptics",
         "hardware_backed_security_mainline",
+        "input",
         "machine_learning",
+        "mainline_sdk",
         "media_audio",
+        "media_drm",
         "media_solutions",
         "nfc",
         "pixel_audio_android",
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index b03cc62..26d99d8 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -6,4 +6,12 @@
     description: "Utilize new OomAdjuster implementation"
     bug: "298055811"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+     name: "anr_timer_service_enabled"
+     namespace: "system_performance"
+     is_fixed_read_only: true
+     description: "Feature flag for the ANR timer service"
+     bug: "282428924"
+}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9805fd3..333f62a 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1845,7 +1845,7 @@
     private void parseListenForTickles(TypedXmlPullParser parser) {
         int userId = 0;
         try {
-            parser.getAttributeInt(null, XML_ATTR_USER);
+            userId = parser.getAttributeInt(null, XML_ATTR_USER);
         } catch (XmlPullParserException e) {
             Slog.e(TAG, "error parsing the user for listen-for-tickles", e);
         }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 9e92c8d..cfbe0c6 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -60,6 +60,9 @@
 import com.android.server.display.config.NitsMap;
 import com.android.server.display.config.NonNegativeFloatToFloatPoint;
 import com.android.server.display.config.Point;
+import com.android.server.display.config.PowerThrottlingConfig;
+import com.android.server.display.config.PowerThrottlingMap;
+import com.android.server.display.config.PowerThrottlingPoint;
 import com.android.server.display.config.PredefinedBrightnessLimitNames;
 import com.android.server.display.config.RefreshRateConfigs;
 import com.android.server.display.config.RefreshRateRange;
@@ -139,6 +142,30 @@
  *      </screenBrightnessMap>
  *
  *      <screenBrightnessDefault>0.65</screenBrightnessDefault>
+ *      <powerThrottlingConfig>
+ *        <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
+ *        <pollingWindowMillis>15</pollingWindowMillis>
+ *          <powerThrottlingMap>
+ *              <powerThrottlingPoint>
+ *                  <thermalStatus>severe</thermalStatus>
+ *                  <powerQuotaMilliWatts>200.6</powerQuotaMilliWatts>
+ *              </powerThrottlingPoint>
+ *              <powerThrottlingPoint>
+ *                  <thermalStatus>critical</thermalStatus>
+ *                  <powerQuotaMilliWatts>300</powerQuotaMilliWatts>
+ *              </powerThrottlingPoint>
+ *          </powerThrottlingMap>
+ *          <powerThrottlingMap id="id_2"> // optional attribute, leave blank for default
+ *             <powerThrottlingPoint>
+ *                 <thermalStatus>moderate</thermalStatus>
+ *                 <powerQuotaMilliWatts>400</powerQuotaMilliWatts>
+ *             </powerThrottlingPoint>
+ *             <powerThrottlingPoint>
+ *                 <thermalStatus>severe</thermalStatus>
+ *                 <powerQuotaMilliWatts>250</powerQuotaMilliWatts>
+ *            </powerThrottlingPoint>
+ *          </powerThrottlingMap>
+ *      </powerThrottlingConfig>
  *
  *      <thermalThrottling>
  *        <brightnessThrottlingMap>
@@ -669,6 +696,8 @@
     private List<String> mQuirks;
     private boolean mIsHighBrightnessModeEnabled = false;
     private HighBrightnessModeData mHbmData;
+    @Nullable
+    private PowerThrottlingConfigData mPowerThrottlingConfigData;
     private DensityMapping mDensityMapping;
     private String mLoadedFrom = null;
     private Spline mSdrToHdrRatioSpline;
@@ -781,6 +810,9 @@
     private final HashMap<String, ThermalBrightnessThrottlingData>
             mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
 
+    private final HashMap<String, PowerThrottlingData>
+            mPowerThrottlingDataMapByThrottlingId = new HashMap<>();
+
     private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
             mRefreshRateThrottlingMap = new HashMap<>();
 
@@ -1458,6 +1490,14 @@
         return hbmData;
     }
 
+    /**
+     * @return Power throttling configuration data for the display.
+     */
+    @Nullable
+    public PowerThrottlingConfigData getPowerThrottlingConfigData() {
+        return mPowerThrottlingConfigData;
+    }
+
     @NonNull
     public Map<BrightnessLimitMapType, Map<Float, Float>> getLuxThrottlingData() {
         return mLuxThrottlingData;
@@ -1491,6 +1531,14 @@
     }
 
     /**
+     * @return power throttling configuration data for this display, for each throttling id.
+     **/
+    public HashMap<String, PowerThrottlingData>
+            getPowerThrottlingDataMapByThrottlingId() {
+        return mPowerThrottlingDataMapByThrottlingId;
+    }
+
+    /**
      * @return Auto brightness darkening light debounce
      */
     public long getAutoBrightnessDarkeningLightDebounce() {
@@ -1702,6 +1750,9 @@
                 + ", mThermalBrightnessThrottlingDataMapByThrottlingId="
                 + mThermalBrightnessThrottlingDataMapByThrottlingId
                 + "\n"
+                + ", mPowerThrottlingDataMapByThrottlingId="
+                + mPowerThrottlingDataMapByThrottlingId
+                + "\n"
                 + "mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
                 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
                 + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
@@ -1853,6 +1904,7 @@
                 loadBrightnessConstraintsFromConfigXml();
                 loadBrightnessMap(config);
                 loadThermalThrottlingConfig(config);
+                loadPowerThrottlingConfigData(config);
                 loadHighBrightnessModeData(config);
                 loadLuxThrottling(config);
                 loadQuirks(config);
@@ -2171,6 +2223,59 @@
         }
     }
 
+    private boolean loadPowerThrottlingMaps(PowerThrottlingConfig throttlingConfig) {
+        final List<PowerThrottlingMap> maps = throttlingConfig.getPowerThrottlingMap();
+        if (maps == null || maps.isEmpty()) {
+            Slog.i(TAG, "No power throttling map found");
+            return false;
+        }
+
+        for (PowerThrottlingMap map : maps) {
+            final List<PowerThrottlingPoint> points = map.getPowerThrottlingPoint();
+            // At least 1 point is guaranteed by the display device config schema
+            List<PowerThrottlingData.ThrottlingLevel> throttlingLevels =
+                    new ArrayList<>(points.size());
+
+            boolean badConfig = false;
+            for (PowerThrottlingPoint point : points) {
+                ThermalStatus status = point.getThermalStatus();
+                if (!thermalStatusIsValid(status)) {
+                    badConfig = true;
+                    break;
+                }
+
+                throttlingLevels.add(new PowerThrottlingData.ThrottlingLevel(
+                        convertThermalStatus(status),
+                            point.getPowerQuotaMilliWatts().floatValue()));
+            }
+
+            if (!badConfig) {
+                String id = map.getId() == null ? DEFAULT_ID : map.getId();
+                if (mPowerThrottlingDataMapByThrottlingId.containsKey(id)) {
+                    throw new RuntimeException("Power throttling data with ID " + id
+                            + " already exists");
+                }
+                mPowerThrottlingDataMapByThrottlingId.put(id,
+                        PowerThrottlingData.create(throttlingLevels));
+            }
+        }
+        return true;
+    }
+
+    private void loadPowerThrottlingConfigData(DisplayConfiguration config) {
+        final PowerThrottlingConfig powerThrottlingCfg = config.getPowerThrottlingConfig();
+        if (powerThrottlingCfg == null) {
+            return;
+        }
+        if (!loadPowerThrottlingMaps(powerThrottlingCfg)) {
+            return;
+        }
+        float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
+        int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue();
+        mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
+                                                                   pollingWindowMillis);
+    }
+
     private void loadRefreshRateSetting(DisplayConfiguration config) {
         final RefreshRateConfigs refreshRateConfigs =
                 (config == null) ? null : config.getRefreshRate();
@@ -3379,6 +3484,148 @@
     }
 
     /**
+     * Container for Power throttling configuration data.
+     * TODO(b/302814899): extract to separate class.
+     */
+    public static class PowerThrottlingConfigData {
+        /** Lowest brightness cap allowed for this device. */
+        public final float brightnessLowestCapAllowed;
+        /** Time window for polling power in seconds. */
+        public final int pollingWindowMillis;
+        public PowerThrottlingConfigData(float brightnessLowestCapAllowed,
+                int pollingWindowMillis) {
+            this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
+            this.pollingWindowMillis = pollingWindowMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "PowerThrottlingConfigData{"
+                    + "brightnessLowestCapAllowed: "
+                    + brightnessLowestCapAllowed
+                    + ", pollingWindowMillis: " + pollingWindowMillis
+                    + "} ";
+        }
+    }
+
+    /**
+     * Container for power throttling data.
+     * TODO(b/302814899): extract to separate class and unify with ThermalBrightnessThrottlingData.
+     */
+    public static class PowerThrottlingData {
+        public List<ThrottlingLevel> throttlingLevels;
+
+        /**
+         * thermal status to power quota mapping.
+         */
+        public static class ThrottlingLevel {
+            public @PowerManager.ThermalStatus int thermalStatus;
+            public float powerQuotaMilliWatts;
+
+            public ThrottlingLevel(
+                    @PowerManager.ThermalStatus int thermalStatus, float powerQuotaMilliWatts) {
+                this.thermalStatus = thermalStatus;
+                this.powerQuotaMilliWatts = powerQuotaMilliWatts;
+            }
+
+            @Override
+            public String toString() {
+                return "[" + thermalStatus + "," + powerQuotaMilliWatts + "]";
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                if (!(obj instanceof ThrottlingLevel)) {
+                    return false;
+                }
+                ThrottlingLevel otherThrottlingLevel = (ThrottlingLevel) obj;
+
+                return otherThrottlingLevel.thermalStatus == this.thermalStatus
+                        && otherThrottlingLevel.powerQuotaMilliWatts == this.powerQuotaMilliWatts;
+            }
+
+            @Override
+            public int hashCode() {
+                int result = 1;
+                result = 31 * result + thermalStatus;
+                result = 31 * result + Float.hashCode(powerQuotaMilliWatts);
+                return result;
+            }
+        }
+
+
+        /**
+         * Creates multiple temperature based throttling levels of power quota.
+         */
+        public static PowerThrottlingData create(
+                List<ThrottlingLevel> throttlingLevels) {
+            if (throttlingLevels == null || throttlingLevels.size() == 0) {
+                Slog.e(TAG, "PowerThrottlingData received null or empty throttling levels");
+                return null;
+            }
+
+            ThrottlingLevel prevLevel = throttlingLevels.get(0);
+            final int numLevels = throttlingLevels.size();
+            for (int i = 1; i < numLevels; i++) {
+                ThrottlingLevel thisLevel = throttlingLevels.get(i);
+
+                if (thisLevel.thermalStatus <= prevLevel.thermalStatus) {
+                    Slog.e(TAG, "powerThrottlingMap must be strictly increasing, ignoring "
+                            + "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= "
+                            + prevLevel.thermalStatus);
+                    return null;
+                }
+
+                if (thisLevel.powerQuotaMilliWatts >= prevLevel.powerQuotaMilliWatts) {
+                    Slog.e(TAG, "powerThrottlingMap must be strictly decreasing, ignoring "
+                            + "configuration. powerQuotaMilliWatts "
+                            + thisLevel.powerQuotaMilliWatts + " >= "
+                            + prevLevel.powerQuotaMilliWatts);
+                    return null;
+                }
+
+                prevLevel = thisLevel;
+            }
+            return new PowerThrottlingData(throttlingLevels);
+        }
+
+        @Override
+        public String toString() {
+            return "PowerThrottlingData{"
+                    + "throttlingLevels:" + throttlingLevels
+                    + "} ";
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+
+            if (!(obj instanceof PowerThrottlingData)) {
+                return false;
+            }
+
+            PowerThrottlingData otherData = (PowerThrottlingData) obj;
+            return throttlingLevels.equals(otherData.throttlingLevels);
+        }
+
+        @Override
+        public int hashCode() {
+            return throttlingLevels.hashCode();
+        }
+
+        @VisibleForTesting
+        PowerThrottlingData(List<ThrottlingLevel> inLevels) {
+            throttlingLevels = new ArrayList<>(inLevels.size());
+            for (ThrottlingLevel level : inLevels) {
+                throttlingLevels.add(new ThrottlingLevel(level.thermalStatus,
+                        level.powerQuotaMilliWatts));
+            }
+        }
+    }
+
+    /**
      * Container for brightness throttling data.
      */
     public static class ThermalBrightnessThrottlingData {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 652e6cf..39f0b13 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -105,16 +105,21 @@
     public void resetHdrConfig(HdrBrightnessData data, int width, int height,
             float minimumHdrPercentOfScreen, IBinder displayToken) {
         mHdrBrightnessData = data;
-        mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
+        mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1
+                : (float) (width * height) * minimumHdrPercentOfScreen;
         if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
             if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
                 mHdrListener.unregister(mRegisteredDisplayToken);
                 mHdrVisible = false;
+                mRegisteredDisplayToken = null;
             }
-            if (displayToken != null) { // new token not null, subscribe
+            // new token not null and hdr min % of the screen is set, subscribe.
+            // e.g. for virtual display, HBM data will be missing and HdrListener
+            // should not be registered
+            if (displayToken != null && mHdrListener.mHdrMinPixels > 0) {
                 mHdrListener.register(displayToken);
+                mRegisteredDisplayToken = displayToken;
             }
-            mRegisteredDisplayToken = displayToken;
         }
         recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
     }
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 b6273e1..e66fa5b 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -51,22 +51,10 @@
             Flags.FLAG_ENABLE_DISPLAY_OFFLOAD,
             Flags::enableDisplayOffload);
 
-    private final FlagState mDisplayResolutionRangeVotingState = new FlagState(
-            Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING,
-            Flags::enableDisplayResolutionRangeVoting);
-
-    private final FlagState mUserPreferredModeVoteState = new FlagState(
-            Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE,
-            Flags::enableUserPreferredModeVote);
-
     private final FlagState mExternalDisplayLimitModeState = new FlagState(
             Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
             Flags::enableModeLimitForExternalDisplay);
 
-    private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState(
-            Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION,
-            Flags::enableDisplaysRefreshRatesSynchronization);
-
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
         return mConnectedDisplayManagementFlagState.isEnabled();
@@ -90,7 +78,7 @@
 
     /** Returns whether resolution range voting feature is enabled or not. */
     public boolean isDisplayResolutionRangeVotingEnabled() {
-        return mDisplayResolutionRangeVotingState.isEnabled();
+        return isExternalDisplayLimitModeEnabled();
     }
 
     /**
@@ -98,7 +86,7 @@
      *      {@link com.android.server.display.mode.DisplayModeDirector}
      */
     public boolean isUserPreferredModeVoteEnabled() {
-        return mUserPreferredModeVoteState.isEnabled();
+        return isExternalDisplayLimitModeEnabled();
     }
 
     /**
@@ -112,7 +100,7 @@
      * @return Whether displays refresh rate synchronization is enabled.
      */
     public boolean isDisplaysRefreshRatesSynchronizationEnabled() {
-        return mDisplaysRefreshRatesSynchronizationState.isEnabled();
+        return isExternalDisplayLimitModeEnabled();
     }
 
     /** Returns whether displayoffload is enabled on not */
@@ -150,19 +138,20 @@
         }
 
         private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
-            // TODO(b/299462337) Remove when the infrastructure is ready.
-            if ((Build.IS_ENG || Build.IS_USERDEBUG)
-                    && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
-                return true;
-            }
+            boolean flagValue = false;
             try {
-                return flagFunction.get();
+                flagValue = flagFunction.get();
             } catch (Throwable ex) {
                 if (DEBUG) {
                     Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
                 }
-                return false;
             }
+            // TODO(b/299462337) Remove when the infrastructure is ready.
+            if (Build.IS_ENG || Build.IS_USERDEBUG) {
+                return SystemProperties.getBoolean("persist.sys." + flagName + "-override",
+                        flagValue);
+            }
+            return flagValue;
         }
     }
 }
diff --git a/services/core/java/com/android/server/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java
deleted file mode 100644
index 4b8fabde..0000000
--- a/services/core/java/com/android/server/input/FocusEventDebugView.java
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * 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.input;
-
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-import static android.util.TypedValue.COMPLEX_UNIT_SP;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.animation.LayoutTransition;
-import android.annotation.AnyThread;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RoundedCorner;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.WindowInsets;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.HorizontalScrollView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/**
- *  Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
- *  the screen.
- */
-class FocusEventDebugView extends RelativeLayout {
-
-    private static final String TAG = FocusEventDebugView.class.getSimpleName();
-
-    private static final int KEY_FADEOUT_DURATION_MILLIS = 1000;
-    private static final int KEY_TRANSITION_DURATION_MILLIS = 100;
-
-    private static final int OUTER_PADDING_DP = 16;
-    private static final int KEY_SEPARATION_MARGIN_DP = 16;
-    private static final int KEY_VIEW_SIDE_PADDING_DP = 16;
-    private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
-    private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
-    private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
-    private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
-
-    private final InputManagerService mService;
-    private final int mOuterPadding;
-    private final DisplayMetrics mDm;
-
-    // Tracks all keys that are currently pressed/down.
-    private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
-            mPressedKeys = new HashMap<>();
-
-    @Nullable
-    private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
-    @Nullable
-    private PressedKeyContainer mPressedKeyContainer;
-    @Nullable
-    private PressedKeyContainer mPressedModifierContainer;
-    private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
-    @Nullable
-    private RotaryInputValueView mRotaryInputValueView;
-    private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
-    @Nullable
-    private RotaryInputGraphView mRotaryInputGraphView;
-
-    @VisibleForTesting
-    FocusEventDebugView(Context c, InputManagerService service,
-            Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
-            Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
-        super(c);
-        setFocusableInTouchMode(true);
-
-        mService = service;
-        mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
-        mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
-        mDm = mContext.getResources().getDisplayMetrics();
-        mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
-    }
-
-    FocusEventDebugView(Context c, InputManagerService service) {
-        this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        int paddingBottom = 0;
-
-        final RoundedCorner bottomLeft =
-                insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
-        if (bottomLeft != null && !insets.isRound()) {
-            paddingBottom = bottomLeft.getRadius();
-        }
-
-        final RoundedCorner bottomRight =
-                insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
-        if (bottomRight != null && !insets.isRound()) {
-            paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
-        }
-
-        if (insets.getDisplayCutout() != null) {
-            paddingBottom =
-                    Math.max(paddingBottom, insets.getDisplayCutout().getSafeInsetBottom());
-        }
-
-        setPadding(mOuterPadding, mOuterPadding, mOuterPadding, mOuterPadding + paddingBottom);
-        setClipToPadding(false);
-        invalidate();
-        return super.onApplyWindowInsets(insets);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        handleKeyEvent(event);
-        return super.dispatchKeyEvent(event);
-    }
-
-    @AnyThread
-    public void updateShowKeyPresses(boolean enabled) {
-        post(() -> handleUpdateShowKeyPresses(enabled));
-    }
-
-    @AnyThread
-    public void updateShowRotaryInput(boolean enabled) {
-        post(() -> handleUpdateShowRotaryInput(enabled));
-    }
-
-    private void handleUpdateShowKeyPresses(boolean enabled) {
-        if (enabled == showKeyPresses()) {
-            return;
-        }
-
-        if (!enabled) {
-            removeView(mPressedKeyContainer);
-            mPressedKeyContainer = null;
-            removeView(mPressedModifierContainer);
-            mPressedModifierContainer = null;
-            return;
-        }
-
-        mPressedKeyContainer = new PressedKeyContainer(mContext);
-        mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
-        mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
-        mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
-        final var scroller = new HorizontalScrollView(mContext);
-        scroller.addView(mPressedKeyContainer);
-        scroller.setHorizontalScrollBarEnabled(false);
-        scroller.addOnLayoutChangeListener(
-                (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
-        scroller.setHorizontalFadingEdgeEnabled(true);
-        LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
-        scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
-        scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
-        addView(scroller, scrollerLayoutParams);
-
-        mPressedModifierContainer = new PressedKeyContainer(mContext);
-        mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
-        mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
-        LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
-        modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
-        modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
-        modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
-        addView(mPressedModifierContainer, modifierLayoutParams);
-    }
-
-    @VisibleForTesting
-    void handleUpdateShowRotaryInput(boolean enabled) {
-        if (enabled == showRotaryInput()) {
-            return;
-        }
-
-        if (!enabled) {
-            mFocusEventDebugGlobalMonitor.dispose();
-            mFocusEventDebugGlobalMonitor = null;
-            removeView(mRotaryInputValueView);
-            mRotaryInputValueView = null;
-            removeView(mRotaryInputGraphView);
-            mRotaryInputGraphView = null;
-            return;
-        }
-
-        mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
-
-        mRotaryInputValueView = mRotaryInputValueViewFactory.get();
-        LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
-        valueLayoutParams.addRule(CENTER_HORIZONTAL);
-        valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
-        addView(mRotaryInputValueView, valueLayoutParams);
-
-        mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
-        LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
-                (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
-        graphLayoutParams.addRule(CENTER_IN_PARENT);
-        addView(mRotaryInputGraphView, graphLayoutParams);
-    }
-
-    /** Report a key event to the debug view. */
-    @AnyThread
-    public void reportKeyEvent(KeyEvent event) {
-        post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
-    }
-
-    /** Report a motion event to the debug view. */
-    @AnyThread
-    public void reportMotionEvent(MotionEvent event) {
-        if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
-            return;
-        }
-
-        post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
-    }
-
-    private void handleKeyEvent(KeyEvent keyEvent) {
-        if (!showKeyPresses()) {
-            return;
-        }
-
-        final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
-        final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
-                ? mPressedModifierContainer
-                : mPressedKeyContainer;
-        PressedKeyView pressedKeyView = mPressedKeys.get(identifier);
-        switch (keyEvent.getAction()) {
-            case KeyEvent.ACTION_DOWN: {
-                if (pressedKeyView != null) {
-                    if (keyEvent.getRepeatCount() == 0) {
-                        Slog.w(TAG, "Got key down for "
-                                + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
-                                + " that was already tracked as being down.");
-                        break;
-                    }
-                    container.handleKeyRepeat(pressedKeyView);
-                    break;
-                }
-
-                pressedKeyView = new PressedKeyView(mContext, getLabel(keyEvent));
-                mPressedKeys.put(identifier, pressedKeyView);
-                container.handleKeyPressed(pressedKeyView);
-                break;
-            }
-            case KeyEvent.ACTION_UP: {
-                if (pressedKeyView == null) {
-                    Slog.w(TAG, "Got key up for " + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
-                            + " that was not tracked as being down.");
-                    break;
-                }
-                mPressedKeys.remove(identifier);
-                container.handleKeyRelease(pressedKeyView);
-                break;
-            }
-            default:
-                break;
-        }
-        keyEvent.recycle();
-    }
-
-    @VisibleForTesting
-    void handleRotaryInput(MotionEvent motionEvent) {
-        if (!showRotaryInput()) {
-            return;
-        }
-
-        float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
-        mRotaryInputValueView.updateValue(scrollAxisValue);
-        mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
-
-        motionEvent.recycle();
-    }
-
-    private static String getLabel(KeyEvent event) {
-        switch (event.getKeyCode()) {
-            case KeyEvent.KEYCODE_SPACE:
-                return "\u2423";
-            case KeyEvent.KEYCODE_TAB:
-                return "\u21e5";
-            case KeyEvent.KEYCODE_ENTER:
-            case KeyEvent.KEYCODE_NUMPAD_ENTER:
-                return "\u23CE";
-            case KeyEvent.KEYCODE_DEL:
-                return "\u232B";
-            case KeyEvent.KEYCODE_FORWARD_DEL:
-                return "\u2326";
-            case KeyEvent.KEYCODE_ESCAPE:
-                return "ESC";
-            case KeyEvent.KEYCODE_DPAD_UP:
-                return "\u2191";
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                return "\u2193";
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                return "\u2190";
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                return "\u2192";
-            case KeyEvent.KEYCODE_DPAD_UP_RIGHT:
-                return "\u2197";
-            case KeyEvent.KEYCODE_DPAD_UP_LEFT:
-                return "\u2196";
-            case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT:
-                return "\u2198";
-            case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
-                return "\u2199";
-            default:
-                break;
-        }
-
-        final int unicodeChar = event.getUnicodeChar();
-        if (unicodeChar != 0) {
-            return new String(Character.toChars(unicodeChar));
-        }
-
-        final var label = KeyEvent.keyCodeToString(event.getKeyCode());
-        if (label.startsWith("KEYCODE_")) {
-            return label.substring(8);
-        }
-        return label;
-    }
-
-    /** Determine whether to show key presses by checking one of the key-related objects. */
-    private boolean showKeyPresses() {
-        return mPressedKeyContainer != null;
-    }
-
-    /** Determine whether to show rotary input by checking one of the rotary-related objects. */
-    private boolean showRotaryInput() {
-        return mRotaryInputValueView != null;
-    }
-
-    /**
-     * Converts a dimension in scaled pixel units to integer display pixels.
-     */
-    private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
-        return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
-    }
-
-    private static class PressedKeyView extends TextView {
-
-        private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
-                -1.0f,     0,     0,    0, 255, // red
-                0, -1.0f,     0,    0, 255, // green
-                0,     0, -1.0f,    0, 255, // blue
-                0,     0,     0, 1.0f, 0    // alpha
-        });
-
-        PressedKeyView(Context c, String label) {
-            super(c);
-
-            final var dm = c.getResources().getDisplayMetrics();
-            final int keyViewSidePadding =
-                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_SIDE_PADDING_DP, dm);
-            final int keyViewVerticalPadding =
-                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_VERTICAL_PADDING_DP,
-                            dm);
-            final int keyViewMinWidth =
-                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_MIN_WIDTH_DP, dm);
-            final int textSize =
-                    (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, KEY_VIEW_TEXT_SIZE_SP, dm);
-
-            setText(label);
-            setGravity(Gravity.CENTER);
-            setMinimumWidth(keyViewMinWidth);
-            setTextSize(textSize);
-            setTypeface(Typeface.SANS_SERIF);
-            setBackgroundResource(R.drawable.focus_event_pressed_key_background);
-            setPaddingRelative(keyViewSidePadding, keyViewVerticalPadding, keyViewSidePadding,
-                    keyViewVerticalPadding);
-
-            setHighlighted(true);
-        }
-
-        void setHighlighted(boolean isHighlighted) {
-            if (isHighlighted) {
-                setTextColor(Color.BLACK);
-                getBackground().setColorFilter(sInvertColors);
-            } else {
-                setTextColor(Color.WHITE);
-                getBackground().clearColorFilter();
-            }
-            invalidate();
-        }
-    }
-
-    private static class PressedKeyContainer extends LinearLayout {
-
-        private final MarginLayoutParams mPressedKeyLayoutParams;
-
-        PressedKeyContainer(Context c) {
-            super(c);
-
-            final var dm = c.getResources().getDisplayMetrics();
-            final int keySeparationMargin =
-                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_SEPARATION_MARGIN_DP, dm);
-
-            final var transition = new LayoutTransition();
-            transition.disableTransitionType(LayoutTransition.APPEARING);
-            transition.disableTransitionType(LayoutTransition.DISAPPEARING);
-            transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-            transition.setDuration(KEY_TRANSITION_DURATION_MILLIS);
-            setLayoutTransition(transition);
-
-            mPressedKeyLayoutParams = new MarginLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
-            if (getOrientation() == VERTICAL) {
-                mPressedKeyLayoutParams.setMargins(0, keySeparationMargin, 0, 0);
-            } else {
-                mPressedKeyLayoutParams.setMargins(keySeparationMargin, 0, 0, 0);
-            }
-        }
-
-        public void handleKeyPressed(PressedKeyView pressedKeyView) {
-            addView(pressedKeyView, getChildCount(), mPressedKeyLayoutParams);
-            invalidate();
-        }
-
-        public void handleKeyRepeat(PressedKeyView repeatedKeyView) {
-            // Do nothing for now.
-        }
-
-        public void handleKeyRelease(PressedKeyView releasedKeyView) {
-            releasedKeyView.setHighlighted(false);
-            releasedKeyView.clearAnimation();
-            releasedKeyView.animate()
-                    .alpha(0)
-                    .setDuration(KEY_FADEOUT_DURATION_MILLIS)
-                    .setInterpolator(new AccelerateInterpolator())
-                    .withEndAction(this::cleanUpPressedKeyViews)
-                    .start();
-        }
-
-        private void cleanUpPressedKeyViews() {
-            int numChildrenToRemove = 0;
-            for (int i = 0; i < getChildCount(); i++) {
-                final View child = getChildAt(i);
-                if (child.getAlpha() != 0) {
-                    break;
-                }
-                child.setVisibility(View.GONE);
-                child.clearAnimation();
-                numChildrenToRemove++;
-            }
-            removeViews(0, numChildrenToRemove);
-            invalidate();
-        }
-    }
-
-    // TODO(b/286086154): move RotaryInputGraphView and RotaryInputValueView to a subpackage.
-
-    /** Draws the most recent rotary input value and indicates whether the source is active. */
-    @VisibleForTesting
-    static class RotaryInputValueView extends TextView {
-
-        private static final int INACTIVE_TEXT_COLOR = 0xffff00ff;
-        private static final int ACTIVE_TEXT_COLOR = 0xff420f28;
-        private static final int TEXT_SIZE_SP = 8;
-        private static final int SIDE_PADDING_SP = 4;
-        /** Determines how long the active status lasts. */
-        private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */;
-        private static final ColorFilter ACTIVE_BACKGROUND_FILTER =
-                new ColorMatrixColorFilter(new float[]{
-                        0, 0, 0, 0, 255, // red
-                        0, 0, 0, 0,   0, // green
-                        0, 0, 0, 0, 255, // blue
-                        0, 0, 0, 0, 200  // alpha
-                });
-
-        private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false);
-        private final float mScaledVerticalScrollFactor;
-
-        @VisibleForTesting
-        RotaryInputValueView(Context c) {
-            super(c);
-
-            DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
-            mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
-
-            setText(getFormattedValue(0));
-            setTextColor(INACTIVE_TEXT_COLOR);
-            setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm));
-            setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0,
-                    applyDimensionSp(SIDE_PADDING_SP, dm), 0);
-            setTypeface(null, Typeface.BOLD);
-            setBackgroundResource(R.drawable.focus_event_rotary_input_background);
-        }
-
-        void updateValue(float value) {
-            removeCallbacks(mUpdateActivityStatusCallback);
-
-            setText(getFormattedValue(value * mScaledVerticalScrollFactor));
-
-            updateActivityStatus(true);
-            postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION);
-        }
-
-        @VisibleForTesting
-        void updateActivityStatus(boolean active) {
-            if (active) {
-                setTextColor(ACTIVE_TEXT_COLOR);
-                getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER);
-            } else {
-                setTextColor(INACTIVE_TEXT_COLOR);
-                getBackground().clearColorFilter();
-            }
-        }
-
-        private static String getFormattedValue(float value) {
-            return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value));
-        }
-    }
-
-    /**
-     * Shows a graph with the rotary input values as a function of time.
-     * The graph gets reset if no action is received for a certain amount of time.
-     */
-    @VisibleForTesting
-    static class RotaryInputGraphView extends View {
-
-        private static final int FRAME_COLOR = 0xbf741b47;
-        private static final int FRAME_WIDTH_SP = 2;
-        private static final int FRAME_BORDER_GAP_SP = 10;
-        private static final int FRAME_TEXT_SIZE_SP = 10;
-        private static final int FRAME_TEXT_OFFSET_SP = 2;
-        private static final int GRAPH_COLOR = 0xffff00ff;
-        private static final int GRAPH_LINE_WIDTH_SP = 1;
-        private static final int GRAPH_POINT_RADIUS_SP = 4;
-        private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5);
-        private static final float DEFAULT_FRAME_CENTER_POSITION = 0;
-        private static final int MAX_GRAPH_VALUES_SIZE = 400;
-        /** Maximum time between values so that they are considered part of the same gesture. */
-        private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1);
-
-        private final DisplayMetrics mDm;
-        /**
-         * Distance in position units (amount scrolled in display pixels) from the center to the
-         * top/bottom frame lines.
-         */
-        private final float mFrameCenterToBorderDistance;
-        private final float mScaledVerticalScrollFactor;
-        private final Locale mDefaultLocale;
-        private final Paint mFramePaint = new Paint();
-        private final Paint mFrameTextPaint = new Paint();
-        private final Paint mGraphLinePaint = new Paint();
-        private final Paint mGraphPointPaint = new Paint();
-
-        private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE);
-        /** Position at which graph values are placed at the center of the graph. */
-        private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
-
-        @VisibleForTesting
-        RotaryInputGraphView(Context c) {
-            super(c);
-
-            mDm = mContext.getResources().getDisplayMetrics();
-            // This makes the center-to-border distance equivalent to the display height, meaning
-            // that the total height of the graph is equivalent to 2x the display height.
-            mFrameCenterToBorderDistance = mDm.heightPixels;
-            mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
-            mDefaultLocale = Locale.getDefault();
-
-            mFramePaint.setColor(FRAME_COLOR);
-            mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm));
-
-            mFrameTextPaint.setColor(GRAPH_COLOR);
-            mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm));
-
-            mGraphLinePaint.setColor(GRAPH_COLOR);
-            mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm));
-            mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND);
-            mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND);
-
-            mGraphPointPaint.setColor(GRAPH_COLOR);
-            mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm));
-            mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND);
-            mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND);
-        }
-
-        /**
-         * Reads new scroll axis value and updates the list accordingly. Old positions are
-         * kept at the front (what you would get with getFirst), while the recent positions are
-         * kept at the back (what you would get with getLast). Also updates the frame center
-         * position to handle out-of-bounds cases.
-         */
-        void addValue(float scrollAxisValue, long eventTime) {
-            // Remove values that are too old.
-            while (mGraphValues.getSize() > 0
-                    && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) {
-                mGraphValues.removeFirst();
-            }
-
-            // If there are no recent values, reset the frame center.
-            if (mGraphValues.getSize() == 0) {
-                mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
-            }
-
-            // Handle new value. We multiply the scroll axis value by the scaled scroll factor to
-            // get the amount of pixels to be scrolled. We also compute the accumulated position
-            // by adding the current value to the last one (if not empty).
-            final float displacement = scrollAxisValue * mScaledVerticalScrollFactor;
-            final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos);
-            final float pos = prevPos + displacement;
-
-            mGraphValues.add(pos, eventTime);
-
-            // The difference between the distance of the most recent position from the center
-            // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center
-            // frame (mFrameCenterToBorderDistance).
-            final float verticalDiff = Math.abs(pos - mFrameCenterPosition)
-                    - mFrameCenterToBorderDistance;
-            // If needed, translate frame.
-            if (verticalDiff > 0) {
-                final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1;
-                // Here, we update the center frame position by the exact amount needed for us to
-                // stay within the maximum allowed distance from the center frame.
-                mFrameCenterPosition += sign * verticalDiff;
-            }
-
-            // Redraw canvas.
-            invalidate();
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
-
-            // Note: vertical coordinates in Canvas go from top to bottom,
-            // that is bottomY > middleY > topY.
-            final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm);
-            final int topY = verticalMargin;
-            final int bottomY = getHeight() - verticalMargin;
-            final int middleY = (topY + bottomY) / 2;
-
-            // Note: horizontal coordinates in Canvas go from left to right,
-            // that is rightX > leftX.
-            final int leftX = 0;
-            final int rightX = getWidth();
-
-            // Draw the frame, which includes 3 lines that show the maximum,
-            // minimum and middle positions of the graph.
-            canvas.drawLine(leftX, topY, rightX, topY, mFramePaint);
-            canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint);
-            canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint);
-
-            // Draw the position that each frame line corresponds to.
-            final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm);
-            canvas.drawText(
-                    String.format(mDefaultLocale, "%.1f",
-                            mFrameCenterPosition + mFrameCenterToBorderDistance),
-                    leftX,
-                    topY - frameTextOffset, mFrameTextPaint
-            );
-            canvas.drawText(
-                    String.format(mDefaultLocale, "%.1f", mFrameCenterPosition),
-                    leftX,
-                    middleY - frameTextOffset, mFrameTextPaint
-            );
-            canvas.drawText(
-                    String.format(mDefaultLocale, "%.1f",
-                            mFrameCenterPosition - mFrameCenterToBorderDistance),
-                    leftX,
-                    bottomY - frameTextOffset, mFrameTextPaint
-            );
-
-            // If there are no graph values to be drawn, stop here.
-            if (mGraphValues.getSize() == 0) {
-                return;
-            }
-
-            // Draw the graph using the times and positions.
-            // We start at the most recent value (which should be drawn at the right) and move
-            // to the older values (which should be drawn to the left of more recent ones). Negative
-            // indices are handled by circuling back to the end of the buffer.
-            final long mostRecentTime = mGraphValues.getLast().mTime;
-            float prevCoordX = 0;
-            float prevCoordY = 0;
-            float prevAge = 0;
-            for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) {
-                final GraphValue value = iter.next();
-
-                final int age = (int) (mostRecentTime - value.mTime);
-                final float pos = value.mPos;
-
-                // We get the horizontal coordinate in time units from left to right with
-                // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas
-                // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL)
-                // and by multiplying it by the canvas length (rightX - leftX). Finally, we
-                // offset the coordinate by adding it to leftX.
-                final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age)
-                        / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX);
-
-                // We get the vertical coordinate in position units from middle to top with
-                // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas
-                // units by dividing it by half of the position-domain length
-                // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas
-                // length (middleY - topY). Finally, we offset the coordinate by subtracting
-                // it from middleY (we can't "add" here because the coordinate grows from top
-                // to bottom).
-                final float coordY = middleY - ((pos - mFrameCenterPosition)
-                        / mFrameCenterToBorderDistance) * (middleY - topY);
-
-                // Draw a point for this value.
-                canvas.drawPoint(coordX, coordY, mGraphPointPaint);
-
-                // If this value is part of the same gesture as the previous one, draw a line
-                // between them. We ignore the first value (with age = 0).
-                if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) {
-                    canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint);
-                }
-
-                prevCoordX = coordX;
-                prevCoordY = coordY;
-                prevAge = age;
-            }
-        }
-
-        @VisibleForTesting
-        float getFrameCenterPosition() {
-            return mFrameCenterPosition;
-        }
-
-        /**
-         * Holds data needed to draw each entry in the graph.
-         */
-        private static class GraphValue {
-            /** Position. */
-            float mPos;
-            /** Time when this value was added. */
-            long mTime;
-
-            GraphValue(float pos, long time) {
-                this.mPos = pos;
-                this.mTime = time;
-            }
-        }
-
-        /**
-         * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the
-         * old values with new ones to avoid creating new objects.
-         */
-        private static class CyclicBuffer {
-            private final GraphValue[] mValues;
-            private final int mCapacity;
-            private int mSize = 0;
-            private int mLastIndex = 0;
-
-            // The iteration index and counter are here to make it easier to reset them.
-            /** Determines the value currently pointed by the iterator. */
-            private int mIteratorIndex;
-            /** Counts how many values have been iterated through. */
-            private int mIteratorCount;
-
-            /** Used traverse the values in reverse order. */
-            private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() {
-                @Override
-                public boolean hasNext() {
-                    return mIteratorCount <= mSize;
-                }
-
-                @Override
-                public GraphValue next() {
-                    // Returns the value currently pointed by the iterator and moves the iterator to
-                    // the previous one.
-                    mIteratorCount++;
-                    return mValues[(mIteratorIndex-- + mCapacity) % mCapacity];
-                }
-            };
-
-            CyclicBuffer(int capacity) {
-                mCapacity = capacity;
-                mValues = new GraphValue[capacity];
-            }
-
-            /**
-             * Add new graph value. If there is an existing object, we replace its data with the
-             * new one. With this, we re-use old objects instead of creating new ones.
-             */
-            void add(float pos, long time) {
-                mLastIndex = (mLastIndex + 1) % mCapacity;
-                if (mValues[mLastIndex] == null) {
-                    mValues[mLastIndex] = new GraphValue(pos, time);
-                } else {
-                    final GraphValue oldValue = mValues[mLastIndex];
-                    oldValue.mPos = pos;
-                    oldValue.mTime = time;
-                }
-
-                // If needed, account for new value in the buffer size.
-                if (mSize != mCapacity) {
-                    mSize++;
-                }
-            }
-
-            int getSize() {
-                return mSize;
-            }
-
-            GraphValue getFirst() {
-                final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1;
-                final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity;
-                return mValues[firstIndex];
-            }
-
-            GraphValue getLast() {
-                return mValues[mLastIndex];
-            }
-
-            void removeFirst() {
-                mSize--;
-            }
-
-            /** Returns an iterator pointing at the last value. */
-            Iterator<GraphValue> reverseIterator() {
-                mIteratorIndex = mLastIndex;
-                mIteratorCount = 1;
-                return mReverseIterator;
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index 2ede56d..a2c8748 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -62,10 +62,10 @@
         mWindowHandle.ownerUid = uid;
         mWindowHandle.scaleFactor = 1.0f;
         mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
-        mWindowHandle.inputConfig =
-                InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY;
+        mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY;
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
         t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 6b399de..2533e02 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -117,6 +117,7 @@
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerInternal.LidSwitchCallback;
+import com.android.server.input.debug.FocusEventDebugView;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
 
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 0eb620f..bad6bf0 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -71,6 +71,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -99,7 +100,7 @@
  *
  * @hide
  */
-final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+class KeyboardLayoutManager implements InputManager.InputDeviceListener {
 
     private static final String TAG = "KeyboardLayoutManager";
 
@@ -1295,7 +1296,8 @@
     }
 
     @SuppressLint("MissingPermission")
-    private List<ImeInfo> getImeInfoListForLayoutMapping() {
+    @VisibleForTesting
+    public List<ImeInfo> getImeInfoListForLayoutMapping() {
         List<ImeInfo> imeInfoList = new ArrayList<>();
         UserManager userManager = Objects.requireNonNull(
                 mContext.getSystemService(UserManager.class));
@@ -1402,7 +1404,8 @@
         }
     }
 
-    private static class ImeInfo {
+    @VisibleForTesting
+    public static class ImeInfo {
         @UserIdInt int mUserId;
         @NonNull InputMethodSubtypeHandle mImeSubtypeHandle;
         @Nullable InputMethodSubtype mImeSubtype;
diff --git a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
similarity index 94%
rename from services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java
rename to services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
index 67c221f..2b21e49 100644
--- a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.input;
+package com.android.server.input.debug;
 
 import android.view.Display;
 import android.view.InputEvent;
@@ -22,6 +22,7 @@
 import android.view.MotionEvent;
 
 import com.android.server.UiThread;
+import com.android.server.input.InputManagerService;
 
 /**
  * Receives input events before they are dispatched and reports them to FocusEventDebugView.
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
new file mode 100644
index 0000000..6eec0de
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -0,0 +1,466 @@
+/*
+ * 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.input.debug;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.animation.LayoutTransition;
+import android.annotation.AnyThread;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Typeface;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RoundedCorner;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.input.InputManagerService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ *  Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
+ *  the screen.
+ */
+public class FocusEventDebugView extends RelativeLayout {
+
+    private static final String TAG = FocusEventDebugView.class.getSimpleName();
+
+    private static final int KEY_FADEOUT_DURATION_MILLIS = 1000;
+    private static final int KEY_TRANSITION_DURATION_MILLIS = 100;
+
+    private static final int OUTER_PADDING_DP = 16;
+    private static final int KEY_SEPARATION_MARGIN_DP = 16;
+    private static final int KEY_VIEW_SIDE_PADDING_DP = 16;
+    private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
+    private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
+    private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
+    private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
+
+    private final InputManagerService mService;
+    private final int mOuterPadding;
+    private final DisplayMetrics mDm;
+
+    // Tracks all keys that are currently pressed/down.
+    private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
+            mPressedKeys = new HashMap<>();
+
+    @Nullable
+    private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
+    @Nullable
+    private PressedKeyContainer mPressedKeyContainer;
+    @Nullable
+    private PressedKeyContainer mPressedModifierContainer;
+    private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
+    @Nullable
+    private RotaryInputValueView mRotaryInputValueView;
+    private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
+    @Nullable
+    private RotaryInputGraphView mRotaryInputGraphView;
+
+    @VisibleForTesting
+    FocusEventDebugView(Context c, InputManagerService service,
+            Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
+            Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
+        super(c);
+        setFocusableInTouchMode(true);
+
+        mService = service;
+        mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
+        mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
+        mDm = mContext.getResources().getDisplayMetrics();
+        mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
+    }
+
+    public FocusEventDebugView(Context c, InputManagerService service) {
+        this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        int paddingBottom = 0;
+
+        final RoundedCorner bottomLeft =
+                insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+        if (bottomLeft != null && !insets.isRound()) {
+            paddingBottom = bottomLeft.getRadius();
+        }
+
+        final RoundedCorner bottomRight =
+                insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+        if (bottomRight != null && !insets.isRound()) {
+            paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
+        }
+
+        if (insets.getDisplayCutout() != null) {
+            paddingBottom =
+                    Math.max(paddingBottom, insets.getDisplayCutout().getSafeInsetBottom());
+        }
+
+        setPadding(mOuterPadding, mOuterPadding, mOuterPadding, mOuterPadding + paddingBottom);
+        setClipToPadding(false);
+        invalidate();
+        return super.onApplyWindowInsets(insets);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        handleKeyEvent(event);
+        return super.dispatchKeyEvent(event);
+    }
+
+    /** Determines whether to show the key presses visualization. */
+    @AnyThread
+    public void updateShowKeyPresses(boolean enabled) {
+        post(() -> handleUpdateShowKeyPresses(enabled));
+    }
+
+    /** Determines whether to show the rotary input visualization. */
+    @AnyThread
+    public void updateShowRotaryInput(boolean enabled) {
+        post(() -> handleUpdateShowRotaryInput(enabled));
+    }
+
+    private void handleUpdateShowKeyPresses(boolean enabled) {
+        if (enabled == showKeyPresses()) {
+            return;
+        }
+
+        if (!enabled) {
+            removeView(mPressedKeyContainer);
+            mPressedKeyContainer = null;
+            removeView(mPressedModifierContainer);
+            mPressedModifierContainer = null;
+            return;
+        }
+
+        mPressedKeyContainer = new PressedKeyContainer(mContext);
+        mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
+        mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
+        mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
+        final var scroller = new HorizontalScrollView(mContext);
+        scroller.addView(mPressedKeyContainer);
+        scroller.setHorizontalScrollBarEnabled(false);
+        scroller.addOnLayoutChangeListener(
+                (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
+        scroller.setHorizontalFadingEdgeEnabled(true);
+        LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+        scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+        scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
+        addView(scroller, scrollerLayoutParams);
+
+        mPressedModifierContainer = new PressedKeyContainer(mContext);
+        mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
+        mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
+        LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+        modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+        modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
+        modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
+        addView(mPressedModifierContainer, modifierLayoutParams);
+    }
+
+    @VisibleForTesting
+    void handleUpdateShowRotaryInput(boolean enabled) {
+        if (enabled == showRotaryInput()) {
+            return;
+        }
+
+        if (!enabled) {
+            mFocusEventDebugGlobalMonitor.dispose();
+            mFocusEventDebugGlobalMonitor = null;
+            removeView(mRotaryInputValueView);
+            mRotaryInputValueView = null;
+            removeView(mRotaryInputGraphView);
+            mRotaryInputGraphView = null;
+            return;
+        }
+
+        mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
+
+        mRotaryInputValueView = mRotaryInputValueViewFactory.get();
+        LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+        valueLayoutParams.addRule(CENTER_HORIZONTAL);
+        valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+        addView(mRotaryInputValueView, valueLayoutParams);
+
+        mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
+        LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+                (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
+        graphLayoutParams.addRule(CENTER_IN_PARENT);
+        addView(mRotaryInputGraphView, graphLayoutParams);
+    }
+
+    /** Report a key event to the debug view. */
+    @AnyThread
+    public void reportKeyEvent(KeyEvent event) {
+        post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
+    }
+
+    /** Report a motion event to the debug view. */
+    @AnyThread
+    public void reportMotionEvent(MotionEvent event) {
+        if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
+            return;
+        }
+
+        post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
+    }
+
+    private void handleKeyEvent(KeyEvent keyEvent) {
+        if (!showKeyPresses()) {
+            return;
+        }
+
+        final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
+        final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
+                ? mPressedModifierContainer
+                : mPressedKeyContainer;
+        PressedKeyView pressedKeyView = mPressedKeys.get(identifier);
+        switch (keyEvent.getAction()) {
+            case KeyEvent.ACTION_DOWN: {
+                if (pressedKeyView != null) {
+                    if (keyEvent.getRepeatCount() == 0) {
+                        Slog.w(TAG, "Got key down for "
+                                + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
+                                + " that was already tracked as being down.");
+                        break;
+                    }
+                    container.handleKeyRepeat(pressedKeyView);
+                    break;
+                }
+
+                pressedKeyView = new PressedKeyView(mContext, getLabel(keyEvent));
+                mPressedKeys.put(identifier, pressedKeyView);
+                container.handleKeyPressed(pressedKeyView);
+                break;
+            }
+            case KeyEvent.ACTION_UP: {
+                if (pressedKeyView == null) {
+                    Slog.w(TAG, "Got key up for " + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
+                            + " that was not tracked as being down.");
+                    break;
+                }
+                mPressedKeys.remove(identifier);
+                container.handleKeyRelease(pressedKeyView);
+                break;
+            }
+            default:
+                break;
+        }
+        keyEvent.recycle();
+    }
+
+    @VisibleForTesting
+    void handleRotaryInput(MotionEvent motionEvent) {
+        if (!showRotaryInput()) {
+            return;
+        }
+
+        float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
+        mRotaryInputValueView.updateValue(scrollAxisValue);
+        mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
+
+        motionEvent.recycle();
+    }
+
+    private static String getLabel(KeyEvent event) {
+        switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_SPACE:
+                return "\u2423";
+            case KeyEvent.KEYCODE_TAB:
+                return "\u21e5";
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_NUMPAD_ENTER:
+                return "\u23CE";
+            case KeyEvent.KEYCODE_DEL:
+                return "\u232B";
+            case KeyEvent.KEYCODE_FORWARD_DEL:
+                return "\u2326";
+            case KeyEvent.KEYCODE_ESCAPE:
+                return "ESC";
+            case KeyEvent.KEYCODE_DPAD_UP:
+                return "\u2191";
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                return "\u2193";
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                return "\u2190";
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                return "\u2192";
+            case KeyEvent.KEYCODE_DPAD_UP_RIGHT:
+                return "\u2197";
+            case KeyEvent.KEYCODE_DPAD_UP_LEFT:
+                return "\u2196";
+            case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT:
+                return "\u2198";
+            case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
+                return "\u2199";
+            default:
+                break;
+        }
+
+        final int unicodeChar = event.getUnicodeChar();
+        if (unicodeChar != 0) {
+            return new String(Character.toChars(unicodeChar));
+        }
+
+        final var label = KeyEvent.keyCodeToString(event.getKeyCode());
+        if (label.startsWith("KEYCODE_")) {
+            return label.substring(8);
+        }
+        return label;
+    }
+
+    /** Determine whether to show key presses by checking one of the key-related objects. */
+    private boolean showKeyPresses() {
+        return mPressedKeyContainer != null;
+    }
+
+    /** Determine whether to show rotary input by checking one of the rotary-related objects. */
+    private boolean showRotaryInput() {
+        return mRotaryInputValueView != null;
+    }
+
+    private static class PressedKeyView extends TextView {
+
+        private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
+                -1.0f,     0,     0,    0, 255, // red
+                0, -1.0f,     0,    0, 255, // green
+                0,     0, -1.0f,    0, 255, // blue
+                0,     0,     0, 1.0f, 0    // alpha
+        });
+
+        PressedKeyView(Context c, String label) {
+            super(c);
+
+            final var dm = c.getResources().getDisplayMetrics();
+            final int keyViewSidePadding =
+                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_SIDE_PADDING_DP, dm);
+            final int keyViewVerticalPadding =
+                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_VERTICAL_PADDING_DP,
+                            dm);
+            final int keyViewMinWidth =
+                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_MIN_WIDTH_DP, dm);
+            final int textSize =
+                    (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, KEY_VIEW_TEXT_SIZE_SP, dm);
+
+            setText(label);
+            setGravity(Gravity.CENTER);
+            setMinimumWidth(keyViewMinWidth);
+            setTextSize(textSize);
+            setTypeface(Typeface.SANS_SERIF);
+            setBackgroundResource(R.drawable.focus_event_pressed_key_background);
+            setPaddingRelative(keyViewSidePadding, keyViewVerticalPadding, keyViewSidePadding,
+                    keyViewVerticalPadding);
+
+            setHighlighted(true);
+        }
+
+        void setHighlighted(boolean isHighlighted) {
+            if (isHighlighted) {
+                setTextColor(Color.BLACK);
+                getBackground().setColorFilter(sInvertColors);
+            } else {
+                setTextColor(Color.WHITE);
+                getBackground().clearColorFilter();
+            }
+            invalidate();
+        }
+    }
+
+    private static class PressedKeyContainer extends LinearLayout {
+
+        private final MarginLayoutParams mPressedKeyLayoutParams;
+
+        PressedKeyContainer(Context c) {
+            super(c);
+
+            final var dm = c.getResources().getDisplayMetrics();
+            final int keySeparationMargin =
+                    (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_SEPARATION_MARGIN_DP, dm);
+
+            final var transition = new LayoutTransition();
+            transition.disableTransitionType(LayoutTransition.APPEARING);
+            transition.disableTransitionType(LayoutTransition.DISAPPEARING);
+            transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+            transition.setDuration(KEY_TRANSITION_DURATION_MILLIS);
+            setLayoutTransition(transition);
+
+            mPressedKeyLayoutParams = new MarginLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+            if (getOrientation() == VERTICAL) {
+                mPressedKeyLayoutParams.setMargins(0, keySeparationMargin, 0, 0);
+            } else {
+                mPressedKeyLayoutParams.setMargins(keySeparationMargin, 0, 0, 0);
+            }
+        }
+
+        public void handleKeyPressed(PressedKeyView pressedKeyView) {
+            addView(pressedKeyView, getChildCount(), mPressedKeyLayoutParams);
+            invalidate();
+        }
+
+        public void handleKeyRepeat(PressedKeyView repeatedKeyView) {
+            // Do nothing for now.
+        }
+
+        public void handleKeyRelease(PressedKeyView releasedKeyView) {
+            releasedKeyView.setHighlighted(false);
+            releasedKeyView.clearAnimation();
+            releasedKeyView.animate()
+                    .alpha(0)
+                    .setDuration(KEY_FADEOUT_DURATION_MILLIS)
+                    .setInterpolator(new AccelerateInterpolator())
+                    .withEndAction(this::cleanUpPressedKeyViews)
+                    .start();
+        }
+
+        private void cleanUpPressedKeyViews() {
+            int numChildrenToRemove = 0;
+            for (int i = 0; i < getChildCount(); i++) {
+                final View child = getChildAt(i);
+                if (child.getAlpha() != 0) {
+                    break;
+                }
+                child.setVisibility(View.GONE);
+                child.clearAnimation();
+                numChildrenToRemove++;
+            }
+            removeViews(0, numChildrenToRemove);
+            invalidate();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java
new file mode 100644
index 0000000..95635d9
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java
@@ -0,0 +1,342 @@
+/*
+ * 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.input.debug;
+
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Shows a graph with the rotary input values as a function of time.
+ * The graph gets reset if no action is received for a certain amount of time.
+ */
+public class RotaryInputGraphView extends View {
+
+    private static final int FRAME_COLOR = 0xbf741b47;
+    private static final int FRAME_WIDTH_SP = 2;
+    private static final int FRAME_BORDER_GAP_SP = 10;
+    private static final int FRAME_TEXT_SIZE_SP = 10;
+    private static final int FRAME_TEXT_OFFSET_SP = 2;
+    private static final int GRAPH_COLOR = 0xffff00ff;
+    private static final int GRAPH_LINE_WIDTH_SP = 1;
+    private static final int GRAPH_POINT_RADIUS_SP = 4;
+    private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5);
+    private static final float DEFAULT_FRAME_CENTER_POSITION = 0;
+    private static final int MAX_GRAPH_VALUES_SIZE = 400;
+    /** Maximum time between values so that they are considered part of the same gesture. */
+    private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1);
+
+    private final DisplayMetrics mDm;
+    /**
+     * Distance in position units (amount scrolled in display pixels) from the center to the
+     * top/bottom frame lines.
+     */
+    private final float mFrameCenterToBorderDistance;
+    private final float mScaledVerticalScrollFactor;
+    private final Locale mDefaultLocale = Locale.getDefault();
+    private final Paint mFramePaint = new Paint();
+    private final Paint mFrameTextPaint = new Paint();
+    private final Paint mGraphLinePaint = new Paint();
+    private final Paint mGraphPointPaint = new Paint();
+
+    private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE);
+    /** Position at which graph values are placed at the center of the graph. */
+    private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
+
+    public RotaryInputGraphView(Context c) {
+        super(c);
+
+        mDm = mContext.getResources().getDisplayMetrics();
+        // This makes the center-to-border distance equivalent to the display height, meaning
+        // that the total height of the graph is equivalent to 2x the display height.
+        mFrameCenterToBorderDistance = mDm.heightPixels;
+        mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+
+        mFramePaint.setColor(FRAME_COLOR);
+        mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm));
+
+        mFrameTextPaint.setColor(GRAPH_COLOR);
+        mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm));
+
+        mGraphLinePaint.setColor(GRAPH_COLOR);
+        mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm));
+        mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND);
+        mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND);
+
+        mGraphPointPaint.setColor(GRAPH_COLOR);
+        mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm));
+        mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND);
+        mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND);
+    }
+
+    /**
+     * Reads new scroll axis value and updates the list accordingly. Old positions are
+     * kept at the front (what you would get with getFirst), while the recent positions are
+     * kept at the back (what you would get with getLast). Also updates the frame center
+     * position to handle out-of-bounds cases.
+     */
+    public void addValue(float scrollAxisValue, long eventTime) {
+        // Remove values that are too old.
+        while (mGraphValues.getSize() > 0
+                && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) {
+            mGraphValues.removeFirst();
+        }
+
+        // If there are no recent values, reset the frame center.
+        if (mGraphValues.getSize() == 0) {
+            mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
+        }
+
+        // Handle new value. We multiply the scroll axis value by the scaled scroll factor to
+        // get the amount of pixels to be scrolled. We also compute the accumulated position
+        // by adding the current value to the last one (if not empty).
+        final float displacement = scrollAxisValue * mScaledVerticalScrollFactor;
+        final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos);
+        final float pos = prevPos + displacement;
+
+        mGraphValues.add(pos, eventTime);
+
+        // The difference between the distance of the most recent position from the center
+        // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center
+        // frame (mFrameCenterToBorderDistance).
+        final float verticalDiff = Math.abs(pos - mFrameCenterPosition)
+                - mFrameCenterToBorderDistance;
+        // If needed, translate frame.
+        if (verticalDiff > 0) {
+            final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1;
+            // Here, we update the center frame position by the exact amount needed for us to
+            // stay within the maximum allowed distance from the center frame.
+            mFrameCenterPosition += sign * verticalDiff;
+        }
+
+        // Redraw canvas.
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        // Note: vertical coordinates in Canvas go from top to bottom,
+        // that is bottomY > middleY > topY.
+        final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm);
+        final int topY = verticalMargin;
+        final int bottomY = getHeight() - verticalMargin;
+        final int middleY = (topY + bottomY) / 2;
+
+        // Note: horizontal coordinates in Canvas go from left to right,
+        // that is rightX > leftX.
+        final int leftX = 0;
+        final int rightX = getWidth();
+
+        // Draw the frame, which includes 3 lines that show the maximum,
+        // minimum and middle positions of the graph.
+        canvas.drawLine(leftX, topY, rightX, topY, mFramePaint);
+        canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint);
+        canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint);
+
+        // Draw the position that each frame line corresponds to.
+        final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm);
+        canvas.drawText(
+                String.format(mDefaultLocale, "%.1f",
+                        mFrameCenterPosition + mFrameCenterToBorderDistance),
+                leftX,
+                topY - frameTextOffset, mFrameTextPaint
+        );
+        canvas.drawText(
+                String.format(mDefaultLocale, "%.1f", mFrameCenterPosition),
+                leftX,
+                middleY - frameTextOffset, mFrameTextPaint
+        );
+        canvas.drawText(
+                String.format(mDefaultLocale, "%.1f",
+                        mFrameCenterPosition - mFrameCenterToBorderDistance),
+                leftX,
+                bottomY - frameTextOffset, mFrameTextPaint
+        );
+
+        // If there are no graph values to be drawn, stop here.
+        if (mGraphValues.getSize() == 0) {
+            return;
+        }
+
+        // Draw the graph using the times and positions.
+        // We start at the most recent value (which should be drawn at the right) and move
+        // to the older values (which should be drawn to the left of more recent ones). Negative
+        // indices are handled by circuling back to the end of the buffer.
+        final long mostRecentTime = mGraphValues.getLast().mTime;
+        float prevCoordX = 0;
+        float prevCoordY = 0;
+        float prevAge = 0;
+        for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) {
+            final GraphValue value = iter.next();
+
+            final int age = (int) (mostRecentTime - value.mTime);
+            final float pos = value.mPos;
+
+            // We get the horizontal coordinate in time units from left to right with
+            // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas
+            // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL)
+            // and by multiplying it by the canvas length (rightX - leftX). Finally, we
+            // offset the coordinate by adding it to leftX.
+            final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age)
+                    / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX);
+
+            // We get the vertical coordinate in position units from middle to top with
+            // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas
+            // units by dividing it by half of the position-domain length
+            // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas
+            // length (middleY - topY). Finally, we offset the coordinate by subtracting
+            // it from middleY (we can't "add" here because the coordinate grows from top
+            // to bottom).
+            final float coordY = middleY - ((pos - mFrameCenterPosition)
+                    / mFrameCenterToBorderDistance) * (middleY - topY);
+
+            // Draw a point for this value.
+            canvas.drawPoint(coordX, coordY, mGraphPointPaint);
+
+            // If this value is part of the same gesture as the previous one, draw a line
+            // between them. We ignore the first value (with age = 0).
+            if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) {
+                canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint);
+            }
+
+            prevCoordX = coordX;
+            prevCoordY = coordY;
+            prevAge = age;
+        }
+    }
+
+    public float getFrameCenterPosition() {
+        return mFrameCenterPosition;
+    }
+
+    /**
+     * Converts a dimension in scaled pixel units to integer display pixels.
+     */
+    private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
+        return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
+    }
+
+    /**
+     * Holds data needed to draw each entry in the graph.
+     */
+    private static class GraphValue {
+        /** Position. */
+        float mPos;
+        /** Time when this value was added. */
+        long mTime;
+
+        GraphValue(float pos, long time) {
+            this.mPos = pos;
+            this.mTime = time;
+        }
+    }
+
+    /**
+     * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the
+     * old values with new ones to avoid creating new objects.
+     */
+    private static class CyclicBuffer {
+        private final GraphValue[] mValues;
+        private final int mCapacity;
+        private int mSize = 0;
+        private int mLastIndex = 0;
+
+        // The iteration index and counter are here to make it easier to reset them.
+        /** Determines the value currently pointed by the iterator. */
+        private int mIteratorIndex;
+        /** Counts how many values have been iterated through. */
+        private int mIteratorCount;
+
+        /** Used traverse the values in reverse order. */
+        private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() {
+            @Override
+            public boolean hasNext() {
+                return mIteratorCount <= mSize;
+            }
+
+            @Override
+            public GraphValue next() {
+                // Returns the value currently pointed by the iterator and moves the iterator to
+                // the previous one.
+                mIteratorCount++;
+                return mValues[(mIteratorIndex-- + mCapacity) % mCapacity];
+            }
+        };
+
+        CyclicBuffer(int capacity) {
+            mCapacity = capacity;
+            mValues = new GraphValue[capacity];
+        }
+
+        /**
+         * Add new graph value. If there is an existing object, we replace its data with the
+         * new one. With this, we re-use old objects instead of creating new ones.
+         */
+        void add(float pos, long time) {
+            mLastIndex = (mLastIndex + 1) % mCapacity;
+            if (mValues[mLastIndex] == null) {
+                mValues[mLastIndex] = new GraphValue(pos, time);
+            } else {
+                final GraphValue oldValue = mValues[mLastIndex];
+                oldValue.mPos = pos;
+                oldValue.mTime = time;
+            }
+
+            // If needed, account for new value in the buffer size.
+            if (mSize != mCapacity) {
+                mSize++;
+            }
+        }
+
+        int getSize() {
+            return mSize;
+        }
+
+        GraphValue getFirst() {
+            final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1;
+            final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity;
+            return mValues[firstIndex];
+        }
+
+        GraphValue getLast() {
+            return mValues[mLastIndex];
+        }
+
+        void removeFirst() {
+            mSize--;
+        }
+
+        /** Returns an iterator pointing at the last value. */
+        Iterator<GraphValue> reverseIterator() {
+            mIteratorIndex = mLastIndex;
+            mIteratorCount = 1;
+            return mReverseIterator;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/input/debug/RotaryInputValueView.java b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java
new file mode 100644
index 0000000..9fadac5
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java
@@ -0,0 +1,103 @@
+/*
+ * 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.input.debug;
+
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
+import android.content.Context;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Typeface;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.util.Locale;
+
+/**
+ * Draws the most recent rotary input value and indicates whether the source is active.
+ */
+public class RotaryInputValueView extends TextView {
+
+    private static final int INACTIVE_TEXT_COLOR = 0xffff00ff;
+    private static final int ACTIVE_TEXT_COLOR = 0xff420f28;
+    private static final int TEXT_SIZE_SP = 8;
+    private static final int SIDE_PADDING_SP = 4;
+    /** Determines how long the active status lasts. */
+    private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */;
+    private static final ColorFilter ACTIVE_BACKGROUND_FILTER =
+            new ColorMatrixColorFilter(new float[]{
+                    0, 0, 0, 0, 255, // red
+                    0, 0, 0, 0,   0, // green
+                    0, 0, 0, 0, 255, // blue
+                    0, 0, 0, 0, 200  // alpha
+            });
+
+    private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false);
+    private final float mScaledVerticalScrollFactor;
+    private final Locale mDefaultLocale = Locale.getDefault();
+
+    public RotaryInputValueView(Context c) {
+        super(c);
+
+        DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+        mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+
+        setText(getFormattedValue(0));
+        setTextColor(INACTIVE_TEXT_COLOR);
+        setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm));
+        setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0,
+                applyDimensionSp(SIDE_PADDING_SP, dm), 0);
+        setTypeface(null, Typeface.BOLD);
+        setBackgroundResource(R.drawable.focus_event_rotary_input_background);
+    }
+
+    /** Updates the shown text with the formatted value. */
+    public void updateValue(float value) {
+        removeCallbacks(mUpdateActivityStatusCallback);
+
+        setText(getFormattedValue(value * mScaledVerticalScrollFactor));
+
+        updateActivityStatus(true);
+        postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION);
+    }
+
+    /** Updates whether or not there's active rotary input. */
+    public void updateActivityStatus(boolean active) {
+        if (active) {
+            setTextColor(ACTIVE_TEXT_COLOR);
+            getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER);
+        } else {
+            setTextColor(INACTIVE_TEXT_COLOR);
+            getBackground().clearColorFilter();
+        }
+    }
+
+    private String getFormattedValue(float value) {
+        return String.format(mDefaultLocale, "%s%.1f", value < 0 ? "-" : "+", Math.abs(value));
+    }
+
+    /**
+     * Converts a dimension in scaled pixel units to integer display pixels.
+     */
+    private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
+        return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 7726f40..dbbbed3 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -57,13 +57,13 @@
                 InputConfig.NOT_FOCUSABLE
                         | InputConfig.NOT_TOUCHABLE
                         | InputConfig.SPY
-                        | InputConfig.INTERCEPTS_STYLUS
-                        | InputConfig.TRUSTED_OVERLAY;
+                        | InputConfig.INTERCEPTS_STYLUS;
 
         // Configure the surface to receive stylus events across the entire display.
         mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
         t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 93c66a1..08cf3f7 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1431,13 +1431,17 @@
                 mContextHubWrapper.onBtMainSettingChanged(btEnabled);
             }
         } else {
-            Log.d(TAG, "BT adapter not available. Defaulting to disabled");
-            if (forceUpdate || mIsBtMainEnabled) {
-                mIsBtMainEnabled = false;
+            Log.d(TAG, "BT adapter not available. Getting permissions from user settings");
+            boolean btEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.BLUETOOTH_ON, 0) == 1;
+            boolean btScanEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
+            if (forceUpdate || mIsBtMainEnabled != btEnabled) {
+                mIsBtMainEnabled = btEnabled;
                 mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled);
             }
-            if (forceUpdate || mIsBtScanningEnabled) {
-                mIsBtScanningEnabled = false;
+            if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) {
+                mIsBtScanningEnabled = btScanEnabled;
                 mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled);
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
index 4335a1d..e1a0707 100644
--- a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
+++ b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
@@ -29,7 +29,12 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 
-import java.util.Calendar;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
 
 /**
  * This service runs everyday at 2am local time to remove expired bitmaps.
@@ -69,26 +74,25 @@
      * @return Milliseconds until the next time the job should run.
      */
     private static long getRunAfterMs() {
-        Calendar cal = Calendar.getInstance();  // Uses local time zone
-        final long now = cal.getTimeInMillis();
+        ZoneId zoneId = ZoneId.systemDefault();
+        ZonedDateTime now = Instant.now().atZone(zoneId);
 
-        cal.set(Calendar.HOUR_OF_DAY, 2);
-        cal.set(Calendar.MINUTE, 0);
-        cal.set(Calendar.MILLISECOND, 0);
-        final long today2AM = cal.getTimeInMillis();
+        LocalDate today = now.toLocalDate();
+        LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0);
 
-        cal.add(Calendar.DAY_OF_YEAR, 1);
-        final long tomorrow2AM = cal.getTimeInMillis();
+        ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId);
+        ZonedDateTime tomorrow2AM = today2AM.plusDays(1);
 
         return getTimeUntilRemoval(now, today2AM, tomorrow2AM);
     }
 
     @VisibleForTesting
-    static long getTimeUntilRemoval(long now, long today2AM, long tomorrow2AM) {
-        if (now < today2AM) {
-            return today2AM - now;
+    static long getTimeUntilRemoval(ZonedDateTime now, ZonedDateTime today2AM,
+                                    ZonedDateTime tomorrow2AM) {
+        if (Duration.between(now, today2AM).isNegative()) {
+            return Duration.between(now, tomorrow2AM).toMillis();
         }
-        return tomorrow2AM - now;
+        return Duration.between(now, today2AM).toMillis();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java
index 3776ad7..c9317d1 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java
@@ -27,6 +27,7 @@
 import android.os.CancellationSignal;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 
 import java.util.concurrent.TimeUnit;
@@ -77,5 +78,11 @@
         }
         return false;
     }
+
+    @Override
+    @VisibleForTesting
+    protected void attachBaseContext(Context base) {
+        super.attachBaseContext(base);
+    }
 }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a3c71c2..87c3067 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -396,7 +396,7 @@
     static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7;
     static final int MESSAGE_ON_PACKAGE_CHANGED = 8;
 
-    static final long BITMAP_EXPIRATION_TIME_MS = TimeUnit.HOURS.toMillis(24);
+    static final Duration BITMAP_DURATION = Duration.ofHours(24);
 
     // ranking thread messages
     private static final int MESSAGE_RECONSIDER_RANKING = 1000;
@@ -541,6 +541,13 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
     private static final long NOTIFICATION_LOG_ASSISTANT_CANCEL = 195579280L;
 
+    /**
+     * NO_CLEAR flag will be set for any media notification.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L;
+
     private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
 
     private IActivityManager mAm;
@@ -6698,7 +6705,7 @@
                     final long timePostedMs = r.getSbn().getPostTime();
                     final long timeNowMs = System.currentTimeMillis();
 
-                    if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_EXPIRATION_TIME_MS)) {
+                    if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) {
                         removeBitmapAndRepost(r);
                     }
                 }
@@ -7189,6 +7196,12 @@
                             + "MEDIA_CONTENT_CONTROL permission");
                 }
             }
+
+            // Enforce NO_CLEAR flag on MediaStyle notification for apps with targetSdk >= V.
+            if (CompatChanges.isChangeEnabled(ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION,
+                    notificationUid)) {
+                notification.flags |= Notification.FLAG_NO_CLEAR;
+            }
         }
 
         // Ensure only allowed packages have a substitute app name
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 82d3e91..68a8e40 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -286,6 +286,8 @@
 
     private static final int USER_VERSION = 11;
 
+    private static final int MAX_USER_STRING_LENGTH = 500;
+
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
     static final int WRITE_USER_MSG = 1;
@@ -4374,15 +4376,17 @@
         // Write seed data
         if (userData.persistSeedData) {
             if (userData.seedAccountName != null) {
-                serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName);
+                serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME,
+                        truncateString(userData.seedAccountName));
             }
             if (userData.seedAccountType != null) {
-                serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType);
+                serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE,
+                        truncateString(userData.seedAccountType));
             }
         }
         if (userInfo.name != null) {
             serializer.startTag(null, TAG_NAME);
-            serializer.text(userInfo.name);
+            serializer.text(truncateString(userInfo.name));
             serializer.endTag(null, TAG_NAME);
         }
         synchronized (mRestrictionsLock) {
@@ -4431,6 +4435,13 @@
         serializer.endDocument();
     }
 
+    private String truncateString(String original) {
+        if (original == null || original.length() <= MAX_USER_STRING_LENGTH) {
+            return original;
+        }
+        return original.substring(0, MAX_USER_STRING_LENGTH);
+    }
+
     /*
      * Writes the user list file in this format:
      *
@@ -4869,7 +4880,7 @@
             @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages,
             @NonNull TimingsTraceAndSlog t, @Nullable Object token)
             throws UserManager.CheckedUserOperationException {
-
+        String truncatedName = truncateString(name);
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         if (userTypeDetails == null) {
             throwCheckedUserOperationException(
@@ -4904,8 +4915,8 @@
 
         // Try to use a pre-created user (if available).
         if (!preCreate && parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails)) {
-            final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name,
-                    token);
+            final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags,
+                    truncatedName, token);
             if (preCreatedUser != null) {
                 return preCreatedUser;
             }
@@ -5000,7 +5011,7 @@
                         flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE;
                     }
 
-                    userInfo = new UserInfo(userId, name, null, flags, userType);
+                    userInfo = new UserInfo(userId, truncatedName, null, flags, userType);
                     userInfo.serialNumber = mNextSerialNumber++;
                     userInfo.creationTime = getCreationTime();
                     userInfo.partial = true;
@@ -6456,8 +6467,8 @@
                     Slog.e(LOG_TAG, "No such user for settings seed data u=" + userId);
                     return;
                 }
-                userData.seedAccountName = accountName;
-                userData.seedAccountType = accountType;
+                userData.seedAccountName = truncateString(accountName);
+                userData.seedAccountType = truncateString(accountType);
                 userData.seedAccountOptions = accountOptions;
                 userData.persistSeedData = persist;
             }
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index ebc7163..b83421f 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -42,6 +42,7 @@
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.permission.flags.Flags;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
 import android.text.TextUtils;
@@ -75,9 +76,6 @@
     private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED =
             SystemProperties.getBoolean("ro.hotword.detection_service_required", false);
 
-    //TODO(b/289087412): import this from the flag value in set up in device config.
-    private static final boolean IS_VOICE_ACTIVATION_OP_ENABLED = false;
-
     @NonNull
     private final Object mLock = new Object();
 
@@ -212,7 +210,7 @@
      * @return the op that should be noted for the voice activations of the app by detected hotword.
      */
     public static int getVoiceActivationOp() {
-        if (IS_VOICE_ACTIVATION_OP_ENABLED) {
+        if (Flags.voiceActivationPermissionApis()) {
             return OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
         }
         return OP_RECORD_AUDIO_HOTWORD;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 097656c..3a6664a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -333,6 +333,11 @@
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
 
+    // must match: config_shortPressOnSettingsBehavior in config.xml
+    static final int SHORT_PRESS_SETTINGS_NOTHING = 0;
+    static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1;
+    static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
+
     static final int PENDING_KEY_NULL = -1;
 
     // Must match: config_shortPressOnStemPrimaryBehavior in config.xml
@@ -611,6 +616,9 @@
     // What we do when the user double-taps on home
     int mDoubleTapOnHomeBehavior;
 
+    // What we do when the user presses on settings
+    int mShortPressOnSettingsBehavior;
+
     // Must match config_primaryShortPressTargetActivity in config.xml
     ComponentName mPrimaryShortPressTargetActivity;
 
@@ -2766,6 +2774,13 @@
         if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
         }
+
+        mShortPressOnSettingsBehavior = res.getInteger(
+                com.android.internal.R.integer.config_shortPressOnSettingsBehavior);
+        if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING
+                || mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) {
+            mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING;
+        }
     }
 
     private void updateSettings() {
@@ -3632,6 +3647,15 @@
                 Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
                         + " interceptKeyBeforeQueueing");
                 return true;
+            case KeyEvent.KEYCODE_SETTINGS:
+                if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) {
+                    if (!down) {
+                        toggleNotificationPanel();
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
+                    }
+                    return true;
+                }
+                break;
         }
         if (isValidGlobalKey(keyCode)
                 && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
@@ -6272,6 +6296,9 @@
                 pw.print("mLongPressOnPowerBehavior=");
                 pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
         pw.print(prefix);
+        pw.print("mShortPressOnSettingsBehavior=");
+        pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior));
+        pw.print(prefix);
         pw.print("mLongPressOnPowerAssistantTimeoutMs=");
         pw.println(mLongPressOnPowerAssistantTimeoutMs);
         pw.print(prefix);
@@ -6470,6 +6497,17 @@
         }
     }
 
+    private static String shortPressOnSettingsBehaviorToString(int behavior) {
+        switch (behavior) {
+            case SHORT_PRESS_SETTINGS_NOTHING:
+                return "SHORT_PRESS_SETTINGS_NOTHING";
+            case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL:
+                return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
     private static String veryLongPressOnPowerBehaviorToString(int behavior) {
         switch (behavior) {
             case VERY_LONG_PRESS_POWER_NOTHING:
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 3d89afa..becbbf2 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -25,6 +25,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.HapticFeedbackConstants;
+import android.view.flags.FeatureFlags;
+import android.view.flags.FeatureFlagsImpl;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -54,6 +56,7 @@
     // If present and valid, a vibration here will be used for an effect.
     // Otherwise, the system's default vibration will be used.
     @Nullable private final SparseArray<VibrationEffect> mHapticCustomizations;
+    private final FeatureFlags mViewFeatureFlags;
 
     /** @hide */
     public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
@@ -62,14 +65,16 @@
 
     /** @hide */
     public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
-        this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
+        this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo),
+                new FeatureFlagsImpl());
     }
 
     /** @hide */
     @VisibleForTesting HapticFeedbackVibrationProvider(
             Resources res,
             VibratorInfo vibratorInfo,
-            @Nullable SparseArray<VibrationEffect> hapticCustomizations) {
+            @Nullable SparseArray<VibrationEffect> hapticCustomizations,
+            FeatureFlags viewFeatureFlags) {
         mVibratorInfo = vibratorInfo;
         mHapticTextHandleEnabled = res.getBoolean(
                 com.android.internal.R.bool.config_enableHapticTextHandle);
@@ -78,6 +83,7 @@
             hapticCustomizations = null;
         }
         mHapticCustomizations = hapticCustomizations;
+        mViewFeatureFlags = viewFeatureFlags;
 
         mSafeModeEnabledVibrationEffect =
                 effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED)
@@ -201,12 +207,16 @@
             default:
                 attrs = TOUCH_VIBRATION_ATTRIBUTES;
         }
+
+        int flags = 0;
         if (bypassVibrationIntensitySetting) {
-            attrs = new VibrationAttributes.Builder(attrs)
-                    .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
-                    .build();
+            flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
         }
-        return attrs;
+        if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) {
+            flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+        }
+
+        return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
     }
 
     /** Dumps relevant state. */
@@ -295,4 +305,19 @@
             return null;
         }
     }
+
+    private static boolean shouldBypassInterruptionPolicy(
+            int effectId, FeatureFlags viewFeatureFlags) {
+        switch (effectId) {
+            case HapticFeedbackConstants.SCROLL_TICK:
+            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+            case HapticFeedbackConstants.SCROLL_LIMIT:
+                // The SCROLL_* constants should bypass interruption filter, so that scroll haptics
+                // can play regardless of focus modes like DND. Guard this behavior by the feature
+                // flag controlling the general scroll feedback APIs.
+                return viewFeatureFlags.scrollFeedbackApi();
+            default:
+                return false;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index ee3d697..45bd152 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -448,6 +448,7 @@
             String reason, IBinder token) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
         try {
+            attrs = fixupVibrationAttributes(attrs, effect);
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.VIBRATE, "vibrate");
             return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
@@ -457,7 +458,7 @@
     }
 
     HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg,
-            @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+            @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
             String reason, IBinder token) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
         try {
@@ -468,7 +469,7 @@
     }
 
     private HalVibration vibrateInternal(int uid, int displayId, String opPkg,
-            @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+            @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
             String reason, IBinder token) {
         if (token == null) {
             Slog.e(TAG, "token must not be null");
@@ -478,7 +479,6 @@
         if (!isEffectValid(effect)) {
             return null;
         }
-        attrs = fixupVibrationAttributes(attrs, effect);
         // Create Vibration.Stats as close to the received request as possible, for tracking.
         HalVibration vib = new HalVibration(token, effect,
                 new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index 10a2b97..05da9df 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -23,6 +26,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -78,7 +82,14 @@
 
     protected final WindowManagerService mService;
     protected final float mHighResSnapshotScale;
-    private final Rect mTmpRect = new Rect();
+
+    /**
+     * The transition change info of the target to capture screenshot. It is only non-null when
+     * capturing a snapshot with a given change info. It must be cleared after
+     * {@link #recordSnapshotInner} is done.
+     */
+    protected Transition.ChangeInfo mCurrentChangeInfo;
+
     /**
      * Flag indicating whether we are running on an Android TV device.
      */
@@ -137,41 +148,35 @@
     protected abstract boolean use16BitFormat();
 
     /**
-     * This is different than {@link #recordSnapshotInner(TYPE, boolean)} because it doesn't store
+     * This is different than {@link #recordSnapshotInner(TYPE)} because it doesn't store
      * the snapshot to the cache and returns the TaskSnapshot immediately.
      *
      * This is only used for testing so the snapshot content can be verified.
      */
-    // TODO(b/264551777): clean up the "snapshotHome" argument
     @VisibleForTesting
-    TaskSnapshot captureSnapshot(TYPE source, boolean snapshotHome) {
+    TaskSnapshot captureSnapshot(TYPE source) {
         final TaskSnapshot snapshot;
-        if (snapshotHome) {
-            snapshot = snapshot(source);
-        } else {
-            switch (getSnapshotMode(source)) {
-                case SNAPSHOT_MODE_NONE:
-                    return null;
-                case SNAPSHOT_MODE_APP_THEME:
-                    snapshot = drawAppThemeSnapshot(source);
-                    break;
-                case SNAPSHOT_MODE_REAL:
-                    snapshot = snapshot(source);
-                    break;
-                default:
-                    snapshot = null;
-                    break;
-            }
+        switch (getSnapshotMode(source)) {
+            case SNAPSHOT_MODE_NONE:
+                return null;
+            case SNAPSHOT_MODE_APP_THEME:
+                snapshot = drawAppThemeSnapshot(source);
+                break;
+            case SNAPSHOT_MODE_REAL:
+                snapshot = snapshot(source);
+                break;
+            default:
+                snapshot = null;
+                break;
         }
         return snapshot;
     }
 
-    final TaskSnapshot recordSnapshotInner(TYPE source, boolean allowSnapshotHome) {
+    final TaskSnapshot recordSnapshotInner(TYPE source) {
         if (shouldDisableSnapshots()) {
             return null;
         }
-        final boolean snapshotHome = allowSnapshotHome && source.isActivityTypeHome();
-        final TaskSnapshot snapshot = captureSnapshot(source, snapshotHome);
+        final TaskSnapshot snapshot = captureSnapshot(source);
         if (snapshot == null) {
             return null;
         }
@@ -189,30 +194,32 @@
 
     @VisibleForTesting
     int getSnapshotMode(TYPE source) {
-        final ActivityRecord topChild = getTopActivity(source);
-        if (!source.isActivityTypeStandardOrUndefined() && !source.isActivityTypeAssistant()) {
+        final int type = source.getActivityType();
+        if (type == ACTIVITY_TYPE_RECENTS || type == ACTIVITY_TYPE_DREAM) {
             return SNAPSHOT_MODE_NONE;
-        } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
-            return SNAPSHOT_MODE_APP_THEME;
-        } else {
+        }
+        if (type == ACTIVITY_TYPE_HOME) {
             return SNAPSHOT_MODE_REAL;
         }
+        final ActivityRecord topChild = getTopActivity(source);
+        if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
+            return SNAPSHOT_MODE_APP_THEME;
+        }
+        return SNAPSHOT_MODE_REAL;
     }
 
     @Nullable
     TaskSnapshot snapshot(TYPE source) {
-        return snapshot(source, PixelFormat.UNKNOWN);
-    }
-
-    @Nullable
-    TaskSnapshot snapshot(TYPE source, int pixelFormat) {
         TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
-        if (!prepareTaskSnapshot(source, pixelFormat, builder)) {
+        final Rect crop = prepareTaskSnapshot(source, builder);
+        if (crop == null) {
             // Failed some pre-req. Has been logged.
             return null;
         }
-        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
-                createSnapshot(source, builder);
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot");
+        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source,
+                mHighResSnapshotScale, crop, builder);
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         if (screenshotBuffer == null) {
             // Failed to acquire image. Has been logged.
             return null;
@@ -225,37 +232,13 @@
 
     @Nullable
     ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source,
-            TaskSnapshot.Builder builder) {
-        Point taskSize = new Point();
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot");
-        final ScreenCapture.ScreenshotHardwareBuffer taskSnapshot = createSnapshot(source,
-                mHighResSnapshotScale, builder.getPixelFormat(), taskSize, builder);
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-        builder.setTaskSize(taskSize);
-        return taskSnapshot;
-    }
-
-    @Nullable
-    ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source,
-            float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) {
+            float scaleFraction, Rect crop, TaskSnapshot.Builder builder) {
         if (source.getSurfaceControl() == null) {
             if (DEBUG_SCREENSHOT) {
                 Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + source);
             }
             return null;
         }
-        mTmpRect.setEmpty();
-        if (source.mTransitionController.inFinishingTransition(source)) {
-            final Transition.ChangeInfo changeInfo = source.mTransitionController
-                    .mFinishingTransition.mChanges.get(source);
-            if (changeInfo != null) {
-                mTmpRect.set(changeInfo.mAbsoluteBounds);
-            }
-        }
-        if (mTmpRect.isEmpty()) {
-            source.getBounds(mTmpRect);
-        }
-        mTmpRect.offsetTo(0, 0);
         SurfaceControl[] excludeLayers;
         final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow;
         // Exclude IME window snapshot when IME isn't proper to attach to app.
@@ -281,12 +264,8 @@
         builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());
         final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                 ScreenCapture.captureLayersExcluding(
-                        source.getSurfaceControl(), mTmpRect, scaleFraction,
-                        pixelFormat, excludeLayers);
-        if (outTaskSize != null) {
-            outTaskSize.x = mTmpRect.width();
-            outTaskSize.y = mTmpRect.height();
-        }
+                        source.getSurfaceControl(), crop, scaleFraction,
+                        builder.getPixelFormat(), excludeLayers);
         final HardwareBuffer buffer = screenshotBuffer == null ? null
                 : screenshotBuffer.getHardwareBuffer();
         if (isInvalidHardwareBuffer(buffer)) {
@@ -305,17 +284,16 @@
      * information from the task and populates the builder.
      *
      * @param source the window to capture
-     * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
-     *                    automatically select
      * @param builder the snapshot builder to populate
      *
      * @return true if the state of the task is ok to proceed
      */
     @VisibleForTesting
-    boolean prepareTaskSnapshot(TYPE source, int pixelFormat, TaskSnapshot.Builder builder) {
+    @Nullable
+    Rect prepareTaskSnapshot(TYPE source, TaskSnapshot.Builder builder) {
         final Pair<ActivityRecord, WindowState> result = checkIfReadyToSnapshot(source);
         if (result == null) {
-            return false;
+            return null;
         }
         final ActivityRecord activity = result.first;
         final WindowState mainWindow = result.second;
@@ -329,6 +307,7 @@
         builder.setLetterboxInsets(letterboxInsets);
         final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
         final boolean isShowWallpaper = mainWindow.hasWallpaper();
+        int pixelFormat = builder.getPixelFormat();
         if (pixelFormat == PixelFormat.UNKNOWN) {
             pixelFormat = use16BitFormat() && activity.fillsParent()
                     && !(isWindowTranslucent && isShowWallpaper)
@@ -340,11 +319,29 @@
         builder.setTopActivityComponent(activity.mActivityComponent);
         builder.setPixelFormat(pixelFormat);
         builder.setIsTranslucent(isTranslucent);
-        builder.setOrientation(activity.getTask().getConfiguration().orientation);
-        builder.setRotation(activity.getTask().getDisplayContent().getRotation());
         builder.setWindowingMode(source.getWindowingMode());
         builder.setAppearance(getAppearance(source));
-        return true;
+
+        final Configuration taskConfig = activity.getTask().getConfiguration();
+        final int displayRotation = taskConfig.windowConfiguration.getDisplayRotation();
+        final Rect outCrop = new Rect();
+        final Transition.ChangeInfo changeInfo = mCurrentChangeInfo;
+        if (changeInfo != null && changeInfo.mRotation != displayRotation) {
+            // For example, the source is closing and display rotation changes at the same time.
+            // The snapshot should record the state in previous rotation.
+            outCrop.set(changeInfo.mAbsoluteBounds);
+            builder.setRotation(changeInfo.mRotation);
+            builder.setOrientation(changeInfo.mAbsoluteBounds.height()
+                    >= changeInfo.mAbsoluteBounds.width()
+                    ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE);
+        } else {
+            outCrop.set(taskConfig.windowConfiguration.getBounds());
+            builder.setRotation(displayRotation);
+            builder.setOrientation(taskConfig.orientation);
+        }
+        outCrop.offsetTo(0, 0);
+        builder.setTaskSize(new Point(outCrop.right, outCrop.bottom));
+        return outCrop;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 148bf9b..01b8bf7 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -280,7 +280,7 @@
         if (DEBUG) {
             Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
         }
-        final TaskSnapshot snapshot = recordSnapshotInner(activity, false /* allowSnapshotHome */);
+        final TaskSnapshot snapshot = recordSnapshotInner(activity);
         if (snapshot != null) {
             final int code = getSystemHashCode(activity);
             addUserSavedFile(code, activity.mUserId, snapshot);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index c39b266..4a5311b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -577,7 +577,8 @@
                 .getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
         if (rootTask == null) return false;
         final ActivityRecord r = rootTask.topRunningActivity();
-        if (r == null || r.isVisibleRequested() || !r.attachedToProcess()
+        if (r == null || (r.isVisibleRequested() && rootTask.isTopRootTaskInDisplayArea())
+                || !r.attachedToProcess()
                 || !r.mActivityComponent.equals(intent.getComponent())
                 || !mService.isCallerRecents(r.getUid())
                 // Recents keeps invisible while device is locked.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a450b4d..0b67321 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3858,11 +3858,9 @@
                     return null;
                 }
                 if (updateCache) {
-                    return mWindowManager.mTaskSnapshotController.recordSnapshot(task,
-                            true /* snapshotHome */);
+                    return mWindowManager.mTaskSnapshotController.recordSnapshot(task);
                 } else {
-                    return mWindowManager.mTaskSnapshotController.captureSnapshot(task,
-                            true /* snapshotHome */);
+                    return mWindowManager.mTaskSnapshotController.captureSnapshot(task);
                 }
             }
         } finally {
@@ -6450,19 +6448,8 @@
                 if (!restarting && hasVisibleActivities) {
                     deferWindowLayout();
                     try {
-                        final Task topTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
-                        if (topTask != null
-                                && topTask.topRunningActivity(true /* focusableOnly */) == null) {
-                            topTask.adjustFocusToNextFocusableTask("handleAppDied");
-                        }
-                        if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) {
-                            // If there was nothing to resume, and we are not already restarting
-                            // this process, but there is a visible activity that is hosted by the
-                            // process...then make sure all visible activities are running, taking
-                            // care of restarting this process.
-                            mRootWindowContainer.ensureActivitiesVisible(null, 0,
-                                    !PRESERVE_WINDOWS);
-                        }
+                        mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(
+                                "handleAppDied");
                     } finally {
                         continueWindowLayout();
                     }
@@ -6903,7 +6890,18 @@
         @Override
         public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
             synchronized (mGlobalLock) {
-                return mRootWindowContainer.finishTopCrashedActivities(crashedApp, reason);
+                deferWindowLayout();
+                try {
+                    final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities(
+                            crashedApp, reason);
+                    if (finishedTask != null) {
+                        mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(reason);
+                        return finishedTask.mTaskId;
+                    }
+                    return INVALID_TASK_ID;
+                } finally {
+                    continueWindowLayout();
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c2885da..4237668 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -163,7 +163,8 @@
 
             if (window == null) {
                 EmbeddedWindowController.EmbeddedWindow embeddedWindow =
-                        wmService.mEmbeddedWindowController.getByFocusToken(focusedWindowToken);
+                        wmService.mEmbeddedWindowController.getByInputTransferToken(
+                                focusedWindowToken);
                 if (embeddedWindow != null) {
                     ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
                             "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK.");
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 2439159..73bcc8d 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -66,29 +66,29 @@
 
 public final class CompatModePackages {
     /**
-     * {@link CompatModePackages#DOWNSCALED_INVERSE} is the gatekeeper of all per-app buffer inverse
-     * downscale changes. Enabling this change will allow the following scaling factors:
-     * {@link CompatModePackages#DOWNSCALE_90}
-     * {@link CompatModePackages#DOWNSCALE_85}
-     * {@link CompatModePackages#DOWNSCALE_80}
-     * {@link CompatModePackages#DOWNSCALE_75}
-     * {@link CompatModePackages#DOWNSCALE_70}
-     * {@link CompatModePackages#DOWNSCALE_65}
-     * {@link CompatModePackages#DOWNSCALE_60}
-     * {@link CompatModePackages#DOWNSCALE_55}
-     * {@link CompatModePackages#DOWNSCALE_50}
-     * {@link CompatModePackages#DOWNSCALE_45}
-     * {@link CompatModePackages#DOWNSCALE_40}
-     * {@link CompatModePackages#DOWNSCALE_35}
-     * {@link CompatModePackages#DOWNSCALE_30}
+     * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is the gatekeeper of all per-app buffer
+     * inverse downscale changes. Enabling this change will allow the following scaling factors:
+     * <a href="#DOWNSCALE_90">DOWNSCALE_90</a>
+     * <a href="#DOWNSCALE_85">DOWNSCALE_85</a>
+     * <a href="#DOWNSCALE_80">DOWNSCALE_80</a>
+     * <a href="#DOWNSCALE_75">DOWNSCALE_75</a>
+     * <a href="#DOWNSCALE_70">DOWNSCALE_70</a>
+     * <a href="#DOWNSCALE_65">DOWNSCALE_65</a>
+     * <a href="#DOWNSCALE_60">DOWNSCALE_60</a>
+     * <a href="#DOWNSCALE_55">DOWNSCALE_55</a>
+     * <a href="#DOWNSCALE_50">DOWNSCALE_50</a>
+     * <a href="#DOWNSCALE_45">DOWNSCALE_45</a>
+     * <a href="#DOWNSCALE_40">DOWNSCALE_40</a>
+     * <a href="#DOWNSCALE_35">DOWNSCALE_35</a>
+     * <a href="#DOWNSCALE_30">DOWNSCALE_30</a>
      *
-     * If {@link CompatModePackages#DOWNSCALED_INVERSE} is enabled for an app package, then the app
-     * will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both 1/0.8 and
-     * 1/0.7 (* 100%) were enabled.
+     * If <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is enabled for an app package, then
+     * the app will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both
+     * 1/0.8 and 1/0.7 (* 100%) were enabled.
      *
-     * When both {@link CompatModePackages#DOWNSCALED_INVERSE}
-     * and {@link CompatModePackages#DOWNSCALED} are enabled, then
-     * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence.
+     * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a>
+     * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then
+     * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence.
      */
     @ChangeId
     @Disabled
@@ -96,29 +96,29 @@
     public static final long DOWNSCALED_INVERSE = 273564678L; // This is a Bug ID.
 
     /**
-     * {@link CompatModePackages#DOWNSCALED} is the gatekeeper of all per-app buffer downscaling
+     * <a href="#DOWNSCALED">DOWNSCALED</a> is the gatekeeper of all per-app buffer downscaling
      * changes. Enabling this change will allow the following scaling factors:
-     * {@link CompatModePackages#DOWNSCALE_90}
-     * {@link CompatModePackages#DOWNSCALE_85}
-     * {@link CompatModePackages#DOWNSCALE_80}
-     * {@link CompatModePackages#DOWNSCALE_75}
-     * {@link CompatModePackages#DOWNSCALE_70}
-     * {@link CompatModePackages#DOWNSCALE_65}
-     * {@link CompatModePackages#DOWNSCALE_60}
-     * {@link CompatModePackages#DOWNSCALE_55}
-     * {@link CompatModePackages#DOWNSCALE_50}
-     * {@link CompatModePackages#DOWNSCALE_45}
-     * {@link CompatModePackages#DOWNSCALE_40}
-     * {@link CompatModePackages#DOWNSCALE_35}
-     * {@link CompatModePackages#DOWNSCALE_30}
+     * <a href="#DOWNSCALE_90">DOWNSCALE_90</a>
+     * <a href="#DOWNSCALE_85">DOWNSCALE_85</a>
+     * <a href="#DOWNSCALE_80">DOWNSCALE_80</a>
+     * <a href="#DOWNSCALE_75">DOWNSCALE_75</a>
+     * <a href="#DOWNSCALE_70">DOWNSCALE_70</a>
+     * <a href="#DOWNSCALE_65">DOWNSCALE_65</a>
+     * <a href="#DOWNSCALE_60">DOWNSCALE_60</a>
+     * <a href="#DOWNSCALE_55">DOWNSCALE_55</a>
+     * <a href="#DOWNSCALE_50">DOWNSCALE_50</a>
+     * <a href="#DOWNSCALE_45">DOWNSCALE_45</a>
+     * <a href="#DOWNSCALE_40">DOWNSCALE_40</a>
+     * <a href="#DOWNSCALE_35">DOWNSCALE_35</a>
+     * <a href="#DOWNSCALE_30">DOWNSCALE_30</a>
      *
-     * If {@link CompatModePackages#DOWNSCALED} is enabled for an app package, then the app will be
+     * If <a href="#DOWNSCALED">DOWNSCALED</a> is enabled for an app package, then the app will be
      * forcibly resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were
      * enabled.
      *
-     * When both {@link CompatModePackages#DOWNSCALED_INVERSE}
-     * and {@link CompatModePackages#DOWNSCALED} are enabled, then
-     * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence.
+     * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a>
+     * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then
+     * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence.
      */
     @ChangeId
     @Disabled
@@ -126,12 +126,13 @@
     public static final long DOWNSCALED = 168419799L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_90} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> for a package will force the app to assume it's
      * running on a display with 90% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 111.11% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 111.11% the vertical and horizontal resolution of
+     * the real display
      */
     @ChangeId
     @Disabled
@@ -139,12 +140,13 @@
     public static final long DOWNSCALE_90 = 182811243L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_85} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> for a package will force the app to assume it's
      * running on a display with 85% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 117.65% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 117.65% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -152,12 +154,13 @@
     public static final long DOWNSCALE_85 = 189969734L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_80} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> for a package will force the app to assume it's
      * running on a display with 80% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 125% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 125% the vertical and horizontal resolution of the real
+     * display
      */
     @ChangeId
     @Disabled
@@ -165,12 +168,13 @@
     public static final long DOWNSCALE_80 = 176926753L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_75} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> for a package will force the app to assume it's
      * running on a display with 75% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 133.33% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 133.33% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -178,12 +182,13 @@
     public static final long DOWNSCALE_75 = 189969779L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_70} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> for a package will force the app to assume it's
      * running on a display with 70% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 142.86% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 142.86% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -191,12 +196,13 @@
     public static final long DOWNSCALE_70 = 176926829L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_65} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> for a package will force the app to assume it's
      * running on a display with 65% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 153.85% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 153.85% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -204,12 +210,13 @@
     public static final long DOWNSCALE_65 = 189969744L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_60} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> for a package will force the app to assume it's
      * running on a display with 60% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 166.67% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 166.67% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -217,12 +224,13 @@
     public static final long DOWNSCALE_60 = 176926771L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_55} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> for a package will force the app to assume it's
      * running on a display with 55% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 181.82% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 181.82% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -230,12 +238,13 @@
     public static final long DOWNSCALE_55 = 189970036L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_50} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> for a package will force the app to assume it's
      * running on a display with 50% vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 200% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 200% the vertical and horizontal resolution of the real
+     * display
      */
     @ChangeId
     @Disabled
@@ -243,12 +252,13 @@
     public static final long DOWNSCALE_50 = 176926741L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_45} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> for a package will force the app to assume it's
      * running on a display with 45% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 222.22% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 222.22% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -256,12 +266,13 @@
     public static final long DOWNSCALE_45 = 189969782L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_40} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> for a package will force the app to assume it's
      * running on a display with 40% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 250% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 250% the vertical and horizontal resolution of the real
+     * display
      */
     @ChangeId
     @Disabled
@@ -269,12 +280,13 @@
     public static final long DOWNSCALE_40 = 189970038L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_35} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> for a package will force the app to assume it's
      * running on a display with 35% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 285.71% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 285.71% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
@@ -282,12 +294,13 @@
     public static final long DOWNSCALE_35 = 189969749L;
 
     /**
-     * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
-     * {@link CompatModePackages#DOWNSCALE_30} for a package will force the app to assume it's
+     * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+     * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> for a package will force the app to assume it's
      * running on a display with 30% the vertical and horizontal resolution of the real display.
      *
-     * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
-     * running on a display with 333.33% the vertical and horizontal resolution of the real display
+     * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+     * assume it's running on a display with 333.33% the vertical and horizontal resolution of the
+     * real display
      */
     @ChangeId
     @Disabled
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0f1a105..7af4aad 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -48,7 +48,6 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
-import android.os.InputConfig;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -186,6 +185,10 @@
         // Crop the input surface to the display size.
         mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
 
+        // Make trusted overlay to not block any touches while D&D ongoing and allowing
+        // touches to pass through to windows underneath. This allows user to interact with the
+        // UI to navigate while dragging.
+        h.setTrustedOverlay(mTransaction, mInputSurface, true);
         mTransaction.show(mInputSurface)
                 .setInputWindowInfo(mInputSurface, h)
                 .setLayer(mInputSurface, Integer.MAX_VALUE)
@@ -377,11 +380,6 @@
             mDragWindowHandle.ownerUid = MY_UID;
             mDragWindowHandle.scaleFactor = 1.0f;
 
-            // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing
-            // touches to pass through to windows underneath. This allows user to interact with the
-            // UI to navigate while dragging.
-            mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY;
-
             // The drag window cannot receive new touches.
             mDragWindowHandle.touchableRegion.setEmpty();
 
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index c9bae12..275396f 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -45,8 +45,8 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
     /* maps input token to an embedded window */
     private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
-    private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
-        new ArrayMap<>();
+    private ArrayMap<IBinder /*input transfer token */, EmbeddedWindow>
+            mWindowsByInputTransferToken = new ArrayMap<>();
     private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken =
         new ArrayMap<>();
     private final Object mGlobalLock;
@@ -67,14 +67,14 @@
     void add(IBinder inputToken, EmbeddedWindow window) {
         try {
             mWindows.put(inputToken, window);
-            final IBinder focusToken = window.getFocusGrantToken();
-            mWindowsByFocusToken.put(focusToken, window);
+            final IBinder inputTransferToken = window.getInputTransferToken();
+            mWindowsByInputTransferToken.put(inputTransferToken, window);
             mWindowsByWindowToken.put(window.getWindowToken(), window);
             updateProcessController(window);
             window.mClient.asBinder().linkToDeath(()-> {
                 synchronized (mGlobalLock) {
                     mWindows.remove(inputToken);
-                    mWindowsByFocusToken.remove(focusToken);
+                    mWindowsByInputTransferToken.remove(inputTransferToken);
                 }
             }, 0);
         } catch (RemoteException e) {
@@ -105,7 +105,7 @@
             EmbeddedWindow ew = mWindows.valueAt(i);
             if (ew.mClient.asBinder() == client.asBinder()) {
                 mWindows.removeAt(i).onRemoved();
-                mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+                mWindowsByInputTransferToken.remove(ew.getInputTransferToken());
                 mWindowsByWindowToken.remove(ew.getWindowToken());
                 return;
             }
@@ -117,7 +117,7 @@
             EmbeddedWindow ew = mWindows.valueAt(i);
             if (ew.mHostWindowState == host) {
                 mWindows.removeAt(i).onRemoved();
-                mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+                mWindowsByInputTransferToken.remove(ew.getInputTransferToken());
                 mWindowsByWindowToken.remove(ew.getWindowToken());
             }
         }
@@ -127,8 +127,8 @@
         return mWindows.get(inputToken);
     }
 
-    EmbeddedWindow getByFocusToken(IBinder focusGrantToken) {
-        return mWindowsByFocusToken.get(focusGrantToken);
+    EmbeddedWindow getByInputTransferToken(IBinder inputTransferToken) {
+        return mWindowsByInputTransferToken.get(inputTransferToken);
     }
 
     EmbeddedWindow getByWindowToken(IBinder windowToken) {
@@ -153,7 +153,7 @@
          * to request focus transfer to the embedded. This is not the input token since we don't
          * want to give clients access to each others input token.
          */
-        private final IBinder mFocusGrantToken;
+        private final IBinder mInputTransferToken;
 
         private boolean mIsFocusable;
 
@@ -171,7 +171,7 @@
          */
         EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
                        WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
-                       int displayId, IBinder focusGrantToken, String inputHandleName,
+                       int displayId, IBinder inputTransferToken, String inputHandleName,
                        boolean isFocusable) {
             mSession = session;
             mWmService = service;
@@ -183,7 +183,7 @@
             mOwnerPid = ownerPid;
             mWindowType = windowType;
             mDisplayId = displayId;
-            mFocusGrantToken = focusGrantToken;
+            mInputTransferToken = inputTransferToken;
             final String hostWindowName =
                     (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
                             : "";
@@ -260,8 +260,8 @@
             return mOwnerUid;
         }
 
-        IBinder getFocusGrantToken() {
-            return mFocusGrantToken;
+        IBinder getInputTransferToken() {
+            return mInputTransferToken;
         }
 
         IBinder getInputChannelToken() {
@@ -290,7 +290,7 @@
                     // Use null session since this is being granted by system server and doesn't
                     // require the host session to be passed in
                     mWmService.grantEmbeddedWindowFocus(null, mHostWindowState.mClient,
-                            mFocusGrantToken, grantFocus);
+                            mInputTransferToken, grantFocus);
                     if (grantFocus) {
                         // If granting focus to the embedded when tapped, we need to ensure the host
                         // gains focus as well or the transfer won't take effect since it requires
@@ -298,7 +298,7 @@
                         mHostWindowState.handleTapOutsideFocusInsideSelf();
                     }
                 } else {
-                    mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+                    mWmService.grantEmbeddedWindowFocus(mSession, mInputTransferToken, grantFocus);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 39622c1..c21930d 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -74,7 +74,7 @@
         mWindowHandle.ownerPid = WindowManagerService.MY_PID;
         mWindowHandle.ownerUid = WindowManagerService.MY_UID;
         mWindowHandle.scaleFactor = 1.0f;
-        mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
+        mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
 
         mInputSurface = mService.makeSurfaceBuilder(
                         mService.mRoot.getDisplayContent(displayId).getSession())
@@ -129,12 +129,14 @@
 
     void show(SurfaceControl.Transaction t, WindowContainer w) {
         t.show(mInputSurface);
+        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
     }
 
     void show(SurfaceControl.Transaction t, int layer) {
         t.show(mInputSurface);
+        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, layer);
     }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 825d38b..af307ec3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -675,6 +675,11 @@
                     w.getKeyInterceptionInfo());
 
             if (w.mWinAnimator.hasSurface()) {
+                // Update trusted overlay changes here because they are tied to input info. Input
+                // changes can be updated even if surfaces aren't.
+                inputWindowHandle.setTrustedOverlay(mInputTransaction,
+                        w.mWinAnimator.mSurfaceController.mSurfaceControl,
+                        w.isWindowTrustedOverlay());
                 populateInputWindowHandle(inputWindowHandle, w);
                 setInputWindowInfoIfNeeded(mInputTransaction,
                         w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
@@ -732,7 +737,7 @@
                 new InputWindowHandle(null /* inputApplicationHandle */, displayId));
         inputWindowHandle.setName(name);
         inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
-        inputWindowHandle.setTrustedOverlay(true);
+        inputWindowHandle.setTrustedOverlay(t, sc, true);
         populateOverlayInputInfo(inputWindowHandle);
         setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
     }
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 64b7a60..90d81bd 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -195,6 +195,11 @@
         mChanged = true;
     }
 
+    void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
+            boolean trustedOverlay) {
+        mHandle.setTrustedOverlay(t, sc, trustedOverlay);
+    }
+
     void setOwnerPid(int pid) {
         if (mHandle.ownerPid == pid) {
             return;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index def32a1..82d4b90 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,6 +62,7 @@
 import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -244,7 +245,8 @@
         }
 
         @Override
-        public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
+        public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint,
+                IResultReceiver finishCb) {
             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                     "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
             final long token = Binder.clearCallingIdentity();
@@ -257,6 +259,13 @@
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
+            if (finishCb != null) {
+                try {
+                    finishCb.send(0, new Bundle());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to report animation finished", e);
+                }
+            }
         }
 
         @Override
@@ -901,7 +910,8 @@
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
             final Task task = adapter.mTask;
-            snapshotController.recordSnapshot(task, false /* allowSnapshotHome */);
+            if (task.isActivityTypeHome()) continue;
+            snapshotController.recordSnapshot(task);
             final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId,
                     false /* restoreFromDisk */, false /* isLowResolution */);
             if (snapshot != null) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5533759..cf6a1fe 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -60,6 +60,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -2017,6 +2018,13 @@
     void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
             @Nullable ActivityRecord launchIntoPipHostActivity, String reason,
             @Nullable Transition transition) {
+        moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, transition,
+                null /* bounds */);
+    }
+
+    void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
+            @Nullable ActivityRecord launchIntoPipHostActivity, String reason,
+            @Nullable Transition transition, @Nullable Rect bounds) {
         final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
         final Task task = r.getTask();
         final Task rootTask;
@@ -2053,7 +2061,9 @@
             // Defer the windowing mode change until after the transition to prevent the activity
             // from doing work and changing the activity visuals while animating
             // TODO(task-org): Figure-out more structured way to do this long term.
-            r.setWindowingMode(r.getWindowingMode());
+            if (!isPip2ExperimentEnabled()) {
+                r.setWindowingMode(r.getWindowingMode());
+            }
 
             final TaskFragment organizedTf = r.getOrganizedTaskFragment();
             final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
@@ -2169,21 +2179,27 @@
             // TODO(remove-legacy-transit): Move this to the `singleActivity` case when removing
             //                              legacy transit.
             rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+            if (isPip2ExperimentEnabled() && bounds != null) {
+                // set the final pip bounds in advance if pip2 is enabled
+                rootTask.setBounds(bounds);
+            }
+
             // Set the launch bounds for launch-into-pip Activity on the root task.
             if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
                 // Record the snapshot now, it will be later fetched for content-pip animation.
                 // We do this early in the process to make sure the right snapshot is used for
                 // entering content-pip animation.
-                mWindowManager.mTaskSnapshotController.recordSnapshot(
-                        task, false /* allowSnapshotHome */);
+                mWindowManager.mTaskSnapshotController.recordSnapshot(task);
                 rootTask.setBounds(r.pictureInPictureArgs.getSourceRectHint());
             }
             rootTask.setDeferTaskAppear(false);
 
-            // After setting this, it is not expected to change activity configuration until the
-            // transition animation is finished. So the activity can keep consistent appearance
-            // when animating.
-            r.mWaitForEnteringPinnedMode = true;
+            if (!isPip2ExperimentEnabled()) {
+                // After setting this, it is not expected to change activity configuration until the
+                // transition animation is finished. So the activity can keep consistent appearance
+                // when animating.
+                r.mWaitForEnteringPinnedMode = true;
+            }
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
             // to the root pinned task
             r.supportsEnterPipOnTaskSwitch = false;
@@ -2198,7 +2214,9 @@
         } finally {
             mService.continueWindowLayout();
             try {
-                ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+                if (!isPip2ExperimentEnabled()) {
+                    ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+                }
             } finally {
                 transitionController.continueTransitionReady();
             }
@@ -2214,7 +2232,9 @@
             newTransition.setReady(rootTask, true /* ready */);
         }
 
-        resumeFocusedTasksTopActivities();
+        if (!isPip2ExperimentEnabled()) {
+            resumeFocusedTasksTopActivities();
+        }
 
         notifyActivityPipModeChanged(r.getTask(), r);
     }
@@ -2299,19 +2319,36 @@
      *
      * @param app    The app that crashed.
      * @param reason Reason to perform this action.
-     * @return The task id that was finished in this root task, or INVALID_TASK_ID if none was
-     * finished.
+     * @return The finished task which was on top or visible, otherwise {@code null} if the crashed
+     *         app doesn't have activity in visible task.
      */
-    int finishTopCrashedActivities(WindowProcessController app, String reason) {
+    @Nullable
+    Task finishTopCrashedActivities(WindowProcessController app, String reason) {
         Task focusedRootTask = getTopDisplayFocusedRootTask();
         final Task[] finishedTask = new Task[1];
         forAllRootTasks(rootTask -> {
+            final boolean recordTopOrVisible = finishedTask[0] == null
+                    && (focusedRootTask == rootTask || rootTask.isVisibleRequested());
             final Task t = rootTask.finishTopCrashedActivityLocked(app, reason);
-            if (rootTask == focusedRootTask || finishedTask[0] == null) {
+            if (recordTopOrVisible) {
                 finishedTask[0] = t;
             }
         });
-        return finishedTask[0] != null ? finishedTask[0].mTaskId : INVALID_TASK_ID;
+        return finishedTask[0];
+    }
+
+    void ensureVisibilityOnVisibleActivityDiedOrCrashed(String reason) {
+        final Task topTask = getTopDisplayFocusedRootTask();
+        if (topTask != null && topTask.topRunningActivity(true /* focusableOnly */) == null) {
+            // Move the next focusable task to front.
+            topTask.adjustFocusToNextFocusableTask(reason);
+        }
+        if (!resumeFocusedTasksTopActivities()) {
+            // It may be nothing to resume because there are pausing activities or all the top
+            // activities are resumed. Then it still needs to make sure all visible activities are
+            // running in case the tasks were reordered or there are non-top visible activities.
+            ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
+        }
     }
 
     boolean resumeFocusedTasksTopActivities() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index bbe44c5..e6d4866 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -884,8 +884,8 @@
     @Override
     public void grantInputChannel(int displayId, SurfaceControl surface,
             IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type,
-            int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName,
-            InputChannel outInputChannel) {
+            int inputFeatures, IBinder windowToken, IBinder inputTransferToken,
+            String inputHandleName, InputChannel outInputChannel) {
         if (hostInputToken == null && !mCanAddInternalSystemWindow) {
             // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
             // embedded windows without providing a host window input token
@@ -896,7 +896,7 @@
         try {
             mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken,
                     flags, mCanAddInternalSystemWindow ? privateFlags : 0,
-                    type, inputFeatures, windowToken, focusGrantToken, inputHandleName,
+                    type, inputFeatures, windowToken, inputTransferToken, inputHandleName,
                     outInputChannel);
         } finally {
             Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 37f9730..2e7ff7a 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -83,9 +83,10 @@
             Transition.ChangeInfo info = changeInfos.get(i);
             // Intentionally skip record snapshot for changes originated from PiP.
             if (info.mWindowingMode == WINDOWING_MODE_PINNED) continue;
-            if (info.mContainer.asTask() != null && !info.mContainer.isVisibleRequested()) {
-                mTaskSnapshotController.recordSnapshot(info.mContainer.asTask(),
-                        false /* allowSnapshotHome */);
+            if (info.mContainer.isActivityTypeHome()) continue;
+            final Task task = info.mContainer.asTask();
+            if (task != null && !task.isVisibleRequested()) {
+                mTaskSnapshotController.recordSnapshot(task, info);
             }
             // Won't need to capture activity snapshot in close transition.
             if (isTransitionClose) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4eb4290..2b12e74 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -118,6 +118,7 @@
         mTmpTasks.clear();
         for (int i = closingApps.size() - 1; i >= 0; i--) {
             final ActivityRecord activity = closingApps.valueAt(i);
+            if (activity.isActivityTypeHome()) continue;
             final Task task = activity.getTask();
             if (task == null) continue;
 
@@ -144,25 +145,34 @@
     }
 
     void snapshotTasks(ArraySet<Task> tasks) {
-        snapshotTasks(tasks, false /* allowSnapshotHome */);
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            recordSnapshot(tasks.valueAt(i));
+        }
     }
 
-    TaskSnapshot recordSnapshot(Task task, boolean allowSnapshotHome) {
-        final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
-        final TaskSnapshot snapshot = recordSnapshotInner(task, allowSnapshotHome);
-        if (!snapshotHome && snapshot != null) {
+    /**
+     * The attributes of task snapshot are based on task configuration. But sometimes the
+     * configuration may have been changed during a transition, so supply the ChangeInfo that
+     * stored the previous appearance of the closing task.
+     */
+    void recordSnapshot(Task task, Transition.ChangeInfo changeInfo) {
+        mCurrentChangeInfo = changeInfo;
+        try {
+            recordSnapshot(task);
+        } finally {
+            mCurrentChangeInfo = null;
+        }
+    }
+
+    TaskSnapshot recordSnapshot(Task task) {
+        final TaskSnapshot snapshot = recordSnapshotInner(task);
+        if (snapshot != null && !task.isActivityTypeHome()) {
             mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
             task.onSnapshotChanged(snapshot);
         }
         return snapshot;
     }
 
-    private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            recordSnapshot(tasks.valueAt(i), allowSnapshotHome);
-        }
-    }
-
     /**
      * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW
      * MANAGER LOCK WHEN CALLING THIS METHOD!
@@ -320,19 +330,22 @@
         if (displayContent == null) {
             return;
         }
+        // Allow taking snapshot of home when turning screen off to reduce the delay of waking from
+        // secure lock to home.
+        final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY
+                && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId);
         mTmpTasks.clear();
-        displayContent.forAllTasks(task -> {
+        displayContent.forAllLeafTasks(task -> {
+            if (!allowSnapshotHome && task.isActivityTypeHome()) {
+                return;
+            }
             // Since RecentsAnimation will handle task snapshot while switching apps with the best
             // capture timing (e.g. IME window capture), No need additional task capture while task
             // is controlled by RecentsAnimation.
             if (task.isVisible() && !isAnimatingByRecents(task)) {
                 mTmpTasks.add(task);
             }
-        });
-        // Allow taking snapshot of home when turning screen off to reduce the delay of waking from
-        // secure lock to home.
-        final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY
-                && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId);
-        snapshotTasks(mTmpTasks, allowSnapshotHome);
+        }, true /* traverseTopToBottom */);
+        snapshotTasks(mTmpTasks);
     }
 }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 774be9e..882104a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,7 @@
             parent.forAllTasks(t -> {
                 // Skip transient-launch task
                 if (t == transientRootTask) return false;
-                if (t.isVisibleRequested() && !t.isAlwaysOnTop()
-                        && !t.getWindowConfiguration().tasksAreFloating()) {
+                if (t.isVisibleRequested() && !t.isAlwaysOnTop()) {
                     if (t.isRootTask()) {
                         mTransientHideTasks.add(t);
                     }
@@ -1197,7 +1196,8 @@
                         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                                 "  Commit activity becoming invisible: %s", ar);
                         final SnapshotController snapController = mController.mSnapshotController;
-                        if (mTransientLaunches != null && !task.isVisibleRequested()) {
+                        if (mTransientLaunches != null && !task.isVisibleRequested()
+                                && !task.isActivityTypeHome()) {
                             final long startTimeNs = mLogger.mSendTimeNs;
                             final long lastSnapshotTimeNs = snapController.mTaskSnapshotController
                                     .getSnapshotCaptureTime(task.mTaskId);
@@ -1205,8 +1205,7 @@
                             // transition only if a snapshot was not already captured by request
                             // during the transition
                             if (lastSnapshotTimeNs < startTimeNs) {
-                                snapController.mTaskSnapshotController
-                                        .recordSnapshot(task, false /* allowSnapshotHome */);
+                                snapController.mTaskSnapshotController.recordSnapshot(task);
                             } else {
                                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                                         "  Skipping post-transition snapshot for task %d",
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 7109137..78afaa8 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -721,6 +721,7 @@
             // set the pip task in the request if provided
             if (mCollectingTransition.getPipActivity() != null) {
                 pipTaskInfo = mCollectingTransition.getPipActivity().getTask().getTaskInfo();
+                mCollectingTransition.setPipActivity(null);
             }
 
             final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 82452cc..074b404 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2493,14 +2493,6 @@
                 outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
             }
 
-            // TODO (b/298562855): Remove this after identifying the reason why the frame is empty.
-            if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) {
-                Slog.w(TAG, "Empty frame of " + win
-                        + " configChanged=" + configChanged
-                        + " frame=" + win.getFrame().toShortString()
-                        + " attrs=" + attrs);
-            }
-
             ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
                     win, focusMayChange);
 
@@ -8803,7 +8795,7 @@
     void grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
             SurfaceControl surface, IWindow window, IBinder hostInputToken,
             int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken,
-            IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) {
+            IBinder inputTransferToken, String inputHandleName, InputChannel outInputChannel) {
         final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type);
         final InputApplicationHandle applicationHandle;
         final String name;
@@ -8812,7 +8804,7 @@
             EmbeddedWindowController.EmbeddedWindow win =
                     new EmbeddedWindowController.EmbeddedWindow(session, this, window,
                             mInputToWindowMap.get(hostInputToken), callingUid, callingPid,
-                            sanitizedType, displayId, focusGrantToken, inputHandleName,
+                            sanitizedType, displayId, inputTransferToken, inputHandleName,
                             (flags & FLAG_NOT_FOCUSABLE) == 0);
             win.openInputChannel(outInputChannel);
             mEmbeddedWindowController.add(outInputChannel.getToken(), win);
@@ -8884,11 +8876,6 @@
             h.inputConfig |= InputConfig.NOT_FOCUSABLE;
         }
 
-        //  Check private trusted overlay flag to set trustedOverlay field of input window handle.
-        if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
-            h.inputConfig |= InputConfig.TRUSTED_OVERLAY;
-        }
-
         h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         h.ownerUid = callingUid;
         h.ownerPid = callingPid;
@@ -8908,6 +8895,8 @@
         }
 
         final SurfaceControl.Transaction t = mTransactionFactory.get();
+        //  Check private trusted overlay flag to set trustedOverlay field of input window handle.
+        h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0);
         t.setInputWindowInfo(surface, h);
         t.apply();
         t.close();
@@ -9139,10 +9128,10 @@
         return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
     }
 
-    void grantEmbeddedWindowFocus(Session session, IBinder focusToken, boolean grantFocus) {
+    void grantEmbeddedWindowFocus(Session session, IBinder inputTransferToken, boolean grantFocus) {
         synchronized (mGlobalLock) {
             final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
-                    mEmbeddedWindowController.getByFocusToken(focusToken);
+                    mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
             if (embeddedWindow == null) {
                 Slog.e(TAG, "Embedded window not found");
                 return;
@@ -9187,8 +9176,8 @@
         }
     }
 
-    void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetFocusToken,
-                                  boolean grantFocus) {
+    void grantEmbeddedWindowFocus(Session session, IWindow callingWindow,
+            IBinder inputTransferToken, boolean grantFocus) {
         synchronized (mGlobalLock) {
             final WindowState hostWindow =
                     windowForClientLocked(session, callingWindow, false /* throwOnError*/);
@@ -9201,7 +9190,7 @@
                 return;
             }
             final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
-                    mEmbeddedWindowController.getByFocusToken(targetFocusToken);
+                    mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
             if (embeddedWindow == null) {
                 Slog.e(TAG, "Embedded window not found");
                 return;
@@ -9430,7 +9419,7 @@
                     throw new IllegalArgumentException(
                             "Failed to find matching task for taskId=" + taskId);
                 }
-                taskSnapshot = mTaskSnapshotController.captureSnapshot(task, false);
+                taskSnapshot = mTaskSnapshotController.captureSnapshot(task);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f8e9a56..dd9a88f 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -41,6 +41,7 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK;
@@ -1088,6 +1089,26 @@
                 }
                 break;
             }
+            case HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK: {
+                final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+                Task pipTask = container.asTask();
+                if (pipTask == null) {
+                    break;
+                }
+                ActivityRecord[] pipActivity = new ActivityRecord[1];
+                pipTask.forAllActivities((activity) -> {
+                    if (activity.pictureInPictureArgs != null) {
+                        pipActivity[0] = activity;
+                    }
+                });
+
+                Rect entryBounds = hop.getBounds();
+                mService.mRootWindowContainer.moveActivityToPinnedRootTask(
+                        pipActivity[0], null /* launchIntoPipHostActivity */,
+                        "moveActivityToPinnedRootTask", null /* transition */, entryBounds);
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                break;
+            }
             case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: {
                 if (finishTransition == null) break;
                 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ebef606..4beec2b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -178,6 +178,7 @@
 import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
 import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.surfaceTrustedOverlay;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -1110,7 +1111,9 @@
         mInputWindowHandle.setName(getName());
         mInputWindowHandle.setPackageName(mAttrs.packageName);
         mInputWindowHandle.setLayoutParamsType(mAttrs.type);
-        mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s));
+        if (!surfaceTrustedOverlay()) {
+            mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay());
+        }
         if (DEBUG) {
             Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
                             + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -1185,12 +1188,12 @@
         }
     }
 
-    boolean shouldWindowHandleBeTrusted(Session s) {
+    public boolean isWindowTrustedOverlay() {
         return InputMonitor.isTrustedOverlay(mAttrs.type)
                 || ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
-                        && s.mCanAddInternalSystemWindow)
+                        && mSession.mCanAddInternalSystemWindow)
                 || ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
-                        && s.mCanCreateSystemApplicationOverlay);
+                        && mSession.mCanCreateSystemApplicationOverlay);
     }
 
     int getTouchOcclusionMode() {
@@ -5187,6 +5190,9 @@
             updateFrameRateSelectionPriorityIfNeeded();
             updateScaleIfNeeded();
             mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+            if (surfaceTrustedOverlay()) {
+                getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
+            }
         }
         super.prepareSurfaces();
     }
@@ -5939,7 +5945,13 @@
     }
 
     boolean isTrustedOverlay() {
-        return mInputWindowHandle.isTrustedOverlay();
+        if (surfaceTrustedOverlay()) {
+            WindowState parentWindow = getParentWindow();
+            return isWindowTrustedOverlay() || (parentWindow != null
+                    && parentWindow.isWindowTrustedOverlay());
+        } else {
+            return mInputWindowHandle.isTrustedOverlay();
+        }
     }
 
     public boolean receiveFocusFromTapOutside() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index ec5378f..bc70658 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -78,6 +78,7 @@
         "onload.cpp",
         ":lib_cachedAppOptimizer_native",
         ":lib_gameManagerService_native",
+        ":lib_oomConnection_native",
     ],
 
     include_dirs: [
@@ -122,6 +123,7 @@
         "libhardware_legacy",
         "libhidlbase",
         "libmeminfo",
+        "libmemevents",
         "libmemtrackproxy",
         "libmtp",
         "libnativehelper",
@@ -239,3 +241,8 @@
         "com_android_server_app_GameManagerService.cpp",
     ],
 }
+
+filegroup {
+    name: "lib_oomConnection_native",
+    srcs: ["com_android_server_am_OomConnection.cpp",],
+}
diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp
new file mode 100644
index 0000000..e892d23
--- /dev/null
+++ b/services/core/jni/com_android_server_am_OomConnection.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OomConnection"
+
+#include <core_jni_helpers.h>
+#include <jni.h>
+#include <memevents/memevents.h>
+
+namespace android {
+
+// Used to cache the results of the JNI name lookup
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} sOomKillRecordInfo;
+
+static memevents::MemEventListener memevent_listener;
+
+/**
+ * Initialize listening and waiting for new out-of-memory (OOM) events to occur.
+ * Once a OOM event is detected, we then fetch the list of OOM kills, and return
+ * a corresponding java array with the information gathered.
+ *
+ * In the case that we encounter an error, we make sure to close the epfd, and
+ * the OOM file descriptor, by calling `deregisterAllEvents()`.
+ *
+ * @return list of `android.os.OomKillRecord`
+ * @throws java.lang.RuntimeException
+ */
+static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) {
+    const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL;
+    if (!memevent_listener.registerEvent(oom_event)) {
+        memevent_listener.deregisterAllEvents();
+        jniThrowRuntimeException(env, "listener failed to register to OOM events");
+        return nullptr;
+    }
+
+    memevents::MemEvent event_received;
+    do {
+        event_received = memevent_listener.listen();
+        if (event_received == memevents::MemEvent::ERROR) {
+            memevent_listener.deregisterAllEvents();
+            jniThrowRuntimeException(env, "listener received error event");
+            return nullptr;
+        }
+    } while (event_received != oom_event);
+
+    std::vector<memevents::OomKill> oom_events;
+    if (!memevent_listener.getOomEvents(oom_events)) {
+        memevent_listener.deregisterAllEvents();
+        jniThrowRuntimeException(env, "Failed to get OOM events");
+        return nullptr;
+    }
+
+    jobjectArray java_oom_array =
+            env->NewObjectArray(oom_events.size(), sOomKillRecordInfo.clazz, nullptr);
+    if (java_oom_array == NULL) {
+        memevent_listener.deregisterAllEvents();
+        jniThrowRuntimeException(env, "Failed to create OomKillRecord array");
+        return nullptr;
+    }
+
+    for (int i = 0; i < oom_events.size(); i++) {
+        const memevents::OomKill oom_event = oom_events[i];
+        jstring process_name = env->NewStringUTF(oom_event.process_name);
+        if (process_name == NULL) {
+            memevent_listener.deregisterAllEvents();
+            jniThrowRuntimeException(env, "Failed creating java string for process name");
+        }
+        jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
+                                               oom_event.timestamp_ms, oom_event.pid, oom_event.uid,
+                                               process_name, oom_event.oom_score_adj);
+        if (java_oom_kill == NULL) {
+            memevent_listener.deregisterAllEvents();
+            jniThrowRuntimeException(env, "Failed to create OomKillRecord object");
+            return java_oom_array;
+        }
+        env->SetObjectArrayElement(java_oom_array, i, java_oom_kill);
+    }
+    return java_oom_array;
+}
+
+static const JNINativeMethod sOomConnectionMethods[] = {
+        /* name, signature, funcPtr */
+        {"waitOom", "()[Landroid/os/OomKillRecord;",
+         (void*)android_server_am_OomConnection_waitOom},
+};
+
+int register_android_server_am_OomConnection(JNIEnv* env) {
+    sOomKillRecordInfo.clazz = FindClassOrDie(env, "android/os/OomKillRecord");
+    sOomKillRecordInfo.clazz = MakeGlobalRefOrDie(env, sOomKillRecordInfo.clazz);
+
+    sOomKillRecordInfo.ctor =
+            GetMethodIDOrDie(env, sOomKillRecordInfo.clazz, "<init>", "(JIILjava/lang/String;S)V");
+
+    return RegisterMethodsOrDie(env, "com/android/server/am/OomConnection", sOomConnectionMethods,
+                                NELEM(sOomConnectionMethods));
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index f6f6737..df44895 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,6 +52,7 @@
 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
 int register_android_server_SyntheticPasswordManager(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
+int register_android_server_am_OomConnection(JNIEnv* env);
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
 int register_android_server_am_LowMemDetector(JNIEnv* env);
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
@@ -112,6 +113,7 @@
     register_android_server_storage_AppFuse(env);
     register_android_server_SyntheticPasswordManager(env);
     register_android_hardware_display_DisplayViewport(env);
+    register_android_server_am_OomConnection(env);
     register_android_server_am_CachedAppOptimizer(env);
     register_android_server_am_LowMemDetector(env);
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 74fdabf..68e2c9a 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -168,6 +168,24 @@
     return list;
 }
 
+static const std::map<std::pair<AidlAudioDeviceType, std::string>, audio_devices_t>
+        aidlToNativeAudioType = {
+    {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_ANALOG},
+        AUDIO_DEVICE_IN_LINE},
+    {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_HDMI},
+        AUDIO_DEVICE_IN_HDMI},
+    {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_HDMI_ARC},
+        AUDIO_DEVICE_IN_HDMI_ARC},
+    {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_HDMI_EARC},
+        AUDIO_DEVICE_IN_HDMI_EARC},
+    {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_IP_V4},
+        AUDIO_DEVICE_IN_IP},
+    {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_SPDIF},
+        AUDIO_DEVICE_IN_SPDIF},
+    {{AidlAudioDeviceType::IN_LOOPBACK, ""}, AUDIO_DEVICE_IN_LOOPBACK},
+    {{AidlAudioDeviceType::IN_TV_TUNER, ""}, AUDIO_DEVICE_IN_TV_TUNER},
+};
+
 void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfoWrapper& info) {
     {
         Mutex::Autolock autoLock(&mLock);
@@ -187,9 +205,15 @@
     if (info.isHidl) {
         hidlSetUpAudioInfo(env, builder, info);
     } else {
-        AidlAudioDeviceType audioType = info.aidlAudioDevice.type.type;
-        env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType, audioType);
-        if (audioType != AidlAudioDeviceType::NONE) {
+        auto it = aidlToNativeAudioType.find({info.aidlAudioDevice.type.type,
+                                    info.aidlAudioDevice.type.connection});
+        audio_devices_t nativeAudioType = AUDIO_DEVICE_NONE;
+        if (it != aidlToNativeAudioType.end()) {
+            nativeAudioType = it->second;
+        }
+        env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType,
+            nativeAudioType);
+        if (info.aidlAudioDevice.type.type != AidlAudioDeviceType::NONE) {
             std::stringstream ss;
             switch (info.aidlAudioDevice.address.getTag()) {
                 case AidlAudioDeviceAddress::id:
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index e8b1c99..b7b4b16 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -54,6 +54,7 @@
 
 using AidlAudioDevice = ::aidl::android::media::audio::common::AudioDevice;
 using AidlAudioDeviceAddress = ::aidl::android::media::audio::common::AudioDeviceAddress;
+using AidlAudioDeviceDescription = ::aidl::android::media::audio::common::AudioDeviceDescription;
 using AidlAudioDeviceType = ::aidl::android::media::audio::common::AudioDeviceType;
 using AidlITvInput = ::aidl::android::hardware::tv::input::ITvInput;
 using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index d0a9c45..debd891 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -46,6 +46,8 @@
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
+                <xs:element type="powerThrottlingConfig" name="powerThrottlingConfig" minOccurs="0"
+                            maxOccurs="1"/>
                 <xs:element type="luxThrottling" name="luxThrottling" minOccurs="0"
                             maxOccurs="1"/>
                 <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
@@ -350,6 +352,43 @@
         </xs:sequence>
     </xs:complexType>
 
+    <xs:complexType name="powerThrottlingMap">
+        <xs:sequence>
+            <xs:element name="powerThrottlingPoint" type="powerThrottlingPoint" maxOccurs="unbounded" minOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:sequence>
+        <xs:attribute name="id" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:complexType name="powerThrottlingPoint">
+        <xs:sequence>
+            <xs:element type="thermalStatus" name="thermalStatus">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <xs:element type="nonNegativeDecimal" name="powerQuotaMilliWatts">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="powerThrottlingConfig">
+        <xs:element type="nonNegativeDecimal" name="brightnessLowestCapAllowed">
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger">
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element type="powerThrottlingMap" name="powerThrottlingMap" maxOccurs="unbounded">
+                <xs:annotation name="final"/>
+        </xs:element>
+    </xs:complexType>
+
     <xs:complexType name="nitsMap">
         <xs:sequence>
             <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 949b1f2..2d27f0c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -106,6 +106,7 @@
     method public final com.android.server.display.config.SensorDetails getLightSensor();
     method public com.android.server.display.config.LuxThrottling getLuxThrottling();
     method @Nullable public final String getName();
+    method public com.android.server.display.config.PowerThrottlingConfig getPowerThrottlingConfig();
     method public final com.android.server.display.config.SensorDetails getProxSensor();
     method public com.android.server.display.config.DisplayQuirks getQuirks();
     method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
@@ -138,6 +139,7 @@
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
     method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
     method public final void setName(@Nullable String);
+    method public void setPowerThrottlingConfig(com.android.server.display.config.PowerThrottlingConfig);
     method public final void setProxSensor(com.android.server.display.config.SensorDetails);
     method public void setQuirks(com.android.server.display.config.DisplayQuirks);
     method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
@@ -246,6 +248,30 @@
     method public final void setValue(@NonNull java.math.BigDecimal);
   }
 
+  public class PowerThrottlingConfig {
+    ctor public PowerThrottlingConfig();
+    method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed();
+    method @NonNull public final java.math.BigInteger getPollingWindowMillis();
+    method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap();
+    method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal);
+    method public final void setPollingWindowMillis(@NonNull java.math.BigInteger);
+  }
+
+  public class PowerThrottlingMap {
+    ctor public PowerThrottlingMap();
+    method public String getId();
+    method @NonNull public final java.util.List<com.android.server.display.config.PowerThrottlingPoint> getPowerThrottlingPoint();
+    method public void setId(String);
+  }
+
+  public class PowerThrottlingPoint {
+    ctor public PowerThrottlingPoint();
+    method @NonNull public final java.math.BigDecimal getPowerQuotaMilliWatts();
+    method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus();
+    method public final void setPowerQuotaMilliWatts(@NonNull java.math.BigDecimal);
+    method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus);
+  }
+
   public enum PredefinedBrightnessLimitNames {
     method public String getRawName();
     enum_constant public static final com.android.server.display.config.PredefinedBrightnessLimitNames _default;
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
index 57b5d00..4e95465 100644
--- a/services/core/xsd/display-layout-config/display-layout-config.xsd
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -52,6 +52,7 @@
             <xs:element name="address" type="xs:nonNegativeInteger"/>
             <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
+            <xs:element name="powerThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" />
             <xs:element name="leadDisplayAddress" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1" />
         </xs:sequence>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 2d4f7a4..195cae5 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -8,6 +8,7 @@
     method public String getDisplayGroup();
     method public java.math.BigInteger getLeadDisplayAddress();
     method public String getPosition();
+    method public String getPowerThrottlingMapId();
     method public String getRefreshRateThermalThrottlingMapId();
     method public String getRefreshRateZoneId();
     method public boolean isDefaultDisplay();
@@ -19,6 +20,7 @@
     method public void setEnabled(boolean);
     method public void setLeadDisplayAddress(java.math.BigInteger);
     method public void setPosition(String);
+    method public void setPowerThrottlingMapId(String);
     method public void setRefreshRateThermalThrottlingMapId(String);
     method public void setRefreshRateZoneId(String);
   }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index b90f08e..3c190bf 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -32,6 +32,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.service.credentials.CredentialProviderInfoFactory;
 import android.util.Slog;
 
@@ -171,7 +172,9 @@
                 .setAction(UUID.randomUUID().toString());
         //TODO: Create unique pending intent using request code and cancel any pre-existing pending
         // intents
-        return PendingIntent.getActivity(
-                mContext, /*requestCode=*/0, intent, PendingIntent.FLAG_IMMUTABLE);
+        return PendingIntent.getActivityAsUser(
+                mContext, /*requestCode=*/0, intent,
+                PendingIntent.FLAG_IMMUTABLE, /*options=*/null,
+                UserHandle.of(mUserId));
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 3eb6718..7bd1cc4 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -31,6 +31,7 @@
 import android.credentials.ui.GetCredentialProviderData;
 import android.credentials.ui.ProviderPendingIntentResponse;
 import android.os.ICancellationSignal;
+import android.service.autofill.Flags;
 import android.service.credentials.Action;
 import android.service.credentials.BeginGetCredentialOption;
 import android.service.credentials.BeginGetCredentialRequest;
@@ -42,6 +43,7 @@
 import android.service.credentials.RemoteEntry;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -379,13 +381,23 @@
         // but does not resolve to a valid option. For now, not skipping it because
         // it may be possible that the provider adds their own extras and expects to receive
         // those and complete the flow.
-        if (mBeginGetOptionToCredentialOptionMap.get(id) == null) {
+        Intent intent = new Intent();
+        CredentialOption credentialOption = mBeginGetOptionToCredentialOptionMap.get(id);
+        if (credentialOption == null) {
             Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option");
-            return new Intent();
+            return intent;
         }
-        return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+        AutofillId autofillId = credentialOption
+                .getCandidateQueryData()
+                .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
+        if (autofillId != null && Flags.autofillCredmanIntegration()) {
+            intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
+        }
+        return intent.putExtra(
+                CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
                 new GetCredentialRequest(
-                        mCallingAppInfo, List.of(mBeginGetOptionToCredentialOptionMap.get(id))));
+                        mCallingAppInfo,
+                        List.of(credentialOption)));
     }
 
     private Intent setUpFillInIntentWithQueryRequest() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6aa135a..43e47d7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -241,6 +241,7 @@
 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
 import static android.provider.Telephony.Carriers.INVALID_APN_ID;
 import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
+
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -15781,57 +15782,19 @@
         } else {
             long ident = mInjector.binderClearCallingIdentity();
             try {
-                // TODO(b/277908283): check in the policy engine instead of calling user manager.
-                List<UserManager.EnforcingUser> sources = mUserManager
-                        .getUserRestrictionSources(restriction, UserHandle.of(userId));
-                if (sources == null) {
-                    // The restriction is not enforced.
-                    return null;
-                }
-                int sizeBefore = sources.size();
-                if (sizeBefore > 1) {
-                    Slogf.d(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): "
-                            + "%d sources found, excluding those set by UserManager",
-                            userId, restriction, sizeBefore);
-                    sources = getDevicePolicySources(sources);
-                }
-                if (sources.isEmpty()) {
-                    // The restriction is not enforced (or is just enforced by the system)
+                if (getEnforcingAdminsForRestrictionInternal(userId, restriction).size() == 0) {
                     return null;
                 }
 
-                if (sources.size() > 1) {
-                    // In this case, we'll show an admin support dialog that does not
-                    // specify the admin.
-                    // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
-                    // the admin for the calling user.
-                    Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple "
-                            + "sources for restriction %s on user %d",
-                            userId, restriction, restriction, userId);
+                ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId);
+                if (admin != null) {
                     result = new Bundle();
-                    result.putInt(Intent.EXTRA_USER_ID, userId);
+                    result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier());
+                    result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+                            admin.info.getComponent());
                     return result;
                 }
-                final UserManager.EnforcingUser enforcingUser = sources.get(0);
-                final int sourceType = enforcingUser.getUserRestrictionSource();
-                if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER
-                        || sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
-                    ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId);
-                    if (admin != null) {
-                        result = new Bundle();
-                        result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier());
-                        result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
-                                admin.info.getComponent());
-                        return result;
-                    }
-                } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
-                    /*
-                     * In this case, the user restriction is enforced by the system.
-                     * So we won't show an admin support intent, even if it is also
-                     * enforced by a profile/device owner.
-                     */
-                    return null;
-                }
+                return null;
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp
index a8db05e..84a6df3 100644
--- a/services/foldables/devicestateprovider/tests/Android.bp
+++ b/services/foldables/devicestateprovider/tests/Android.bp
@@ -20,11 +20,11 @@
         "foldable-device-state-provider",
         "androidx.test.rules",
         "junit",
-        "truth-prebuilt",
+        "truth",
         "mockito-target-extended-minus-junit4",
         "androidx.test.uiautomator_uiautomator",
         "androidx.test.ext.junit",
         "testables",
     ],
-    test_suites: ["device-tests"]
+    test_suites: ["device-tests"],
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 57fa12d..49ad84a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -264,6 +264,8 @@
             "com.android.server.backup.BackupManagerService$Lifecycle";
     private static final String APPWIDGET_SERVICE_CLASS =
             "com.android.server.appwidget.AppWidgetService";
+    private static final String ARC_SYSTEM_HEALTH_SERVICE =
+            "com.android.server.arc.health.ArcSystemHealthService";
     private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
             "com.android.server.voiceinteraction.VoiceInteractionManagerService";
     private static final String APP_HIBERNATION_SERVICE_CLASS =
@@ -1287,6 +1289,12 @@
             }
         }
 
+        if (Build.IS_ARC) {
+            t.traceBegin("StartArcSystemHealthService");
+            mSystemServiceManager.startService(ARC_SYSTEM_HEALTH_SERVICE);
+            t.traceEnd();
+        }
+
         t.traceBegin("StartUserManagerService");
         mSystemServiceManager.startService(UserManagerService.LifeCycle.class);
         t.traceEnd();
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index cee2524..bb24d51 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -41,6 +41,7 @@
 import android.os.ServiceManager
 import android.os.UserHandle
 import android.os.UserManager
+import android.permission.flags.Flags
 import android.permission.IOnPermissionsChangeListener
 import android.permission.PermissionControllerManager
 import android.permission.PermissionManager
@@ -1409,7 +1410,7 @@
         permissionName: String,
         deviceId: Int,
     ): Int {
-        return if (deviceId == Context.DEVICE_ID_DEFAULT) {
+        return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) {
             with(policy) { getPermissionFlags(appId, userId, permissionName) }
         } else {
             if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
@@ -1442,7 +1443,7 @@
         deviceId: Int,
         flags: Int
     ): Boolean {
-        return if (deviceId == Context.DEVICE_ID_DEFAULT) {
+        return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) {
             with(policy) {
                setPermissionFlags(appId, userId, permissionName, flags)
             }
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index e04dd68..8b9efb3 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -59,7 +59,7 @@
         "mockito-robolectric-prebuilt",
         "platform-test-annotations",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
 
     instrumentation_for: "BackupFrameworksServicesLib",
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 36446f6..ffe6dc5 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -44,7 +44,7 @@
         "service-permission.stubs.system_server",
         "servicestests-core-utils",
         "servicestests-utils-mockito-extended",
-        "truth-prebuilt",
+        "truth",
     ],
 
     libs: [
@@ -92,7 +92,7 @@
         "service-permission.stubs.system_server",
         "servicestests-core-utils",
         "servicestests-utils-mockito-extended",
-        "truth-prebuilt",
+        "truth",
         "SimpleImeTestingLib",
         "SimpleImeImsLib",
     ],
diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp
index 35d754b..e8fce8e 100644
--- a/services/tests/PackageManager/packageinstaller/Android.bp
+++ b/services/tests/PackageManager/packageinstaller/Android.bp
@@ -32,7 +32,7 @@
         "androidx.test.runner",
         "junit",
         "kotlin-test",
-        "truth-prebuilt",
+        "truth",
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
diff --git a/services/tests/PackageManagerComponentOverrideTests/Android.bp b/services/tests/PackageManagerComponentOverrideTests/Android.bp
index bc36970..00850a5 100644
--- a/services/tests/PackageManagerComponentOverrideTests/Android.bp
+++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp
@@ -38,7 +38,7 @@
         "service-permission.stubs.system_server",
         "servicestests-utils-mockito-extended",
         "testng", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
-        "truth-prebuilt",
+        "truth",
     ],
 
     jni_libs: [
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
index 9c4e6fd..ad7af44 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -29,7 +29,7 @@
     static_libs: [
         "compatibility-device-util-axt",
         "androidx.test.runner",
-        "truth-prebuilt",
+        "truth",
         "Harrier",
     ],
     platform_apis: true,
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index ce28682..6eacef7 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -30,7 +30,7 @@
     libs: [
         "tradefed",
         "junit",
-        "truth-prebuilt",
+        "truth",
     ],
     static_libs: [
         "ApexInstallHelper",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index 462c580..cea9c59 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -38,6 +38,6 @@
         "junit-params",
         "androidx.test.ext.junit",
         "androidx.test.rules",
-        "truth-prebuilt",
+        "truth",
     ],
 }
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
index 5718474..ed5f2b5 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
@@ -28,6 +28,6 @@
         "androidx.test.runner",
         "junit",
         "kotlin-test",
-        "truth-prebuilt",
+        "truth",
     ],
 }
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index a1d846e..3aca1ca 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -41,7 +41,7 @@
         "mockito-target-minus-junit4",
         "platform-test-annotations",
         "ShortcutManagerTestUtils",
-        "truth-prebuilt",
+        "truth",
         "testables",
         "platformprotosnano",
         "framework-protos",
@@ -51,7 +51,7 @@
         "servicestests-utils",
         "service-permission.impl",
         "testng",
-        "truth-prebuilt",
+        "truth",
         "junit",
         "junit-params",
         "platform-compat-test-rules",
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 9b3b8c35..85059838 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -37,7 +37,7 @@
         "services.core",
         "servicestests-utils",
         "servicestests-core-utils",
-        "truth-prebuilt",
+        "truth",
     ],
     jni_libs: [
         "libdexmakerjvmtiagent",
diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp
index fc2c085..19c9136 100644
--- a/services/tests/RemoteProvisioningServiceTests/Android.bp
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -30,8 +30,8 @@
         "mockito-target",
         "service-rkp.impl",
         "services.core",
-        "truth-prebuilt",
-        "truth-java8-extension-jar",
+        "truth",
+        "truth-java8-extension",
     ],
     test_suites: [
         "device-tests",
diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp
index e724e804..9dacfea 100644
--- a/services/tests/apexsystemservices/Android.bp
+++ b/services/tests/apexsystemservices/Android.bp
@@ -34,7 +34,7 @@
         "compatibility-host-util",
         "cts-install-lib-host",
         "frameworks-base-hostutils",
-        "truth-prebuilt",
+        "truth",
         "modules-utils-build-testing",
     ],
     test_suites: [
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 6ef150c..c37d21a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -179,6 +179,89 @@
     }
 
     @Test
+    public void testPowerThrottlingConfigFromDisplayConfig() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        DisplayDeviceConfig.PowerThrottlingConfigData powerThrottlingConfigData =
+                mDisplayDeviceConfig.getPowerThrottlingConfigData();
+        assertNotNull(powerThrottlingConfigData);
+        assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA);
+        assertEquals(10, powerThrottlingConfigData.pollingWindowMillis);
+    }
+
+    @Test
+    public void testPowerThrottlingDataFromDisplayConfig() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel>
+                defaultThrottlingLevels = new ArrayList<>();
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f
+                ));
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f
+                ));
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f
+                ));
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f
+                ));
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f
+                ));
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f
+                ));
+
+        DisplayDeviceConfig.PowerThrottlingData defaultThrottlingData =
+                new DisplayDeviceConfig.PowerThrottlingData(defaultThrottlingLevels);
+
+        List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel>
+                concurrentThrottlingLevels = new ArrayList<>();
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f
+                ));
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f
+                ));
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f
+                ));
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f
+                ));
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f
+                ));
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+                        DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f
+                ));
+        DisplayDeviceConfig.PowerThrottlingData concurrentThrottlingData =
+                new DisplayDeviceConfig.PowerThrottlingData(concurrentThrottlingLevels);
+
+        HashMap<String, DisplayDeviceConfig.PowerThrottlingData> throttlingDataMap =
+                new HashMap<>(2);
+        throttlingDataMap.put("default", defaultThrottlingData);
+        throttlingDataMap.put("concurrent", concurrentThrottlingData);
+
+        assertEquals(throttlingDataMap,
+                mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId());
+    }
+
+    @Test
     public void testConfigValuesFromConfigResource() {
         setupDisplayDeviceConfigFromConfigResourceFile();
         verifyConfigValuesFromConfigResource();
@@ -845,6 +928,64 @@
                 +   "</screenBrightnessRampSlowIncreaseIdle>\n";
     }
 
+    private String getPowerThrottlingConfig() {
+        return  "<powerThrottlingConfig >\n"
+                +       "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n"
+                +       "<pollingWindowMillis>10</pollingWindowMillis>\n"
+                +       "<powerThrottlingMap>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>light</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n"
+                +        "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>moderate</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>severe</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>critical</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>emergency</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>shutdown</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +       "</powerThrottlingMap>\n"
+                +       "<powerThrottlingMap id=\"concurrent\">\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>light</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>moderate</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>severe</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>critical</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>emergency</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +           "<powerThrottlingPoint>\n"
+                +               "<thermalStatus>shutdown</thermalStatus>\n"
+                +               "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n"
+                +           "</powerThrottlingPoint>\n"
+                +       "</powerThrottlingMap>\n"
+                +   "</powerThrottlingConfig>\n";
+    }
     private String getScreenBrightnessRampCapsIdle() {
         return "<screenBrightnessRampIncreaseMaxIdleMillis>"
                 +       "4000"
@@ -915,6 +1056,7 @@
                 +            "</displayBrightnessPoint>\n"
                 +       "</displayBrightnessMapping>\n"
                 +   "</autoBrightness>\n"
+                +  getPowerThrottlingConfig()
                 +   "<highBrightnessMode enabled=\"true\">\n"
                 +       "<transitionPoint>" + BRIGHTNESS[1] + "</transitionPoint>\n"
                 +       "<minimumLux>10000</minimumLux>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index c63fac9..ee187ba 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -22,6 +22,9 @@
 
 import static org.mockito.ArgumentMatchers.any;
 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.when;
 
@@ -97,6 +100,37 @@
     }
 
     @Test
+    public void testRegisterHdrListener() {
+        verify(mMockHdrInfoListener).register(mMockBinder);
+    }
+
+    @Test
+    public void testRegisterOtherHdrListenerWhenCalledWithOtherToken() {
+        IBinder otherBinder = mock(IBinder.class);
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, otherBinder);
+
+        verify(mMockHdrInfoListener).unregister(mMockBinder);
+        verify(mMockHdrInfoListener).register(otherBinder);
+    }
+
+    @Test
+    public void testRegisterHdrListenerOnceWhenCalledWithSameToken() {
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
+
+        verify(mMockHdrInfoListener, never()).unregister(mMockBinder);
+        verify(mMockHdrInfoListener, times(1)).register(mMockBinder);
+    }
+
+    @Test
+    public void testRegisterNotCalledIfHbmConfigIsMissing() {
+        IBinder otherBinder = mock(IBinder.class);
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder);
+
+        verify(mMockHdrInfoListener).unregister(mMockBinder);
+        verify(mMockHdrInfoListener, never()).register(otherBinder);
+    }
+
+    @Test
     public void testClamper_AmbientLuxChangesAboveLimit() {
         mHdrClamper.onAmbientLuxChange(500);
 
diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp
index 7c237ac..086e84b 100644
--- a/services/tests/inprocesstests/Android.bp
+++ b/services/tests/inprocesstests/Android.bp
@@ -14,7 +14,7 @@
         "androidx.test.core",
         "androidx.test.rules",
         "services.core",
-        "truth-prebuilt",
+        "truth",
         "platform-test-annotations",
     ],
     test_suites: ["general-tests"],
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 101498a..063af57 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -46,6 +46,7 @@
         "androidx.test.espresso.core",
         "androidx.test.espresso.contrib",
         "androidx.test.ext.truth",
+        "flag-junit",
         "frameworks-base-testutils",
         "hamcrest-library",
         "kotlin-test",
@@ -67,7 +68,7 @@
         "servicestests-core-utils",
         "servicestests-utils-mockito-extended",
         "testables",
-        "truth-prebuilt",
+        "truth",
         // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
         "testng",
         "compatibility-device-util-axt",
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index f1dc1fa..1eb9888 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -22,6 +22,7 @@
     srcs: [
         ":lib_cachedAppOptimizer_native",
         ":lib_gameManagerService_native",
+        ":lib_oomConnection_native",
         "onload.cpp",
     ],
 
@@ -42,6 +43,7 @@
         "libgui",
         "libhidlbase",
         "liblog",
+        "libmemevents",
         "libmeminfo",
         "libnativehelper",
         "libprocessgroup",
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index 23ccb22..fb91051 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -26,6 +26,7 @@
 namespace android {
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
 int register_android_server_app_GameManagerService(JNIEnv* env);
+int register_android_server_am_OomConnection(JNIEnv* env);
 };
 
 using namespace android;
@@ -42,6 +43,7 @@
     ALOG_ASSERT(env, "Could not retrieve the env!");
     register_android_server_am_CachedAppOptimizer(env);
     register_android_server_app_GameManagerService(env);
+    register_android_server_am_OomConnection(env);
     return JNI_VERSION_1_4;
 }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 5b1508b..be29163 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -1290,7 +1290,8 @@
     }
 
     @Test
-    public void testLightStepIdleStateIdlingTimeIncreases() {
+    public void testLightStepIdleStateIdlingTimeIncreasesExponentially() {
+        mConstants.LIGHT_IDLE_INCREASE_LINEARLY = false;
         final long maintenanceTimeMs = 60_000L;
         mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs;
         mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs;
@@ -1335,13 +1336,88 @@
                 eq(mInjector.nowElapsed + idlingTimeMs),
                 anyLong(), anyString(), any(), any(Handler.class));
 
-        for (int i = 0; i < 2; ++i) {
+        for (int i = 0; i < 10; ++i) {
             // IDLE->MAINTENANCE alarm
             mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
             alarmListener.onAlarm();
             verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
             long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs;
             idlingTimeMs *= mConstants.LIGHT_IDLE_FACTOR;
+            idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT);
+            // Set MAINTENANCE->IDLE
+            alarmManagerInOrder.verify(mAlarmManager).setWindow(
+                    eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                    eq(maintenanceExpiryTime),
+                    anyLong(), anyString(), any(), any(Handler.class));
+
+            // MAINTENANCE->IDLE alarm
+            mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+            alarmListener.onAlarm();
+            verifyLightStateConditions(LIGHT_STATE_IDLE);
+            // Set IDLE->MAINTENANCE again
+            alarmManagerInOrder.verify(mAlarmManager).setWindow(
+                    eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                    eq(mInjector.nowElapsed + idlingTimeMs),
+                    anyLong(), anyString(), any(), any(Handler.class));
+        }
+    }
+
+    @Test
+    public void testLightStepIdleStateIdlingTimeIncreasesLinearly() {
+        mConstants.LIGHT_IDLE_INCREASE_LINEARLY = true;
+        final long maintenanceTimeMs = 60_000L;
+        mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs;
+        mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs;
+        mConstants.LIGHT_IDLE_TIMEOUT = 5 * 60_000L;
+        mConstants.LIGHT_MAX_IDLE_TIMEOUT = 30 * 60_000L;
+        mConstants.LIGHT_IDLE_FACTOR = 2f;
+        mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = 2 * 60_000L;
+
+        setNetworkConnected(true);
+        mDeviceIdleController.setJobsActive(false);
+        mDeviceIdleController.setAlarmsActive(false);
+        mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+        InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+
+        final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = ArgumentCaptor
+                .forClass(AlarmManager.OnAlarmListener.class);
+        doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+                eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+        long idlingTimeMs = mConstants.LIGHT_IDLE_TIMEOUT;
+        final long idleAfterInactiveExpiryTime =
+                mInjector.nowElapsed + mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+        alarmManagerInOrder.verify(mAlarmManager).setWindow(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                eq(idleAfterInactiveExpiryTime),
+                anyLong(), anyString(), any(), any(Handler.class));
+
+        final AlarmManager.OnAlarmListener alarmListener =
+                alarmListenerCaptor.getAllValues().get(0);
+
+        // INACTIVE -> IDLE alarm
+        mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+        alarmListener.onAlarm();
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+        alarmManagerInOrder.verify(mAlarmManager).setWindow(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                eq(mInjector.nowElapsed + idlingTimeMs),
+                anyLong(), anyString(), any(), any(Handler.class));
+
+        for (int i = 0; i < 10; ++i) {
+            // IDLE->MAINTENANCE alarm
+            mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+            alarmListener.onAlarm();
+            verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+            long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs;
+            idlingTimeMs += mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS;
+            idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT);
             // Set MAINTENANCE->IDLE
             alarmManagerInOrder.verify(mAlarmManager).setWindow(
                     eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 64cc397..9ba4f5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -218,4 +218,9 @@
             assertEquals(errMsg, Thread.State.TERMINATED, getState());
         }
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2bc66ac..40b5458 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -1210,4 +1210,9 @@
             return returnValueForstartUserOnSecondaryDisplay;
         }
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index 1c0989c..9391d5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -338,4 +338,8 @@
         }
     }
 
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index d56229c..e15942b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -1126,4 +1126,9 @@
             };
         }
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index 0abf46b..596a3f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -284,4 +284,9 @@
 
         return app;
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 434d200..dfb8fda 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -803,4 +803,9 @@
             return mHandler;
         }
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java
index fd1b068..7ec27be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java
@@ -201,4 +201,9 @@
             return mActiveServices;
         }
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
 }
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 2b56ea8..bded9b4 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
@@ -34,6 +34,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER;
 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
 import static com.android.server.job.JobSchedulerService.RARE_INDEX;
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
@@ -66,6 +67,7 @@
 import android.os.Build;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.CellSignalStrength;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyCallback;
@@ -79,6 +81,7 @@
 import com.android.server.net.NetworkPolicyManagerInternal;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -107,6 +110,9 @@
     @Mock
     private PackageManager mPackageManager;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private Constants mConstants;
 
     private FlexibilityController mFlexibilityController;
@@ -898,7 +904,8 @@
             assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
         }
 
-        // Metered network is only when prefetching, late, and in opportunistic quota
+        // Metered network is only when prefetching, charging*, late, and in opportunistic quota
+        // *Charging only when the flag is enabled
         {
             final Network net = mock(Network.class);
             final NetworkCapabilities caps = createCapabilitiesBuilder()
@@ -910,6 +917,8 @@
             assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
             assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
             assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+            mSetFlagsRule.disableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER);
+            when(mService.isBatteryCharging()).thenReturn(false);
 
             when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
                     any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
@@ -918,6 +927,21 @@
             // Only relax restrictions when we at least know the estimated download bytes.
             assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
             assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+            mSetFlagsRule.enableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER);
+            when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+                    any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+                    .thenReturn(9876543210L);
+            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.isBatteryCharging()).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));
+            assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
         }
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 6304270..305569e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -308,7 +308,6 @@
         addDefaultProfileAndParent();
 
         mUms.setBootUser(PROFILE_USER_ID);
-
         // Boot user not switchable so return most recently in foreground.
         assertWithMessage("getBootUser")
                 .that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
@@ -523,6 +522,24 @@
                 .isFalse();
     }
 
+    @Test
+    public void testCreateUserWithLongName_TruncatesName() {
+        UserInfo user = mUms.createUserWithThrow(generateLongString(), USER_TYPE_FULL_SECONDARY, 0);
+        assertThat(user.name.length()).isEqualTo(500);
+        UserInfo user1 = mUms.createUserWithThrow("Test", USER_TYPE_FULL_SECONDARY, 0);
+        assertThat(user1.name.length()).isEqualTo(4);
+    }
+
+    private String generateLongString() {
+        String partialString = "Test Name Test Name Test Name Test Name Test Name Test Name Test "
+                + "Name Test Name Test Name Test Name "; //String of length 100
+        StringBuilder resultString = new StringBuilder();
+        for (int i = 0; i < 660; i++) {
+            resultString.append(partialString);
+        }
+        return resultString.toString();
+    }
+
     private void removeNonSystemUsers() {
         for (UserInfo user : mUms.getUsers(true)) {
             if (!user.getUserHandle().isSystem()) {
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 8ab4507..18a4f00 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -16,7 +16,7 @@
         "coretests-aidl",
         "platformprotosnano",
         "junit",
-        "truth-prebuilt",
+        "truth",
         "androidx.test.runner",
         "androidx.test.ext.junit",
         "androidx.test.ext.truth",
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 20d8a5d..2ece8c7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -2,6 +2,13 @@
 // Build FrameworksServicesTests package
 //########################################################################
 
+java_defaults {
+    name: "FrameworksServicesTests-jni-defaults",
+    jni_libs: [
+        "libservicestestjni",
+    ],
+}
+
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -13,6 +20,9 @@
 
 android_test {
     name: "FrameworksServicesTests",
+    defaults: [
+        "FrameworksServicesTests-jni-defaults",
+    ],
 
     // Include all test java files.
     srcs: [
@@ -51,7 +61,7 @@
         "mockito-target-minus-junit4",
         "platform-test-annotations",
         "ShortcutManagerTestUtils",
-        "truth-prebuilt",
+        "truth",
         "testables",
         "androidx.test.uiautomator_uiautomator",
         "platformprotosnano",
@@ -62,7 +72,7 @@
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
-        "truth-prebuilt",
+        "truth",
         "junit",
         "junit-params",
         "ActivityContext",
diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp
new file mode 100644
index 0000000..174beb8
--- /dev/null
+++ b/services/tests/servicestests/jni/Android.bp
@@ -0,0 +1,58 @@
+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_library_shared {
+    name: "libservicestestjni",
+
+    defaults: ["android.hardware.graphics.common-ndk_shared"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+    ],
+
+    srcs: [
+        ":lib_cachedAppOptimizer_native",
+        ":lib_gameManagerService_native",
+        ":lib_oomConnection_native",
+        "onload.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/base/libs",
+        "frameworks/native/services",
+        "frameworks/native/libs/math/include",
+        "frameworks/native/libs/ui/include",
+        "system/memory/libmeminfo/include",
+    ],
+
+    shared_libs: [
+        "libandroid",
+        "libandroid_runtime",
+        "libbase",
+        "libbinder",
+        "libgralloctypes",
+        "libgui",
+        "libhidlbase",
+        "liblog",
+        "libmeminfo",
+        "libmemevents",
+        "libnativehelper",
+        "libprocessgroup",
+        "libutils",
+        "libcutils",
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+        "android.hardware.graphics.common@1.2",
+        "android.hardware.graphics.mapper@4.0",
+        "android.hidl.token@1.0-utils",
+    ],
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp
new file mode 100644
index 0000000..f160b3d
--- /dev/null
+++ b/services/tests/servicestests/jni/onload.cpp
@@ -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.
+ */
+
+/*
+ * this is a mini native libaray for cached app optimizer tests to run properly. It
+ * loads all the native methods necessary.
+ */
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+int register_android_server_app_GameManagerService(JNIEnv* env);
+int register_android_server_am_OomConnection(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+    register_android_server_am_CachedAppOptimizer(env);
+    register_android_server_app_GameManagerService(env);
+    register_android_server_am_OomConnection(env);
+    return JNI_VERSION_1_4;
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index acdfee9..c0051c6 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -172,4 +172,9 @@
                 anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService),
                 anyBoolean(), anyBoolean(), any());
     }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("servicestestjni");
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
index 9fdbdda..70527ce 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
@@ -228,6 +228,7 @@
         TestHandler mTestHandler;
 
         TestInjector(int skip, boolean immediate) {
+            super(mHandler);
             mTracker = new TestTracker(skip);
             mImmediate = immediate;
         }
@@ -249,9 +250,16 @@
             return mTestHandler;
         }
 
+        @Override
         AnrTimer.CpuTracker getTracker() {
             return mTracker;
         }
+
+        /** For test purposes, always enable the feature. */
+        @Override
+        boolean getFeatureEnabled() {
+            return true;
+        }
     }
 
     // Tests
@@ -261,7 +269,6 @@
     // 4. Start a couple of timers.  Verify max active timers.  Discard one and verify the active
     //    count drops by 1.  Accept one and verify the active count drops by 1.
 
-
     @Test
     public void testSimpleTimeout() throws Exception {
         // Create an immediate TestHandler.
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 2dacda0..d1f4961 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -31,6 +31,7 @@
         "androidx.test.rules",
         "hamcrest-library",
         "mockito-target-inline-minus-junit4",
+        "platform-compat-test-rules",
         "platform-test-annotations",
         "platformprotosnano",
         "statsdprotolite",
@@ -38,7 +39,7 @@
         "hamcrest-library",
         "servicestests-utils",
         "testables",
-        "truth-prebuilt",
+        "truth",
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java
index 312057ee..348d1bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java
@@ -44,6 +44,12 @@
 import org.mockito.Mock;
 
 import java.lang.reflect.Field;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
 
 @RunWith(AndroidTestingRunner.class)
 public class NotificationBitmapJobServiceTest extends UiServiceTestCase {
@@ -103,17 +109,39 @@
 
     @Test
     public void testGetTimeUntilRemoval_beforeToday2am_returnTimeUntilToday2am() {
-        final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 1,
-                /* today2AM= */ 2, /* tomorrow2AM= */ 26);
+        ZoneId zoneId = ZoneId.systemDefault();
+        ZonedDateTime now = Instant.now().atZone(zoneId);
+        LocalDate today = now.toLocalDate();
 
-        assertThat(timeUntilRemoval).isEqualTo(1);
+        LocalTime oneAM = LocalTime.of(/* hour= */ 1, /* minute= */ 0);
+        LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0);
+
+        ZonedDateTime today1AM = ZonedDateTime.of(today, oneAM, zoneId);
+        ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId);
+        ZonedDateTime tomorrow2AM = today2AM.plusDays(1);
+
+        final long msUntilRemoval = mJobService.getTimeUntilRemoval(
+                /* now= */ today1AM, today2AM, tomorrow2AM);
+
+        assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(1).toMillis());
     }
 
     @Test
     public void testGetTimeUntilRemoval_afterToday2am_returnTimeUntilTomorrow2am() {
-        final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 3,
-                /* today2AM= */ 2, /* tomorrow2AM= */ 26);
+        ZoneId zoneId = ZoneId.systemDefault();
+        ZonedDateTime now = Instant.now().atZone(zoneId);
+        LocalDate today = now.toLocalDate();
 
-        assertThat(timeUntilRemoval).isEqualTo(23);
+        LocalTime threeAM = LocalTime.of(/* hour= */ 3, /* minute= */ 0);
+        LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0);
+
+        ZonedDateTime today3AM = ZonedDateTime.of(today, threeAM, zoneId);
+        ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId);
+        ZonedDateTime tomorrow2AM = today2AM.plusDays(1);
+
+        final long msUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ today3AM,
+                today2AM, tomorrow2AM);
+
+        assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(23).toMillis());
     }
 }
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
index d758e71..3499a12 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
@@ -71,10 +71,10 @@
     @Before
     public void setUp() throws Exception {
         mJobService = new NotificationHistoryJobService();
+        mJobService.attachBaseContext(mContext);
+        mJobService.onCreate();
+        mJobService.onBind(/* intent= */ null);  // Create JobServiceEngine within JobService.
 
-        final Field field = JobService.class.getDeclaredField("mEngine");
-        field.setAccessible(true);
-        field.set(mJobService, mock(JobServiceEngine.class));
         mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler);
 
         // add NotificationManagerInternal to LocalServices
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 7a55143..c05f814 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -462,7 +462,7 @@
     }
 
     private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) {
-        assertEquals(a.getRankingMap(), b.getRankingMap());
+        detailedAssertEquals(a.getRankingMap(), b.getRankingMap());
     }
 
     private void detailedAssertEquals(String comment, Ranking a, Ranking b) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9543a2d..91129a1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -91,7 +91,7 @@
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
-import static com.android.server.notification.NotificationManagerService.BITMAP_EXPIRATION_TIME_MS;
+import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
 import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
 import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED;
 import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
@@ -164,6 +164,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.AssociationInfo;
 import android.companion.ICompanionDeviceManager;
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentUris;
@@ -270,11 +271,15 @@
 import com.google.android.collect.Lists;
 import com.google.common.collect.ImmutableList;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
@@ -317,6 +322,9 @@
     private final int mUid = Binder.getCallingUid();
     private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
 
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private TestableNotificationManagerService mService;
     private INotificationManager mBinderService;
     private NotificationManagerInternal mInternalService;
@@ -4828,6 +4836,62 @@
     }
 
     @Test
+    @EnableCompatChanges({NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION})
+    public void testMediaStyle_enforceNoClearFlagEnabled() throws RemoteException {
+        Notification.MediaStyle style = new Notification.MediaStyle();
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setStyle(style);
+
+        NotificationRecord posted = createAndPostNotification(nb, "testMediaStyleSetNoClearFlag");
+
+        assertThat(posted.getFlags() & FLAG_NO_CLEAR).isEqualTo(FLAG_NO_CLEAR);
+    }
+
+    @Test
+    @EnableCompatChanges({NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION})
+    public void testCustomMediaStyle_enforceNoClearFlagEnabled() throws RemoteException {
+        Notification.DecoratedMediaCustomViewStyle style =
+                new Notification.DecoratedMediaCustomViewStyle();
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setStyle(style);
+
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testCustomMediaStyleSetNoClearFlag");
+
+        assertThat(posted.getFlags() & FLAG_NO_CLEAR).isEqualTo(FLAG_NO_CLEAR);
+    }
+
+    @Test
+    @DisableCompatChanges(NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION)
+    public void testMediaStyle_enforceNoClearFlagDisabled() throws RemoteException {
+        Notification.MediaStyle style = new Notification.MediaStyle();
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setStyle(style);
+
+        NotificationRecord posted = createAndPostNotification(nb, "testMediaStyleSetNoClearFlag");
+
+        assertThat(posted.getFlags() & FLAG_NO_CLEAR).isNotEqualTo(FLAG_NO_CLEAR);
+    }
+
+    @Test
+    @DisableCompatChanges(NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION)
+    public void testCustomMediaStyle_enforceNoClearFlagDisabled() throws RemoteException {
+        Notification.DecoratedMediaCustomViewStyle style =
+                new Notification.DecoratedMediaCustomViewStyle();
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setStyle(style);
+
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testCustomMediaStyleSetNoClearFlag");
+
+        assertThat(posted.getFlags() & FLAG_NO_CLEAR).isNotEqualTo(FLAG_NO_CLEAR);
+    }
+
+    @Test
     public void testMediaStyleRemote_hasPermission() throws RemoteException {
         String deviceName = "device";
         mContext.getTestablePermissions().setPermission(
@@ -4838,17 +4902,8 @@
                 mTestNotificationChannel.getId())
                 .setStyle(style);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testMediaStyleRemoteHasPermission", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        NotificationRecord posted = mService.findNotificationLocked(
-                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testMediaStyleRemoteHasPermission");
         Bundle extras = posted.getNotification().extras;
 
         assertTrue(extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
@@ -4866,17 +4921,8 @@
                 mTestNotificationChannel.getId())
                 .setStyle(style);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testMediaStyleRemoteNoPermission", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        NotificationRecord posted = mService.findNotificationLocked(
-                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testMediaStyleRemoteNoPermission");
 
         assertFalse(posted.getNotification().extras
                 .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
@@ -4899,17 +4945,8 @@
                 mTestNotificationChannel.getId())
                 .setStyle(style);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testCustomMediaStyleRemoteNoPermission", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        NotificationRecord posted = mService.findNotificationLocked(
-                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testCustomMediaStyleRemoteNoPermission");
 
         assertFalse(posted.getNotification().extras
                 .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
@@ -4929,16 +4966,9 @@
         Notification.Builder nb = new Notification.Builder(mContext,
                 mTestNotificationChannel.getId())
                 .addExtras(extras);
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testSubstituteAppNamePermission", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-        NotificationRecord posted = mService.findNotificationLocked(
-                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testSubstituteAppNameHasPermission");
 
         assertTrue(posted.getNotification().extras
                 .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME));
@@ -4955,16 +4985,9 @@
         Notification.Builder nb = new Notification.Builder(mContext,
                 mTestNotificationChannel.getId())
                 .addExtras(extras);
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testSubstituteAppNamePermission", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-        NotificationRecord posted = mService.findNotificationLocked(
-                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        NotificationRecord posted = createAndPostNotification(nb,
+                "testSubstituteAppNameNoPermission");
 
         assertFalse(posted.getNotification().extras
                 .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME));
@@ -11358,7 +11381,7 @@
 
         long timePostedMs = System.currentTimeMillis();
         if (isExpired) {
-            timePostedMs -= BITMAP_EXPIRATION_TIME_MS;
+            timePostedMs -= BITMAP_DURATION.toMillis();
         }
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
                 notification, UserHandle.getUserHandleForUid(mUid), null, timePostedMs);
@@ -12656,6 +12679,20 @@
         verify(service, times(1)).setDNDMigrationDone(user.id);
     }
 
+    private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
+            throws RemoteException {
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        return mService.findNotificationLocked(
+                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+    }
+
     private static <T extends Parcelable> T parcelAndUnparcel(T source,
             Parcelable.Creator<T> creator) {
         Parcel parcel = Parcel.obtain();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index a91bd2b..0003555 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
 import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
 import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
@@ -24,8 +26,13 @@
 import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
 import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED;
 import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE;
+import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
+import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
+import static android.view.HapticFeedbackConstants.SCROLL_TICK;
+
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.Mockito.when;
 
@@ -37,6 +44,7 @@
 import android.os.VibratorInfo;
 import android.util.AtomicFile;
 import android.util.SparseArray;
+import android.view.flags.FeatureFlags;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -59,23 +67,25 @@
     private static final VibrationEffect PRIMITIVE_CLICK_EFFECT =
             VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK, 0.3497f).compose();
 
+    private static final int[] SCROLL_FEEDBACK_CONSTANTS =
+            new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
     private Context mContext = InstrumentationRegistry.getContext();
     private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
 
     @Mock private Resources mResourcesMock;
+    @Mock private FeatureFlags mViewFeatureFlags;
 
     @Test
     public void testNonExistentCustomization_useDefault() throws Exception {
         // No customization file is set.
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
                 .isEqualTo(VibrationEffect.get(EFFECT_TICK));
 
         // The customization file specifies no customization.
         setupCustomizationFile("<haptic-feedback-constants></haptic-feedback-constants>");
-        hapticProvider = new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+        hapticProvider = createProviderWithDefaultCustomizations();
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
                 .isEqualTo(VibrationEffect.get(EFFECT_TICK));
@@ -84,8 +94,7 @@
     @Test
     public void testExceptionParsingCustomizations_useDefault() throws Exception {
         setupCustomizationFile("<bad-xml></bad-xml>");
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
                 .isEqualTo(VibrationEffect.get(EFFECT_TICK));
@@ -97,8 +106,7 @@
         SparseArray<VibrationEffect> customizations = new SparseArray<>();
         customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT);
 
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
 
         // The override for `CONTEXT_CLICK` is used.
         assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
@@ -118,8 +126,7 @@
                 + "</haptic-feedback-constants>";
         setupCustomizationFile(xml);
 
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
         // The override for `CONTEXT_CLICK` is not used because the vibration is not supported.
         assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
@@ -137,15 +144,12 @@
         customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT);
 
         // Test with a customization available for `TEXT_HANDLE_MOVE`.
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
 
         // Test with no customization available for `TEXT_HANDLE_MOVE`.
-        hapticProvider =
-                new HapticFeedbackVibrationProvider(
-                        mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+        hapticProvider = createProvider(/* customizations= */ null);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
     }
@@ -158,16 +162,13 @@
         customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT);
 
         // Test with a customization available for `TEXT_HANDLE_MOVE`.
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
                 .isEqualTo(PRIMITIVE_CLICK_EFFECT);
 
         // Test with no customization available for `TEXT_HANDLE_MOVE`.
-        hapticProvider =
-                new HapticFeedbackVibrationProvider(
-                        mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+        hapticProvider = createProvider(/* customizations= */ null);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
                 .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
@@ -181,15 +182,13 @@
         SparseArray<VibrationEffect> customizations = new SparseArray<>();
         customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
 
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
                 .isEqualTo(PRIMITIVE_CLICK_EFFECT);
 
         mockSafeModeEnabledVibration(null);
-        hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+        hapticProvider = createProvider(customizations);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
                 .isEqualTo(PRIMITIVE_CLICK_EFFECT);
@@ -199,9 +198,7 @@
     public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed()
                 throws Exception {
         mockSafeModeEnabledVibration(10, 20, 30, 40);
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(
-                        mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+        HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
                 .isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1));
@@ -211,35 +208,65 @@
     public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed()
                 throws Exception {
         mockSafeModeEnabledVibration(null);
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(
-                        mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+        HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null);
 
         assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull();
     }
 
     @Test
     public void testVibrationAttribute_forNotBypassingIntensitySettings() {
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
         VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
                 SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false);
 
-        assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
-                .isEqualTo(0);
+        assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse();
     }
 
     @Test
     public void testVibrationAttribute_forByassingIntensitySettings() {
-        HapticFeedbackVibrationProvider hapticProvider =
-                new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
         VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
                 SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true);
 
-        assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
-                .isNotEqualTo(0);
+        assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isTrue();
+    }
+
+    @Test
+    public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() {
+        when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+        for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
+            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+                    effectId, /* bypassVibrationIntensitySetting= */ false);
+            assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
+                   .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue();
+        }
+    }
+
+    @Test
+    public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() {
+        when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(false);
+        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+        for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
+            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+                    effectId, /* bypassVibrationIntensitySetting= */ false);
+            assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
+                   .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse();
+        }
+    }
+
+    private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
+        return createProvider(/* customizations= */ null);
+    }
+
+    private HapticFeedbackVibrationProvider createProvider(
+            SparseArray<VibrationEffect> customizations) {
+        return new HapticFeedbackVibrationProvider(
+            mResourcesMock, mVibratorInfo, customizations, mViewFeatureFlags);
     }
 
     private void mockVibratorPrimitiveSupport(int... supportedPrimitives) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 0eec9cd..40e0e84 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
@@ -46,6 +47,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.res.Resources;
 import android.hardware.input.IInputManager;
@@ -87,6 +89,7 @@
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
+import android.view.flags.FeatureFlags;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
@@ -172,6 +175,8 @@
     private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
     @Mock
     private AudioManager mAudioManagerMock;
+    @Mock
+    private FeatureFlags mViewFeatureFlags;
 
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
 
@@ -321,7 +326,8 @@
                     HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
                             Resources resources, VibratorInfo vibratorInfo) {
                         return new HapticFeedbackVibrationProvider(
-                                resources, vibratorInfo, mHapticFeedbackVibrationMap);
+                                resources, vibratorInfo, mHapticFeedbackVibrationMap,
+                                mViewFeatureFlags);
                     }
                 });
         return mService;
@@ -649,6 +655,42 @@
     }
 
     @Test
+    public void vibrate_withoutBypassFlagsPermissions_bypassFlagsNotApplied() throws Exception {
+        denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+        assertCanVibrateWithBypassFlags(false);
+    }
+
+    @Test
+    public void vibrate_withSecureSettingsPermission_bypassFlagsApplied() throws Exception {
+        grantPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+        assertCanVibrateWithBypassFlags(true);
+    }
+
+    @Test
+    public void vibrate_withModifyPhoneStatePermission_bypassFlagsApplied() throws Exception {
+        denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+        assertCanVibrateWithBypassFlags(true);
+    }
+
+    @Test
+    public void vibrate_withModifyAudioRoutingPermission_bypassFlagsApplied() throws Exception {
+        denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        grantPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+        assertCanVibrateWithBypassFlags(true);
+    }
+
+    @Test
     public void vibrate_withRingtone_usesRingerModeSettings() throws Exception {
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1166,7 +1208,7 @@
     }
 
     @Test
-    public void vibrate_withTriggerCallback_finishesVibration() throws Exception {
+    public void vibrate_withriggerCallback_finishesVibration() throws Exception {
         mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
         mockVibrators(1, 2);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1303,10 +1345,18 @@
     }
 
     @Test
-    public void performHapticFeedback_doesNotRequirePermission() throws Exception {
+    public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception {
+        // Deny permissions that would have been required for regular vibrations, and check that
+        // the vibration proceed as expected to verify that haptic feedback does not need these
+        // permissions.
         denyPermission(android.Manifest.permission.VIBRATE);
+        denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+        // Flag override to enable the scroll feedack constants to bypass interruption policies.
+        when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
         mHapticFeedbackVibrationMap.put(
-                HapticFeedbackConstants.KEYBOARD_TAP,
+                HapticFeedbackConstants.SCROLL_TICK,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1315,13 +1365,16 @@
 
         HalVibration vibration =
                 performHapticFeedbackAndWaitUntilFinished(
-                        service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true);
+                        service, HapticFeedbackConstants.SCROLL_TICK, /* always= */ true);
 
         List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
         assertEquals(1, playedSegments.size());
         PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
         assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
-        assertEquals(VibrationAttributes.USAGE_TOUCH, vibration.callerInfo.attrs.getUsage());
+        VibrationAttributes attrs = vibration.callerInfo.attrs;
+        assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrs.getUsage());
+        assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
+        assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
     }
 
     @Test
@@ -2261,6 +2314,31 @@
         assertNull(metrics.halUnsupportedEffectsUsed);
     }
 
+    private void assertCanVibrateWithBypassFlags(boolean expectedCanApplyBypassFlags)
+            throws Exception {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration vibration = vibrateAndWaitUntilFinished(
+                service,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
+                new VibrationAttributes.Builder()
+                        .setUsage(VibrationAttributes.USAGE_TOUCH)
+                        .setFlags(
+                                VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+                                        | VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)
+                        .build());
+
+        VibrationAttributes attrs = vibration.callerInfo.attrs;
+        assertEquals(
+                expectedCanApplyBypassFlags,
+                attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
+        assertEquals(
+                expectedCanApplyBypassFlags,
+                attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
+    }
+
     private VibrationEffectSegment expectedPrebaked(int effectId) {
         return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
     }
@@ -2327,12 +2405,13 @@
         return vib;
     }
 
-    private void vibrateAndWaitUntilFinished(VibratorManagerService service, VibrationEffect effect,
-            VibrationAttributes attrs) throws InterruptedException {
-        vibrateAndWaitUntilFinished(service, CombinedVibration.createParallel(effect), attrs);
+    private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
+            VibrationEffect effect, VibrationAttributes attrs) throws InterruptedException {
+        return vibrateAndWaitUntilFinished(
+                service, CombinedVibration.createParallel(effect), attrs);
     }
 
-    private void vibrateAndWaitUntilFinished(VibratorManagerService service,
+    private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
             CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
         HalVibration vib =
                 service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
@@ -2340,6 +2419,8 @@
         if (vib != null) {
             vib.waitForEnd();
         }
+
+        return vib;
     }
 
     private void vibrate(VibratorManagerService service, VibrationEffect effect,
@@ -2368,7 +2449,15 @@
         return predicateResult;
     }
 
+    private void grantPermission(String permission) {
+        when(mContextSpy.checkCallingOrSelfPermission(permission))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        doNothing().when(mContextSpy).enforceCallingOrSelfPermission(eq(permission), anyString());
+    }
+
     private void denyPermission(String permission) {
+        when(mContextSpy.checkCallingOrSelfPermission(permission))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
         doThrow(new SecurityException()).when(mContextSpy)
                 .enforceCallingOrSelfPermission(eq(permission), anyString());
     }
diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp
index e704ebf..744cb63 100644
--- a/services/tests/voiceinteractiontests/Android.bp
+++ b/services/tests/voiceinteractiontests/Android.bp
@@ -43,7 +43,7 @@
         "services.soundtrigger",
         "servicestests-core-utils",
         "servicestests-utils-mockito-extended",
-        "truth-prebuilt",
+        "truth",
     ],
 
     libs: [
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index c2812a1..af39b2f 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,12 +57,14 @@
         "platform-test-annotations",
         "servicestests-utils",
         "testng",
-        "truth-prebuilt",
+        "truth",
         "testables",
         "hamcrest-library",
         "platform-compat-test-rules",
         "CtsSurfaceValidatorLib",
         "service-sdksandbox.impl",
+        "com.android.window.flags.window-aconfig-java",
+        "flag-junit",
     ],
 
     libs: [
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 42e3383..762e23c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -47,6 +47,8 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/>
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+    <uses-permission android:name="android.permission.MONITOR_INPUT"/>
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
@@ -104,6 +106,11 @@
             android:showWhenLocked="true"
             android:turnScreenOn="true" />
 
+        <activity android:name="android.app.Activity"
+            android:exported="true"
+            android:showWhenLocked="true"
+            android:turnScreenOn="true" />
+
         <activity
             android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
             android:exported="true">
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 0a7bb00..71098aa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -20,6 +20,7 @@
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
 
 import android.platform.test.annotations.Presubmit;
 import android.view.KeyEvent;
@@ -284,6 +285,16 @@
                         KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_H, META_ON}};
     }
 
+    @Keep
+    private static Object[][] shortPressOnSettingsTestArguments() {
+        // testName, testKeys, shortPressOnSettingsBehavior, expectedLogEvent, expectedKey,
+        // expectedModifierState
+        return new Object[][]{
+                {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
+                        SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL,
+                        KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_SETTINGS, 0}};
+    }
+
     @Before
     public void setUp() {
         setUpPhoneWindowManager(/*supportSettingsUpdate*/ true);
@@ -294,6 +305,7 @@
         mPhoneWindowManager.overrideEnableBugReportTrigger(true);
         mPhoneWindowManager.overrideStatusBarManagerInternal();
         mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.overrideSendBroadcast();
         mPhoneWindowManager.overrideUserSetupComplete();
         mPhoneWindowManager.setupAssistForLaunch();
         mPhoneWindowManager.overrideTogglePanel();
@@ -330,4 +342,15 @@
         mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
                 expectedKey, expectedModifierState, "Failed while executing " + testName);
     }
+
+    @Test
+    @Parameters(method = "shortPressOnSettingsTestArguments")
+    public void testShortPressOnSettings(String testName, int[] testKeys,
+            int shortPressOnSettingsBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey,
+            int expectedModifierState) {
+        mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior);
+        sendKeyCombination(testKeys, 0 /* duration */);
+        mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
+                expectedKey, expectedModifierState, "Failed while executing " + testName);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ef28ffa..2244dbe 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -375,6 +375,10 @@
         mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior;
     }
 
+    void overrideShortPressOnSettingsBehavior(int behavior) {
+        mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior;
+    }
+
     void overrideCanStartDreaming(boolean canDream) {
         doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean());
     }
@@ -484,6 +488,10 @@
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
     }
 
+    void overrideSendBroadcast() {
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+    }
+
     void overrideUserSetupComplete() {
         doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3bc6450..c241033 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -549,10 +549,12 @@
 
         // Let's pretend that the app has crashed.
         firstActivity.app.setThread(null);
-        mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+        final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities(
+                firstActivity.app, "test");
 
         // Verify that the root task was removed.
         assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+        assertEquals(rootTask, finishedTask);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index c1d5147..8119fd4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -138,11 +138,11 @@
         IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
 
         WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
-                mScvh1.getFocusGrantToken(), true);
+                mScvh1.getInputTransferToken(), true);
         assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true));
 
         WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
-                mScvh2.getFocusGrantToken(), true);
+                mScvh2.getInputTransferToken(), true);
         assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 91256ee..4a33594 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -216,7 +216,7 @@
         try {
             final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
             mWm.mTaskSnapshotController.createSnapshot(mAppWindow.mActivityRecord.getTask(),
-                    1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder);
+                    1f /* scaleFraction */, new Rect() /* crop */, builder);
         } catch (NullPointerException e) {
             fail("There should be no exception when calling createTaskSnapshot");
         }
@@ -238,7 +238,7 @@
             spyOn(builder);
             mWm.mTaskSnapshotController.createSnapshot(
                     mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */,
-                    PixelFormat.UNKNOWN, null /* outTaskSize */, builder);
+                    new Rect() /* crop */, builder);
             // Verify the builder should includes IME surface.
             verify(builder).setHasImeSurface(eq(true));
             builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
@@ -261,7 +261,7 @@
         final TaskSnapshot.Builder builder =
                 new TaskSnapshot.Builder();
         boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot(
-                mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder);
+                mAppWindow.mActivityRecord.getTask(), builder) != null;
 
         assertTrue(success);
         // The pixel format should be selected automatically.
@@ -270,7 +270,7 @@
         // Snapshot should not be taken while the rotation of activity and task are different.
         doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform();
         success = mWm.mTaskSnapshotController.prepareTaskSnapshot(
-                mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder);
+                mAppWindow.mActivityRecord.getTask(), builder) != null;
 
         assertFalse(success);
     }
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 241f7d8..474720f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1442,7 +1442,7 @@
         // normally.
         mWm.mSyncEngine.abort(openTransition.getSyncId());
 
-        verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2), eq(false));
+        verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2));
 
         controller.finishTransition(openTransition);
 
@@ -1482,7 +1482,7 @@
 
         // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be
         // called until finish).
-        verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1), eq(false));
+        verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1));
 
         enteringAnimReports.clear();
         doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
@@ -1515,7 +1515,7 @@
         assertFalse(activity1.isVisible());
         assertFalse(activity1.app.hasActivityInVisibleTask());
 
-        verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1), eq(false));
+        verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1));
         assertTrue(enteringAnimReports.contains(activity2));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
new file mode 100644
index 0000000..ac49839
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
@@ -0,0 +1,217 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+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.server.wm.BuildUtils;
+import android.server.wm.CtsWindowInfoUtils;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.window.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+public class TrustedOverlayTests {
+    private static final String TAG = "TrustedOverlayTests";
+    private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Rule
+    public TestName mName = new TestName();
+
+    @Rule
+    public final ActivityScenarioRule<Activity> mActivityRule = new ActivityScenarioRule<>(
+            Activity.class);
+
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivityRule.getScenario().onActivity(activity -> {
+            mActivity = activity;
+        });
+    }
+
+    @RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)
+    @Test
+    public void setTrustedOverlayInputWindow() throws InterruptedException {
+        testTrustedOverlayChildHelper(false);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)
+    public void setTrustedOverlayChildLayer() throws InterruptedException {
+        testTrustedOverlayChildHelper(true);
+    }
+
+    /**
+     * b/300659960 where setting spy window and trusted overlay were not happening in the same
+     * transaction causing the system to crash. This ensures there are no synchronization issues
+     * setting both spy window and trusted overlay.
+     */
+    @Test
+    public void setSpyWindowDoesntCrash() throws InterruptedException {
+        IBinder[] tokens = new IBinder[1];
+        CountDownLatch hostTokenReady = new CountDownLatch(1);
+        mInstrumentation.runOnMainSync(() -> {
+            WindowManager.LayoutParams params = mActivity.getWindow().getAttributes();
+            params.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+            params.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY;
+            mActivity.getWindow().setAttributes(params);
+
+            View rootView = mActivity.getWindow().getDecorView();
+            if (rootView.isAttachedToWindow()) {
+                tokens[0] = rootView.getWindowToken();
+                hostTokenReady.countDown();
+            } else {
+                rootView.getViewTreeObserver().addOnWindowAttachListener(
+                        new ViewTreeObserver.OnWindowAttachListener() {
+                            @Override
+                            public void onWindowAttached() {
+                                tokens[0] = rootView.getWindowToken();
+                                hostTokenReady.countDown();
+                            }
+
+                            @Override
+                            public void onWindowDetached() {
+                            }
+                        });
+            }
+        });
+
+        assertTrue("Failed to wait for host to get added",
+                hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
+
+        boolean[] foundTrusted = new boolean[1];
+        CtsWindowInfoUtils.waitForWindowInfos(
+                windowInfos -> {
+                    for (var windowInfo : windowInfos) {
+                        if (windowInfo.windowToken == tokens[0] && windowInfo.isTrustedOverlay) {
+                            foundTrusted[0] = true;
+                            return true;
+                        }
+                    }
+                    return false;
+                }, TIMEOUT_S, TimeUnit.SECONDS);
+
+        if (!foundTrusted[0]) {
+            CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
+        }
+
+        assertTrue("Failed to find window or was not marked trusted", foundTrusted[0]);
+    }
+
+    private void testTrustedOverlayChildHelper(boolean expectedTrustedChild)
+            throws InterruptedException {
+        IBinder[] tokens = new IBinder[2];
+        CountDownLatch hostTokenReady = new CountDownLatch(1);
+        mInstrumentation.runOnMainSync(() -> {
+            mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
+            View rootView = mActivity.getWindow().getDecorView();
+            if (rootView.isAttachedToWindow()) {
+                tokens[0] = rootView.getWindowToken();
+                hostTokenReady.countDown();
+            } else {
+                rootView.getViewTreeObserver().addOnWindowAttachListener(
+                        new ViewTreeObserver.OnWindowAttachListener() {
+                            @Override
+                            public void onWindowAttached() {
+                                tokens[0] = rootView.getWindowToken();
+                                hostTokenReady.countDown();
+                            }
+
+                            @Override
+                            public void onWindowDetached() {
+                            }
+                        });
+            }
+        });
+
+        assertTrue("Failed to wait for host to get added",
+                hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
+
+        mInstrumentation.runOnMainSync(() -> {
+            WindowManager wm = mActivity.getSystemService(WindowManager.class);
+
+            View childView = new View(mActivity) {
+                @Override
+                protected void onAttachedToWindow() {
+                    super.onAttachedToWindow();
+                    tokens[1] = getWindowToken();
+                }
+            };
+            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+            params.token = tokens[0];
+            params.type = TYPE_APPLICATION_PANEL;
+            wm.addView(childView, params);
+        });
+
+        boolean[] foundTrusted = new boolean[2];
+
+        CtsWindowInfoUtils.waitForWindowInfos(
+                windowInfos -> {
+                    for (var windowInfo : windowInfos) {
+                        if (windowInfo.windowToken == tokens[0]
+                                && windowInfo.isTrustedOverlay) {
+                            foundTrusted[0] = true;
+                        } else if (windowInfo.windowToken == tokens[1]
+                                && windowInfo.isTrustedOverlay) {
+                            foundTrusted[1] = true;
+                        }
+                    }
+                    return foundTrusted[0] && foundTrusted[1];
+                }, TIMEOUT_S, TimeUnit.SECONDS);
+
+        if (!foundTrusted[0] || !foundTrusted[1]) {
+            CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
+        }
+
+        assertTrue("Failed to find parent window or was not marked trusted", foundTrusted[0]);
+        assertEquals("Failed to find child window or was not marked trusted", expectedTrustedChild,
+                foundTrusted[1]);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java b/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
deleted file mode 100644
index 6801c94..0000000
--- a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
+++ /dev/null
@@ -1,80 +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.usage;
-
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-import android.os.Trace;
-
-import java.util.concurrent.Executor;
-
-/**
- * Shared singleton default priority thread for usage stats message handling.
- *
- * @see com.android.internal.os.BackgroundThread
- */
-public final class UsageStatsHandlerThread extends HandlerThread {
-    private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
-    private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
-    private static UsageStatsHandlerThread sInstance;
-    private static Handler sHandler;
-    private static Executor sHandlerExecutor;
-
-    private UsageStatsHandlerThread() {
-        super("usagestats.default", Process.THREAD_PRIORITY_DEFAULT);
-    }
-
-    private static void ensureThreadLocked() {
-        if (sInstance == null) {
-            sInstance = new UsageStatsHandlerThread();
-            sInstance.start();
-            final Looper looper = sInstance.getLooper();
-            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
-            looper.setSlowLogThresholdMs(
-                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
-            sHandler = new Handler(sInstance.getLooper());
-            sHandlerExecutor = new HandlerExecutor(sHandler);
-        }
-    }
-
-    /** Returns the UsageStatsHandlerThread singleton */
-    public static UsageStatsHandlerThread get() {
-        synchronized (UsageStatsHandlerThread.class) {
-            ensureThreadLocked();
-            return sInstance;
-        }
-    }
-
-    /** Returns the singleton handler for UsageStatsHandlerThread */
-    public static Handler getHandler() {
-        synchronized (UsageStatsHandlerThread.class) {
-            ensureThreadLocked();
-            return sHandler;
-        }
-    }
-
-    /** Returns the singleton handler executor for UsageStatsHandlerThread */
-    public static Executor getExecutor() {
-        synchronized (UsageStatsHandlerThread.class) {
-            ensureThreadLocked();
-            return sHandlerExecutor;
-        }
-    }
-}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 18c960e..f3bf026 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -332,8 +332,7 @@
         mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
         mPackageManager = getContext().getPackageManager();
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-
-        mHandler = new H(UsageStatsHandlerThread.get().getLooper());
+        mHandler = new H(BackgroundThread.get().getLooper());
         mIoHandler = new Handler(IoThread.get().getLooper(), mIoHandlerCallback);
 
         mAppStandby = mInjector.getAppStandbyController(getContext());
@@ -495,9 +494,12 @@
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "loadPendingEvents");
             loadPendingEventsLocked(userId, pendingEvents);
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-            final LinkedList<Event> eventsInMem = mReportedEvents.get(userId);
-            if (eventsInMem != null) {
-                pendingEvents.addAll(eventsInMem);
+            synchronized (mReportedEvents) {
+                final LinkedList<Event> eventsInMem = mReportedEvents.get(userId);
+                if (eventsInMem != null) {
+                    pendingEvents.addAll(eventsInMem);
+                    mReportedEvents.remove(userId);
+                }
             }
             boolean needToFlush = !pendingEvents.isEmpty();
 
@@ -518,8 +520,7 @@
             mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK,
                     userId, 0).sendToTarget();
 
-            // Remove all the stats stored in memory and in system DE.
-            mReportedEvents.remove(userId);
+            // Remove all the stats stored in system DE.
             deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));
 
             // Force a flush to disk for the current user to ensure important events are persisted.
@@ -916,6 +917,7 @@
         }
     }
 
+    @GuardedBy({"mLock", "mReportedEvents"})
     private void persistPendingEventsLocked(int userId) {
         final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
         if (pendingEvents == null || pendingEvents.isEmpty()) {
@@ -1019,7 +1021,7 @@
                     + UserUsageStatsService.eventToString(event.mEventType);
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, traceTag);
         }
-        synchronized (mLock) {
+        synchronized (mReportedEvents) {
             LinkedList<Event> events = mReportedEvents.get(userId);
             if (events == null) {
                 events = new LinkedList<>();
@@ -1943,16 +1945,19 @@
                         idpw.println();
                     }
                 } else {
-                    final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
-                    if (pendingEvents != null && !pendingEvents.isEmpty()) {
-                        final int eventCount = pendingEvents.size();
-                        idpw.println("Pending events: count=" + eventCount);
-                        idpw.increaseIndent();
-                        for (int idx = 0; idx < eventCount; idx++) {
-                            UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), true);
+                    synchronized (mReportedEvents) {
+                        final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
+                        if (pendingEvents != null && !pendingEvents.isEmpty()) {
+                            final int eventCount = pendingEvents.size();
+                            idpw.println("Pending events: count=" + eventCount);
+                            idpw.increaseIndent();
+                            for (int idx = 0; idx < eventCount; idx++) {
+                                UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx),
+                                        true);
+                            }
+                            idpw.decreaseIndent();
+                            idpw.println();
                         }
-                        idpw.decreaseIndent();
-                        idpw.println();
                     }
                 }
                 idpw.decreaseIndent();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a5e0638..98bbb40 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -519,8 +519,6 @@
     /**
      * Used in the Preferred Network Types menu to determine if the 2G option is displayed.
      * Value defaults to false as of Android T to discourage the use of insecure 2G protocols.
-     *
-     * @see #KEY_HIDE_ENABLE_2G
      */
     public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
 
@@ -9541,9 +9539,9 @@
      * Used to trade privacy/security against potentially reduced carrier coverage for some
      * carriers.
      *
-     * @deprecated Future versions of Android will disallow carriers from hiding this toggle
-     * because disabling 2g is a security feature that users should always have access to at
-     * their discretion.
+     * @removed This config option is no longer supported as it was hiding a security feature
+     * from users. Setting this option will not change the behavior of the Settings menu starting
+     * in Android V.
      */
     @Deprecated
     public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index de2e952..6ebf3be 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -259,7 +259,7 @@
     /**
      * Whether this subscription is used for communicating with non-terrestrial networks.
      */
-    private final boolean mIsNtn;
+    private final boolean mIsOnlyNonTerrestrialNetwork;
 
     /**
      * @hide
@@ -385,7 +385,7 @@
         this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
         this.mPortIndex = portIndex;
         this.mUsageSetting = usageSetting;
-        this.mIsNtn = false;
+        this.mIsOnlyNonTerrestrialNetwork = false;
     }
 
     /**
@@ -424,7 +424,7 @@
         this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
         this.mPortIndex = builder.mPortIndex;
         this.mUsageSetting = builder.mUsageSetting;
-        this.mIsNtn = builder.mIsNtn;
+        this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
     }
 
     /**
@@ -878,8 +878,8 @@
      * otherwise.
      */
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    public boolean isNtn() {
-        return mIsNtn;
+    public boolean isOnlyNonTerrestrialNetwork() {
+        return mIsOnlyNonTerrestrialNetwork;
     }
 
     @NonNull
@@ -918,7 +918,7 @@
                             UiccAccessRule.CREATOR))
                     .setUiccApplicationsEnabled(source.readBoolean())
                     .setUsageSetting(source.readInt())
-                    .setNtn(source.readBoolean())
+                    .setOnlyNonTerrestrialNetwork(source.readBoolean())
                     .build();
         }
 
@@ -960,7 +960,7 @@
         dest.writeTypedArray(mCarrierConfigAccessRules, flags);
         dest.writeBoolean(mAreUiccApplicationsEnabled);
         dest.writeInt(mUsageSetting);
-        dest.writeBoolean(mIsNtn);
+        dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
     }
 
     @Override
@@ -1023,7 +1023,7 @@
                 + " mType=" + SubscriptionManager.subscriptionTypeToString(mType)
                 + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
                 + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
-                + " ntn=" + mIsNtn
+                + " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
                 + "]";
     }
 
@@ -1049,7 +1049,7 @@
                 that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
                 that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
                 && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
-                && mIsNtn == that.mIsNtn;
+                && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork;
     }
 
     @Override
@@ -1058,7 +1058,7 @@
                 mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
                 mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
                 mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
-                mIsGroupDisabled, mIsNtn);
+                mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork);
         result = 31 * result + Arrays.hashCode(mEhplmns);
         result = 31 * result + Arrays.hashCode(mHplmns);
         result = 31 * result + Arrays.hashCode(mNativeAccessRules);
@@ -1260,7 +1260,7 @@
         /**
          * {@code true} if it is a non-terrestrial network subscription, {@code false} otherwise.
          */
-        private boolean mIsNtn = false;
+        private boolean mIsOnlyNonTerrestrialNetwork = false;
 
         /**
          * Default constructor.
@@ -1304,7 +1304,7 @@
             mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
             mPortIndex = info.mPortIndex;
             mUsageSetting = info.mUsageSetting;
-            mIsNtn = info.mIsNtn;
+            mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
         }
 
         /**
@@ -1692,12 +1692,13 @@
         /**
          * Set whether the subscription is exclusively used for non-terrestrial networks or not.
          *
-         * @param isNtn {@code true} if the subscription is for NTN, {@code false} otherwise.
+         * @param isOnlyNonTerrestrialNetwork {@code true} if the subscription is for NTN,
+         * {@code false} otherwise.
          * @return The builder.
          */
         @NonNull
-        public Builder setNtn(boolean isNtn) {
-            mIsNtn = isNtn;
+        public Builder setOnlyNonTerrestrialNetwork(boolean isOnlyNonTerrestrialNetwork) {
+            mIsOnlyNonTerrestrialNetwork = isOnlyNonTerrestrialNetwork;
             return this;
         }
 
diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp
index 5233a5b..c2a7015 100644
--- a/tests/BatteryStatsPerfTest/Android.bp
+++ b/tests/BatteryStatsPerfTest/Android.bp
@@ -27,7 +27,7 @@
     static_libs: [
         "androidx.test.rules",
         "apct-perftests-utils",
-        "truth-prebuilt",
+        "truth",
     ],
     platform_apis: true,
     certificate: "platform",
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index 615990f..38cb9869 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -30,7 +30,7 @@
         "compatibility-host-util",
     ],
     static_libs: [
-        "truth-prebuilt",
+        "truth",
     ],
     data: [
         ":BinaryTransparencyTestApp",
diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp
index c4faf7f..1fb73e2 100644
--- a/tests/BlobStoreTestUtils/Android.bp
+++ b/tests/BlobStoreTestUtils/Android.bp
@@ -22,12 +22,12 @@
 }
 
 java_library {
-  name: "BlobStoreTestUtils",
-  srcs: ["src/**/*.java"],
-  static_libs: [
-    "truth-prebuilt",
-    "androidx.test.uiautomator_uiautomator",
-    "androidx.test.ext.junit",
-  ],
-  sdk_version: "test_current",
+    name: "BlobStoreTestUtils",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "truth",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.ext.junit",
+    ],
+    sdk_version: "test_current",
 }
diff --git a/tests/ChoreographerTests/Android.bp b/tests/ChoreographerTests/Android.bp
index ca30267..5d49120 100644
--- a/tests/ChoreographerTests/Android.bp
+++ b/tests/ChoreographerTests/Android.bp
@@ -34,7 +34,7 @@
         "androidx.test.rules",
         "compatibility-device-util-axt",
         "com.google.android.material_material",
-        "truth-prebuilt",
+        "truth",
     ],
     jni_libs: [
         "libchoreographertests_jni",
diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp
index 6809521..96e4a9e 100644
--- a/tests/CtsSurfaceControlTestsStaging/Android.bp
+++ b/tests/CtsSurfaceControlTestsStaging/Android.bp
@@ -37,7 +37,7 @@
         "compatibility-device-util-axt",
         "com.google.android.material_material",
         "SurfaceFlingerProperties",
-        "truth-prebuilt",
+        "truth",
     ],
     resource_dirs: ["src/main/res"],
     certificate: "platform",
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
index 448d46f..3f2c808 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
@@ -47,7 +47,7 @@
 
     static_libs: [
         "androidx.test.rules",
-        "truth-prebuilt",
+        "truth",
     ],
 
     compile_multilib: "both",
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index a2ae56e..82aa85d 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -236,7 +236,7 @@
     static_libs: [
         "flickerlib",
         "flickerlib-helpers",
-        "truth-prebuilt",
+        "truth",
         "app-helpers-core",
     ],
 }
@@ -255,7 +255,7 @@
         "flickerlib",
         "flickerlib-apphelpers",
         "flickerlib-helpers",
-        "truth-prebuilt",
+        "truth",
         "app-helpers-core",
         "wm-flicker-window-extensions",
     ],
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index c30786f..da51eff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.helpers
 
 import android.app.Instrumentation
+import android.content.Intent
 import android.media.session.MediaController
 import android.media.session.MediaSessionManager
 import android.tools.common.datatypes.Rect
@@ -267,6 +268,11 @@
     fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
         launchViaIntent(wmHelper)
 
+    fun changeAspectRatio() {
+        val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
+        context.sendBroadcast(intent)
+    }
+
     fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
         clickObject(ENTER_PIP_BUTTON_ID)
 
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index cdb1d42..12eaad1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -82,6 +82,8 @@
             "com.android.wm.shell.flicker.testapp.SWITCH_OFF";
     private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON";
     private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR";
+    private static final String ACTION_ASPECT_RATIO =
+            "com.android.wm.shell.flicker.testapp.ASPECT_RATIO";
 
     private final PictureInPictureParams.Builder mPipParamsBuilder =
             new PictureInPictureParams.Builder()
@@ -109,6 +111,9 @@
                     case ACTION_CLEAR:
                         mPipParamsBuilder.setActions(Collections.emptyList());
                         break;
+                    case ACTION_ASPECT_RATIO:
+                        mPipParamsBuilder.setAspectRatio(RATIO_TALL);
+                        break;
                     case ACTION_NO_OP:
                         return;
                     default:
@@ -190,6 +195,7 @@
         filter.addAction(ACTION_CLEAR);
         filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
         filter.addAction(ACTION_ENTER_PIP);
+        filter.addAction(ACTION_ASPECT_RATIO);
         registerReceiver(mBroadcastReceiver, filter);
 
         handleIntentExtra(getIntent());
diff --git a/tests/FsVerityTest/TEST_MAPPING b/tests/FsVerityTest/TEST_MAPPING
index 39944be..7d59d77 100644
--- a/tests/FsVerityTest/TEST_MAPPING
+++ b/tests/FsVerityTest/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FsVerityTest"
-    },
-    // nextgen test only runs during postsubmit.
-    {
-      "name": "FsVerityTest",
-      "keywords": ["nextgen"]
     }
   ]
 }
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 365e00e..cf2d5d6 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,6 +9,10 @@
 
 android_test {
     name: "InputTests",
+    defaults: [
+        // For ExtendedMockito dependencies.
+        "modules-utils-testable-device-config-defaults",
+    ],
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
@@ -35,7 +39,7 @@
         "services.core.unboosted",
         "testables",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.mock",
diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
deleted file mode 100644
index 1b98887..0000000
--- a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.input;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-import android.view.ViewConfiguration;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Build/Install/Run:
- * atest FocusEventDebugViewTest
- */
-@RunWith(AndroidJUnit4.class)
-public class FocusEventDebugViewTest {
-
-    private FocusEventDebugView mFocusEventDebugView;
-    private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView;
-    private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView;
-    private float mScaledVerticalScrollFactor;
-
-    @Before
-    public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getContext();
-        mScaledVerticalScrollFactor =
-                ViewConfiguration.get(context).getScaledVerticalScrollFactor();
-        InputManagerService mockService = mock(InputManagerService.class);
-        when(mockService.monitorInput(anyString(), anyInt()))
-                .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
-
-        mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context);
-        mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context);
-        mFocusEventDebugView = new FocusEventDebugView(context, mockService,
-                () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
-    }
-
-    @Test
-    public void startsRotaryInputValueViewWithDefaultValue() {
-        assertEquals("+0.0", mRotaryInputValueView.getText());
-    }
-
-    @Test
-    public void startsRotaryInputGraphViewWithDefaultFrameCenter() {
-        assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
-    }
-
-    @Test
-    public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() {
-        mFocusEventDebugView.handleUpdateShowRotaryInput(true);
-
-        mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f));
-
-        assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor),
-                mRotaryInputValueView.getText());
-    }
-
-    @Test
-    public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() {
-        mFocusEventDebugView.handleUpdateShowRotaryInput(true);
-
-        mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f));
-
-        assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
-    }
-
-    @Test
-    public void updateActivityStatus_setsAndRemovesColorFilter() {
-        // It should not be active initially.
-        assertNull(mRotaryInputValueView.getBackground().getColorFilter());
-
-        mRotaryInputValueView.updateActivityStatus(true);
-        // It should be active after rotary input.
-        assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
-
-        mRotaryInputValueView.updateActivityStatus(false);
-        // It should not be active after waiting for mUpdateActivityStatusCallback.
-        assertNull(mRotaryInputValueView.getBackground().getColorFilter());
-    }
-
-    private MotionEvent createRotaryMotionEvent(float scrollAxisValue) {
-        PointerCoords pointerCoords = new PointerCoords();
-        pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
-        PointerProperties pointerProperties = new PointerProperties();
-
-        return MotionEvent.obtain(
-                /* downTime */ 0,
-                /* eventTime */ 0,
-                /* action */ MotionEvent.ACTION_SCROLL,
-                /* pointerCount */ 1,
-                /* pointerProperties */ new PointerProperties[] {pointerProperties},
-                /* pointerCoords */ new PointerCoords[] {pointerCoords},
-                /* metaState */ 0,
-                /* buttonState */ 0,
-                /* xPrecision */ 0,
-                /* yPrecision */ 0,
-                /* deviceId */ 0,
-                /* edgeFlags */ 0,
-                /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
-                /* flags */ 0
-        );
-    }
-}
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index b6477510..fa86e9c 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -32,11 +32,16 @@
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.provider.Settings
+import android.util.proto.ProtoOutputStream
 import android.view.InputDevice
 import android.view.inputmethod.InputMethodInfo
 import android.view.inputmethod.InputMethodSubtype
 import androidx.test.core.R
 import androidx.test.core.app.ApplicationProvider
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.os.KeyboardConfiguredProto
+import com.android.internal.util.FrameworkStatsLog
+import com.android.modules.utils.testing.ExtendedMockitoRule
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
@@ -46,9 +51,9 @@
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.junit.MockitoJUnit
 import java.io.FileNotFoundException
 import java.io.FileOutputStream
 import java.io.IOException
@@ -96,6 +101,9 @@
         private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us"
         private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk"
         private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1"
+        const val LAYOUT_TYPE_QWERTZ = 2
+        const val LAYOUT_TYPE_QWERTY = 1
+        const val LAYOUT_TYPE_DEFAULT = 0
     }
 
     private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME)
@@ -103,8 +111,10 @@
     private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR =
         createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME)
 
-    @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    @JvmField
+    @Rule
+    val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+            .mockStatic(FrameworkStatsLog::class.java).build()!!
 
     @Mock
     private lateinit var iInputManager: IInputManager
@@ -145,7 +155,9 @@
             override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
         })
         testLooper = TestLooper()
-        keyboardLayoutManager = KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+        keyboardLayoutManager = Mockito.spy(
+            KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+        )
         setupInputDevices()
         setupBroadcastReceiver()
         setupIme()
@@ -827,6 +839,100 @@
         }
     }
 
+    @Test
+    fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() {
+        val imeInfos = listOf(
+                KeyboardLayoutManager.ImeInfo(0, imeInfo,
+                        createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+            ExtendedMockito.verify {
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.eq(keyboardDevice.vendorId),
+                        ArgumentMatchers.eq(keyboardDevice.productId),
+                        ArgumentMatchers.eq(createByteArray(
+                                KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+                                LAYOUT_TYPE_DEFAULT,
+                                "German",
+                                KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+                                "de-Latn",
+                                LAYOUT_TYPE_QWERTZ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() {
+        val imeInfos = listOf(
+                KeyboardLayoutManager.ImeInfo(0, imeInfo,
+                        createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
+            ExtendedMockito.verify {
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
+                        ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
+                        ArgumentMatchers.eq(createByteArray(
+                                "en",
+                                LAYOUT_TYPE_QWERTY,
+                                "English (US)",
+                                KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+                                "de-Latn",
+                                LAYOUT_TYPE_QWERTZ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() {
+        val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+            ExtendedMockito.verify {
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.eq(keyboardDevice.vendorId),
+                        ArgumentMatchers.eq(keyboardDevice.productId),
+                        ArgumentMatchers.eq(createByteArray(
+                                KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+                                LAYOUT_TYPE_DEFAULT,
+                                "Default",
+                                KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+                                KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+                                LAYOUT_TYPE_DEFAULT))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testConfigurationNotLogged_onInputDeviceChanged() {
+        val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+            ExtendedMockito.verify({
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any(ByteArray::class.java)
+                )
+            }, Mockito.times(0))
+        }
+    }
+
     private fun assertCorrectLayout(
         device: InputDevice,
         imeSubtype: InputMethodSubtype,
@@ -842,18 +948,60 @@
     }
 
     private fun createImeSubtype(): InputMethodSubtype =
-        InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++).build()
+            createImeSubtypeForLanguageTagAndLayoutType(null, null)
 
     private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype =
-        InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++)
-            .setLanguageTag(languageTag).build()
+            createImeSubtypeForLanguageTagAndLayoutType(languageTag, null)
 
     private fun createImeSubtypeForLanguageTagAndLayoutType(
-        languageTag: String,
-        layoutType: String
-    ): InputMethodSubtype =
-        InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++)
-            .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build()
+            languageTag: String?,
+            layoutType: String?
+    ): InputMethodSubtype {
+        val builder = InputMethodSubtype.InputMethodSubtypeBuilder()
+                .setSubtypeId(nextImeSubtypeId++)
+                .setIsAuxiliary(false)
+                .setSubtypeMode("keyboard")
+        if (languageTag != null && layoutType != null) {
+            builder.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType)
+        } else if (languageTag != null) {
+            builder.setLanguageTag(languageTag)
+        }
+        return builder.build()
+    }
+
+    private fun createByteArray(
+            expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String,
+            expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray {
+        val proto = ProtoOutputStream()
+        val keyboardLayoutConfigToken = proto.start(
+                KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
+                expectedLanguageTag
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
+                expectedLayoutType
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
+                expectedLayoutName
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
+                expectedCriteria
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG,
+                expectedImeLanguageTag
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
+                expectedImeLayoutType
+        )
+        proto.end(keyboardLayoutConfigToken);
+        return proto.bytes
+    }
 
     private fun hasLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean {
         for (kl in layoutList) {
diff --git a/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
new file mode 100644
index 0000000..ae7fb3b
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.input.debug;
+
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.input.InputManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest FocusEventDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FocusEventDebugViewTest {
+
+    private FocusEventDebugView mFocusEventDebugView;
+    private RotaryInputValueView mRotaryInputValueView;
+    private RotaryInputGraphView mRotaryInputGraphView;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        InputManagerService mockService = mock(InputManagerService.class);
+        when(mockService.monitorInput(anyString(), anyInt()))
+                .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
+
+        mRotaryInputValueView = spy(new RotaryInputValueView(context));
+        mRotaryInputGraphView = spy(new RotaryInputGraphView(context));
+        mFocusEventDebugView = new FocusEventDebugView(context, mockService,
+                () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
+    }
+
+    @Test
+    public void handleRotaryInput_sendsMotionEventWhenEnabled() {
+        mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+        mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f,  10L));
+
+        verify(mRotaryInputGraphView).addValue(0.5f, 10L);
+        verify(mRotaryInputValueView).updateValue(0.5f);
+    }
+
+    @Test
+    public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() {
+        mFocusEventDebugView.handleUpdateShowRotaryInput(false);
+
+        mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+        verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong());
+        verify(mRotaryInputValueView, never()).updateValue(anyFloat());
+    }
+
+    private MotionEvent createRotaryMotionEvent(float scrollAxisValue, long eventTime) {
+        PointerCoords pointerCoords = new PointerCoords();
+        pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
+        PointerProperties pointerProperties = new PointerProperties();
+
+        return MotionEvent.obtain(
+                /* downTime */ 0,
+                /* eventTime */ eventTime,
+                /* action */ MotionEvent.ACTION_SCROLL,
+                /* pointerCount */ 1,
+                /* pointerProperties */ new PointerProperties[] {pointerProperties},
+                /* pointerCoords */ new PointerCoords[] {pointerCoords},
+                /* metaState */ 0,
+                /* buttonState */ 0,
+                /* xPrecision */ 0,
+                /* yPrecision */ 0,
+                /* deviceId */ 0,
+                /* edgeFlags */ 0,
+                /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
+                /* flags */ 0
+        );
+    }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java
new file mode 100644
index 0000000..af6ece4
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.input.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest RotaryInputGraphViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RotaryInputGraphViewTest {
+
+    private RotaryInputGraphView mRotaryInputGraphView;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+
+        mRotaryInputGraphView = new RotaryInputGraphView(context);
+    }
+
+    @Test
+    public void startsWithDefaultFrameCenter() {
+        assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
+    }
+
+    @Test
+    public void addValue_translatesRotaryInputGraphViewWithHighScrollValue() {
+        final float scrollAxisValue = 1000f;
+        final long eventTime = 0;
+
+        mRotaryInputGraphView.addValue(scrollAxisValue, eventTime);
+
+        assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
+    }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java
new file mode 100644
index 0000000..e5e3852
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.input.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.view.ViewConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Build/Install/Run:
+ * atest RotaryInputValueViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RotaryInputValueViewTest {
+
+    private final Locale mDefaultLocale = Locale.getDefault();
+
+    private RotaryInputValueView mRotaryInputValueView;
+    private float mScaledVerticalScrollFactor;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        mScaledVerticalScrollFactor =
+                ViewConfiguration.get(context).getScaledVerticalScrollFactor();
+
+        mRotaryInputValueView = new RotaryInputValueView(context);
+    }
+
+    @Test
+    public void startsWithDefaultValue() {
+        assertEquals("+0.0", mRotaryInputValueView.getText().toString());
+    }
+
+    @Test
+    public void updateValue_updatesTextWithScrollValue() {
+        final float scrollAxisValue = 1000f;
+        final String expectedText = String.format(mDefaultLocale, "+%.1f",
+                scrollAxisValue * mScaledVerticalScrollFactor);
+
+        mRotaryInputValueView.updateValue(scrollAxisValue);
+
+        assertEquals(expectedText, mRotaryInputValueView.getText().toString());
+    }
+
+    @Test
+    public void updateActivityStatus_setsAndRemovesColorFilter() {
+        // It should not be active initially.
+        assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+        mRotaryInputValueView.updateActivityStatus(true);
+        // It should be active after rotary input.
+        assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+        mRotaryInputValueView.updateActivityStatus(false);
+        // It should not be active after waiting for mUpdateActivityStatusCallback.
+        assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+    }
+}
diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp
index 84845c6..58ceb3f 100644
--- a/tests/InputMethodStressTest/Android.bp
+++ b/tests/InputMethodStressTest/Android.bp
@@ -26,7 +26,7 @@
         "compatibility-device-util-axt",
         "platform-test-annotations",
         "platform-test-rules",
-        "truth-prebuilt",
+        "truth",
     ],
     test_suites: [
         "general-tests",
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
index eee486f..15aaa46 100644
--- a/tests/InputScreenshotTest/Android.bp
+++ b/tests/InputScreenshotTest/Android.bp
@@ -29,7 +29,7 @@
         "androidx.lifecycle_lifecycle-livedata-ktx",
         "androidx.lifecycle_lifecycle-runtime-compose",
         "androidx.navigation_navigation-compose",
-        "truth-prebuilt",
+        "truth",
         "androidx.compose.runtime_runtime",
         "androidx.test.core",
         "androidx.test.ext.junit",
@@ -47,7 +47,7 @@
         "services.core.unboosted",
         "testables",
         "testng",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.mock",
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index ef45864..ddec8fa 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -19,7 +19,7 @@
         "junit",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
         "platform-test-annotations",
     ],
     java_resource_dirs: ["res"],
diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp
index 4e0b0a8..909ca59 100644
--- a/tests/LocalizationTest/Android.bp
+++ b/tests/LocalizationTest/Android.bp
@@ -34,7 +34,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "mockito-target-extended-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     jni_libs: [
         // For mockito extended
diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp
index 254770d..fcacab3 100644
--- a/tests/MidiTests/Android.bp
+++ b/tests/MidiTests/Android.bp
@@ -31,7 +31,7 @@
         "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
         "services.midi",
-        "truth-prebuilt",
+        "truth",
     ],
     jni_libs: ["libdexmakerjvmtiagent"],
     certificate: "platform",
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 1e1dc84..e0e6c4c 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -32,7 +32,7 @@
         "androidx.test.rules",
         "services.core",
         "services.net",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: ["android.test.runner"],
     jni_libs: [
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index f0f9c4b..fd992cf 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -38,7 +38,7 @@
         "androidx.test.ext.junit",
         "mockito-target-minus-junit4",
         "testng",
-        "truth-prebuilt",
+        "truth",
         "platform-compat-test-rules",
     ],
 }
diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp
index 5f91f9d0..f6a41c2 100644
--- a/tests/PlatformCompatGating/test-rules/Android.bp
+++ b/tests/PlatformCompatGating/test-rules/Android.bp
@@ -29,7 +29,7 @@
     static_libs: [
         "junit",
         "androidx.test.core",
-        "truth-prebuilt",
-        "core-compat-test-rules"
+        "truth",
+        "core-compat-test-rules",
     ],
 }
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index 38313f8..055d625 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -45,7 +45,7 @@
         "kotlinx-coroutines-android",
         "flickerlib",
         "flickerlib-trace_processor_shell",
-        "truth-prebuilt",
+        "truth",
         "cts-wm-util",
         "CtsSurfaceValidatorLib",
     ],
diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp
index bf12f42..d2ade34 100644
--- a/tests/TaskOrganizerTest/Android.bp
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -43,6 +43,6 @@
         "kotlinx-coroutines-android",
         "flickerlib",
         "flickerlib-trace_processor_shell",
-        "truth-prebuilt",
+        "truth",
     ],
 }
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
index 81ec265..b968e5d 100644
--- a/tests/TelephonyCommonTests/Android.bp
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -32,11 +32,11 @@
     static_libs: [
         "mockito-target-extended",
         "androidx.test.rules",
-        "truth-prebuilt",
+        "truth",
         "platform-test-annotations",
         "androidx.core_core",
         "androidx.fragment_fragment",
-        "androidx.test.ext.junit"
+        "androidx.test.ext.junit",
     ],
 
     jni_libs: ["libdexmakerjvmtiagent"],
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index c216bce..4e75a1d 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -28,7 +28,7 @@
         "flag-junit",
         "mockito-target-minus-junit4",
         "servicestests-utils",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: [
         "android.test.runner",
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index 9bfcc18..ddb3649 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -30,7 +30,7 @@
         "androidx.test.uiautomator_uiautomator",
         "compatibility-device-util-axt",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
     ],
     test_suites: [
         "general-tests",
diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp
index 97fbf5b..c02d8e9 100644
--- a/tests/UsbManagerTests/Android.bp
+++ b/tests/UsbManagerTests/Android.bp
@@ -31,7 +31,7 @@
         "androidx.test.rules",
         "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
-        "truth-prebuilt",
+        "truth",
         "UsbManagerTestLib",
     ],
     jni_libs: ["libdexmakerjvmtiagent"],
diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp
index 994484c..4e5a70fe 100644
--- a/tests/UsbManagerTests/lib/Android.bp
+++ b/tests/UsbManagerTests/lib/Android.bp
@@ -34,7 +34,7 @@
         "services.core",
         "services.net",
         "services.usb",
-        "truth-prebuilt",
+        "truth",
         "androidx.core_core",
     ],
     libs: [
diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp
index c60c519..92c2711 100644
--- a/tests/UsbTests/Android.bp
+++ b/tests/UsbTests/Android.bp
@@ -34,7 +34,7 @@
         "services.core",
         "services.net",
         "services.usb",
-        "truth-prebuilt",
+        "truth",
         "UsbManagerTestLib",
     ],
     jni_libs: [
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
index 01d34e4..39037f2 100644
--- a/tests/componentalias/Android.bp
+++ b/tests/componentalias/Android.bp
@@ -25,7 +25,7 @@
         "androidx.test.rules",
         "compatibility-device-util-axt",
         "mockito-target-extended-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
     libs: ["android.test.base"],
     srcs: [
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7323b0f..977b276 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -220,6 +220,7 @@
     name: "aapt2-protos",
     tools: [":soong_zip"],
     srcs: [
+        "ApkInfo.proto",
         "Configuration.proto",
         "ResourcesInternal.proto",
         "ResourceMetadata.proto",
diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
index e7fb2de..b71e5c4 100644
--- a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
+++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
@@ -24,7 +24,7 @@
     ],
     static_libs: [
         "junit",
-        "truth-prebuilt",
+        "truth",
         "mockito",
 
         // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
index 05d6a43..f9dc305 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -104,7 +104,7 @@
     ],
     static_libs: [
         "junit",
-        "truth-prebuilt",
+        "truth",
 
         // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
         "platform-test-annotations",
diff --git a/tools/processors/immutability/Android.bp b/tools/processors/immutability/Android.bp
index a7d69039..ecc283b 100644
--- a/tools/processors/immutability/Android.bp
+++ b/tools/processors/immutability/Android.bp
@@ -50,7 +50,7 @@
 
     static_libs: [
         "compile-testing-prebuilt",
-        "truth-prebuilt",
+        "truth",
         "junit",
         "kotlin-reflect",
         "ImmutabilityAnnotationProcessorHostLibrary",
diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp
index 7059c52..9c755b7 100644
--- a/tools/processors/intdef_mappings/Android.bp
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -38,7 +38,7 @@
 
     static_libs: [
         "compile-testing-prebuilt",
-        "truth-prebuilt",
+        "truth",
         "junit",
         "guava",
         "libintdef-annotation-processor",
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index 7a29969..5a0f742 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -40,7 +40,7 @@
         "frameworks-base-testutils",
         "guava",
         "mockito-target-minus-junit4",
-        "truth-prebuilt",
+        "truth",
     ],
 
     libs: [