Merge "Add offset when rescaling streams with different min indices" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 1b0ee63..12781e4 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -588,7 +588,6 @@
 java_aconfig_library {
     name: "android.view.inputmethod.flags-aconfig-java",
     aconfig_declarations: "android.view.inputmethod.flags-aconfig",
-    host_supported: true,
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e469f16..ce0da7e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,12 @@
       "name": "FrameworksUiServicesTests"
     },
     {
+      "name": "FrameworksUiServicesNotificationTests"
+    },
+    {
+      "name": "FrameworksUiServicesZenTests"
+    },
+    {
       "name": "FrameworksInputMethodSystemServerTests_server_inputmethod"
     },
     {
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 876274e..aae5bb3 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -126,3 +126,15 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "tune_quota_window_default_parameters"
+    namespace: "backstage_power"
+    description: "Tune default active/exempted bucket quota parameters"
+    bug: "401767691"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 54d337e..a9c4a15 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -360,13 +360,13 @@
 
     /** How much time each app will have to run jobs within their standby bucket window. */
     private final long[] mAllowedTimePerPeriodMs = new long[]{
-            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+            QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
             0, // NEVER
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
-            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+            QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
     };
 
     /**
@@ -3178,9 +3178,11 @@
         static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
                 QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
 
-        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+        // Legacy default time each app will have to run jobs within EXEMPTED bucket
+        private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
                 10 * 60 * 1000L; // 10 minutes
-        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+        // Legacy default time each app will have to run jobs within ACTIVE bucket
+        private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
                 10 * 60 * 1000L; // 10 minutes
@@ -3192,14 +3194,26 @@
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                 10 * 60 * 1000L; // 10 minutes
+
+        // Current default time each app will have to run jobs within EXEMPTED bucket
+        private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+                20 * 60 * 1000L; // 20 minutes
+        // Current default time each app will have to run jobs within ACTIVE bucket
+        private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                20 * 60 * 1000L; // 20 minutes
+        private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+                20 * 60 * 1000L; // 20 minutes
+
         private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
                 30 * 1000L; // 30 seconds
         // Legacy default window size for EXEMPTED bucket
+        // EXEMPT apps can run jobs at any time
         private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
         // Legacy default window size for ACTIVE bucket
+        // ACTIVE apps can run jobs at any time
         private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
         // Legacy default window size for WORKING bucket
         private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
                 2 * 60 * 60 * 1000L; // 2 hours
@@ -3216,6 +3230,13 @@
         private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
                 12 * 60 * 60 * 1000L; // 12 hours
 
+        // Latest default window size for EXEMPTED bucket.
+        private static final long DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS =
+                40 * 60 * 1000L; // 40 minutes.
+        // Latest default window size for ACTIVE bucket.
+        private static final long DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS =
+                60 * 60 * 1000L; // 60 minutes.
+
         private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
                 24 * 60 * 60 * 1000L; // 24 hours
         private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3276,12 +3297,13 @@
          * bucket window.
          */
         public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
         /**
          * How much time each app in the active bucket will have to run jobs within their standby
          * bucket window.
          */
-        public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
         /**
          * How much time each app in the working set bucket will have to run jobs within their
          * standby bucket window.
@@ -3575,11 +3597,30 @@
         public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
 
         void adjustDefaultBucketWindowSizes() {
-            WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
-            WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+            ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+            ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+            ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+                    DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+            WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+                    DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+            WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+                    DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
             WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
             WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
 
+            mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+            mAllowedTimePerPeriodMs[ACTIVE_INDEX] = Math.min(MAX_PERIOD_MS,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+
             mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
                     mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
@@ -3592,6 +3633,11 @@
             mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
                     mAllowedTimePerPeriodMs[FREQUENT_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+
+            mAllowedTimePeriodAdditionaInstallerMs =
+                    Math.min(mBucketPeriodsMs[EXEMPTED_INDEX]
+                                    - mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+                            ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
         }
 
         void adjustDefaultEjLimits() {
@@ -3882,10 +3928,14 @@
                     KEY_WINDOW_SIZE_RESTRICTED_MS);
             ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+                            Flags.tuneQuotaWindowDefaultParameters()
+                                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+                                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
             ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+                            Flags.tuneQuotaWindowDefaultParameters()
+                                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+                                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
             ALLOWED_TIME_PER_PERIOD_WORKING_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
                             DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
@@ -3900,19 +3950,27 @@
                             DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
             ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
+                            Flags.tuneQuotaWindowDefaultParameters()
+                                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS
+                                    : DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
             IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
                     DEFAULT_IN_QUOTA_BUFFER_MS);
             MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
                     DEFAULT_MAX_EXECUTION_TIME_MS);
             WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
-                    Flags.adjustQuotaDefaultConstants()
-                            ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
-                            DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
+                    (Flags.adjustQuotaDefaultConstants()
+                            && Flags.tuneQuotaWindowDefaultParameters())
+                            ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+                            (Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+                                    DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
             WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
-                    Flags.adjustQuotaDefaultConstants()
-                            ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
-                            DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
+                    (Flags.adjustQuotaDefaultConstants()
+                            && Flags.tuneQuotaWindowDefaultParameters())
+                            ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+                            (Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+                                    DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
             WINDOW_SIZE_WORKING_MS =
                     properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
                             Flags.adjustQuotaDefaultConstants()
diff --git a/core/api/current.txt b/core/api/current.txt
index 1630d80..4cd6d6f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -46681,16 +46681,16 @@
     method public int getLastCauseCode();
     method @Nullable public android.net.LinkProperties getLinkProperties();
     method public int getNetworkType();
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+    method public int getNetworkValidationStatus();
     method public int getState();
     method public int getTransportType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
+    field public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
+    field public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
+    field public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
+    field public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
   }
 
   public final class RadioAccessSpecifier implements android.os.Parcelable {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 132c65c..526a213 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -410,7 +410,6 @@
     method public void invalidateCache();
     method public static void invalidateCache(@NonNull String, @NonNull String);
     method @Nullable public Result query(@NonNull Query);
-    method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
     field public static final String MODULE_BLUETOOTH = "bluetooth";
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 32b170a..35720fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2941,6 +2941,7 @@
 package android.app.supervision {
 
   @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager {
+    method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent();
     method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled();
   }
 
@@ -3642,11 +3643,26 @@
     method public int getDeviceId();
     method @NonNull public String getName();
     method public int getType();
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public void sendAdditionalInfo(@NonNull android.companion.virtual.sensor.VirtualSensorAdditionalInfo);
     method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
   }
 
+  @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public final class VirtualSensorAdditionalInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getType();
+    method @NonNull public java.util.List<float[]> getValues();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorAdditionalInfo> CREATOR;
+  }
+
+  public static final class VirtualSensorAdditionalInfo.Builder {
+    ctor public VirtualSensorAdditionalInfo.Builder(int);
+    method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo.Builder addValues(@NonNull float[]);
+    method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo build();
+  }
+
   public interface VirtualSensorCallback {
     method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
   }
@@ -3664,6 +3680,7 @@
     method public float getResolution();
     method public int getType();
     method @Nullable public String getVendor();
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public boolean isAdditionalInfoSupported();
     method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
@@ -3672,6 +3689,7 @@
   public static final class VirtualSensorConfig.Builder {
     ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setAdditionalInfoSupported(boolean);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaxDelay(int);
@@ -16442,7 +16460,7 @@
     method @Deprecated public int getMtu();
     method public int getMtuV4();
     method public int getMtuV6();
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+    method public int getNetworkValidationStatus();
     method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
     method public int getPduSessionId();
     method public int getProtocolType();
@@ -16479,7 +16497,7 @@
     method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
+    method @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
@@ -16559,7 +16577,7 @@
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
     method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
     method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -16621,7 +16639,7 @@
     method public final int getSlotIndex();
     method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
     method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 12bfccf..4c82839 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2460,7 +2460,7 @@
     method public static void invalidateCache(@NonNull String, @NonNull String);
     method public final boolean isDisabled();
     method @Nullable public Result query(@NonNull Query);
-    method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
+    method public static void setTestMode(boolean);
     field public static final String MODULE_BLUETOOTH = "bluetooth";
     field public static final String MODULE_SYSTEM = "system_server";
     field public static final String MODULE_TEST = "test";
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1ed64f9..00fa1c1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -200,6 +200,8 @@
     @GuardedBy("mPackageMonitorCallbacks")
     private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>();
 
+    private final boolean mUseSystemFeaturesCache;
+
     UserManager getUserManager() {
         if (mUserManager == null) {
             mUserManager = UserManager.get(mContext);
@@ -824,8 +826,7 @@
         if (maybeHasSystemFeature != null) {
             return maybeHasSystemFeature;
         }
-        if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
-                && android.content.pm.Flags.cacheSdkSystemFeatures()) {
+        if (mUseSystemFeaturesCache) {
             maybeHasSystemFeature =
                     SystemFeaturesCache.getInstance().maybeHasFeature(name, version);
             if (maybeHasSystemFeature != null) {
@@ -2221,6 +2222,25 @@
     protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
         mContext = context;
         mPM = pm;
+        mUseSystemFeaturesCache = isSystemFeaturesCacheEnabledAndAvailable();
+    }
+
+    private static boolean isSystemFeaturesCacheEnabledAndAvailable() {
+        if (!android.content.pm.Flags.cacheSdkSystemFeatures()) {
+            return false;
+        }
+        if (!com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
+            return false;
+        }
+        if (ActivityThread.isSystem() && !SystemFeaturesCache.hasInstance()) {
+            // There are a handful of utility "system" processes that are neither system_server nor
+            // bound as applications. For these processes, we don't have access to application
+            // shared memory or the dependent system features cache.
+            // TODO(b/400713460): Revisit this exception after deprecating these command-like
+            // system processes.
+            return false;
+        }
+        return true;
     }
 
     /**
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 2daa52b..fa977c9 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -228,7 +228,7 @@
     public AutomaticZenRule(Parcel source) {
         enabled = source.readInt() == ENABLED;
         if (source.readInt() == ENABLED) {
-            name = getTrimmedString(source.readString8());
+            name = getTrimmedString(source.readString());
         }
         interruptionFilter = source.readInt();
         conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class));
@@ -238,11 +238,11 @@
                 source.readParcelable(null, android.content.ComponentName.class));
         creationTime = source.readLong();
         mZenPolicy = source.readParcelable(null, ZenPolicy.class);
-        mPkg = source.readString8();
+        mPkg = source.readString();
         mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
         mAllowManualInvocation = source.readBoolean();
         mIconResId = source.readInt();
-        mTriggerDescription = getTrimmedString(source.readString8(), MAX_DESC_LENGTH);
+        mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
         mType = source.readInt();
     }
 
@@ -514,7 +514,7 @@
         dest.writeInt(enabled ? ENABLED : DISABLED);
         if (name != null) {
             dest.writeInt(1);
-            dest.writeString8(name);
+            dest.writeString(name);
         } else {
             dest.writeInt(0);
         }
@@ -524,11 +524,11 @@
         dest.writeParcelable(configurationActivity, 0);
         dest.writeLong(creationTime);
         dest.writeParcelable(mZenPolicy, 0);
-        dest.writeString8(mPkg);
+        dest.writeString(mPkg);
         dest.writeParcelable(mDeviceEffects, 0);
         dest.writeBoolean(mAllowManualInvocation);
         dest.writeInt(mIconResId);
-        dest.writeString8(mTriggerDescription);
+        dest.writeString(mTriggerDescription);
         dest.writeInt(mType);
     }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7e5c0fb..99a2763 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2473,11 +2473,9 @@
     @Override
     public int getPermissionRequestState(String permission) {
         Objects.requireNonNull(permission, "Permission name can't be null");
-        int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
-                permission);
         PermissionManager permissionManager = getSystemService(PermissionManager.class);
         return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
-                deviceId);
+                getDeviceId());
     }
 
     @Override
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index feaa98f..7c293cb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6470,9 +6470,11 @@
             contentView.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
             contentView.setTextViewText(R.id.notification_material_reply_text_3, null);
 
-            // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
-            contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
-                    RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+            if (!notificationsRedesignTemplates()) {
+                // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
+                contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                        RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+            }
         }
 
         private boolean bindSnoozeAction(RemoteViews contentView, StandardTemplateParams p) {
@@ -6489,7 +6491,7 @@
             final boolean snoozeEnabled = !hideSnoozeButton
                     && mContext.getContentResolver() != null
                     && isSnoozeSettingEnabled();
-            if (snoozeEnabled) {
+            if (!notificationsRedesignTemplates() && snoozeEnabled) {
                 contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
                         RemoteViews.MARGIN_BOTTOM, 0);
             }
@@ -6569,44 +6571,18 @@
             }
 
             boolean validRemoteInput = false;
+            // With the new design, the actions_container should always be visible to act as padding
+            // when there are no actions. We're making its child GONE instead.
+            int actionsContainerForVisibilityChange = notificationsRedesignTemplates()
+                    ? R.id.actions_container_layout : R.id.actions_container;
             if (numActions > 0 && !p.mHideActions) {
-                contentView.setViewVisibility(R.id.actions_container, View.VISIBLE);
+                contentView.setViewVisibility(actionsContainerForVisibilityChange, View.VISIBLE);
                 contentView.setViewVisibility(R.id.actions, View.VISIBLE);
-                contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
-                        RemoteViews.MARGIN_BOTTOM, 0);
-                if (notificationsRedesignTemplates()) {
-                    // No need for additional space under smart replies/smart actions.
-                    contentView.setViewLayoutMarginDimen(R.id.smart_reply_container,
-                            RemoteViews.MARGIN_BOTTOM, 0);
-                    if (emphasizedMode) {
-                        // Emphasized actions look similar to smart replies, so let's use the same
-                        // margins.
-                        contentView.setViewLayoutMarginDimen(R.id.actions_container,
-                                RemoteViews.MARGIN_TOP,
-                                R.dimen.notification_2025_smart_reply_container_margin);
-                        contentView.setViewLayoutMarginDimen(R.id.actions_container,
-                                RemoteViews.MARGIN_BOTTOM,
-                                R.dimen.notification_2025_smart_reply_container_margin);
-                    } else {
-                        contentView.setViewLayoutMarginDimen(R.id.actions_container,
-                                RemoteViews.MARGIN_TOP, 0);
-                        contentView.setViewLayoutMarginDimen(R.id.actions_container,
-                                RemoteViews.MARGIN_BOTTOM,
-                                R.dimen.notification_2025_action_list_margin_bottom);
-                    }
-                }
+                updateMarginsForActions(contentView, emphasizedMode);
                 validRemoteInput = populateActionsContainer(contentView, p, nonContextualActions,
                         numActions, emphasizedMode);
             } else {
-                contentView.setViewVisibility(R.id.actions_container, View.GONE);
-                if (notificationsRedesignTemplates() && !snoozeEnabled) {
-                    // Make sure smart replies & smart actions have enough space at the bottom
-                    // (if present) when there are no actions. This should be set to 0 if we're
-                    // showing the snooze or bubble buttons.
-                    contentView.setViewLayoutMarginDimen(R.id.smart_reply_container,
-                            RemoteViews.MARGIN_BOTTOM,
-                            R.dimen.notification_2025_smart_reply_container_margin);
-                }
+                contentView.setViewVisibility(actionsContainerForVisibilityChange, View.GONE);
             }
 
             RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
@@ -6652,6 +6628,30 @@
             return contentView;
         }
 
+        private void updateMarginsForActions(RemoteViews contentView, boolean emphasizedMode) {
+            if (notificationsRedesignTemplates()) {
+                if (emphasizedMode) {
+                    // Emphasized actions look similar to smart replies, so let's use the same
+                    // margins.
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_TOP,
+                            R.dimen.notification_2025_smart_reply_container_margin);
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_BOTTOM,
+                            R.dimen.notification_2025_smart_reply_container_margin);
+                } else {
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_TOP, 0);
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_BOTTOM,
+                            R.dimen.notification_2025_action_list_margin_bottom);
+                }
+            } else {
+                contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                        RemoteViews.MARGIN_BOTTOM, 0);
+            }
+        }
+
         private boolean populateActionsContainer(RemoteViews contentView, StandardTemplateParams p,
                 List<Action> nonContextualActions, int numActions, boolean emphasizedMode) {
             boolean validRemoteInput = false;
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6e49576..38141cf 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1417,36 +1417,7 @@
     }
 
     /**
-     * Throw if the current process is not allowed to use test APIs.
-     */
-    @android.ravenwood.annotation.RavenwoodReplace
-    private static void throwIfNotTest() {
-        final ActivityThread activityThread = ActivityThread.currentActivityThread();
-        if (activityThread == null) {
-            // Only tests can reach here.
-            return;
-        }
-        final Instrumentation instrumentation = activityThread.getInstrumentation();
-        if (instrumentation == null) {
-            // Only tests can reach here.
-            return;
-        }
-        if (instrumentation.isInstrumenting()) {
-            return;
-        }
-        if (Flags.enforcePicTestmodeProtocol()) {
-            throw new IllegalStateException("Test-only API called not from a test.");
-        }
-    }
-
-    /**
-     * Do not throw if running under ravenwood.
-     */
-    private static void throwIfNotTest$ravenwood() {
-    }
-
-    /**
-     * Enable or disable test mode.  The protocol requires that the mode toggle: for instance, it is
+     * Enable or disable testing.  The protocol requires that the mode toggle: for instance, it is
      * illegal to clear the test mode if the test mode is already off.  Enabling test mode puts
      * all caches in the process into test mode; all nonces are initialized to UNSET and
      * subsequent reads and writes are to process memory.  This has the effect of disabling all
@@ -1454,12 +1425,10 @@
      * operation.
      * @param mode The desired test mode.
      * @throws IllegalStateException if the supplied mode is already set.
-     * @throws IllegalStateException if the process is not running an instrumentation test.
      * @hide
      */
     @VisibleForTesting
     public static void setTestMode(boolean mode) {
-        throwIfNotTest();
         synchronized (sGlobalLock) {
             if (sTestMode == mode) {
                 final String msg = "cannot set test mode redundantly: mode=" + mode;
@@ -1495,11 +1464,9 @@
      * for which it would not otherwise have permission.  Caches in test mode do NOT write their
      * values to the system properties.  The effect is local to the current process.  Test mode
      * must be true when this method is called.
-     * @throws IllegalStateException if the process is not running an instrumentation test.
      * @hide
      */
     public void testPropertyName() {
-        throwIfNotTest();
         synchronized (sGlobalLock) {
             if (sTestMode == false) {
                 throw new IllegalStateException("cannot test property name with test mode off");
@@ -1810,12 +1777,10 @@
      * When multiple caches share a single property value, using an instance method on one of
      * the cache objects to invalidate all of the cache objects becomes confusing and you should
      * just use the static version of this function.
-     * @throws IllegalStateException if the process is not running an instrumentation test.
      * @hide
      */
     @VisibleForTesting
     public void disableSystemWide() {
-        throwIfNotTest();
         disableSystemWide(mPropertyName);
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c74bd1a..c528db8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -224,7 +224,7 @@
  * <li>A <i id="deviceowner">Device Owner</i>, which only ever exists on the
  * {@link UserManager#isSystemUser System User} or Main User, is
  * the most powerful type of Device Policy Controller and can affect policy across the device.
- * <li>A <i id="profileowner">Profile Owner<i>, which can exist on any user, can
+ * <li>A <i id="profileowner">Profile Owner</i>, which can exist on any user, can
  * affect policy on the user it is on, and when it is running on
  * {@link UserManager#isProfile a profile} has
  * <a href="#profile-on-parent">limited</a> ability to affect policy on its parent.
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7718d15..3c8b1a0 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -366,7 +366,7 @@
                 30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
                 Integer.MAX_VALUE
         };
-        private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length];
+        private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length - 1];
 
         // Histogram of frame duration overruns encoded in predetermined buckets.
         public PendingJankStat() {
@@ -511,7 +511,7 @@
             if (overrunTime <= 1000) {
                 return (overrunTime - 200) / 100 + 43;
             }
-            return sFrameOverrunHistogramBounds.length - 1;
+            return mFrameOverrunBuckets.length - 1;
         }
 
     }
diff --git a/core/java/android/app/jank/TEST_MAPPING b/core/java/android/app/jank/TEST_MAPPING
new file mode 100644
index 0000000..271eb4332
--- /dev/null
+++ b/core/java/android/app/jank/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "postsubmit": [
+    {
+      "name": "CoreAppJankTestCases"
+    },
+    {
+      "name": "CtsAppJankTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 5891bdd..6f0eafe 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -269,7 +269,7 @@
   namespace: "systemui"
   description: "enables metrics when redacting notifications on the lockscreen"
   bug: "343631648"
-    metadata {
+  metadata {
     purpose: PURPOSE_BUGFIX
   }
 }
@@ -279,7 +279,16 @@
   namespace: "systemui"
   description: "enables user expanding the public view of a notification"
   bug: "398853084"
-    metadata {
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+ }
+flag {
+  name: "notif_entry_creation_time_use_elapsed_realtime"
+  namespace: "systemui"
+  description: "makes the notification entry expect its creation time to be elapsedRealtime, not uptimeMillis"
+  bug: "343631648"
+  metadata {
     purpose: PURPOSE_BUGFIX
   }
 }
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 8217ca1..172ed23 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -97,6 +97,8 @@
      *
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_SUPERVISION_MANAGER_APIS)
     @RequiresPermission(anyOf = {MANAGE_USERS, QUERY_USERS})
     @Nullable
     public Intent createConfirmSupervisionCredentialsIntent() {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index f8ac27d..db77ade 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -24,6 +24,7 @@
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.companion.virtual.camera.VirtualCameraConfig;
@@ -251,6 +252,11 @@
     boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);
 
     /**
+     * Sends additional information about the virtual sensor corresponding to the given token.
+     */
+    boolean sendSensorAdditionalInfo(IBinder token, in VirtualSensorAdditionalInfo info);
+
+    /**
      * Launches a pending intent on the given display that is owned by this virtual device.
      */
     void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 4fb3982..615a6df 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -158,3 +158,11 @@
     bug: "370720522"
     is_exported: true
 }
+
+flag {
+    name: "virtual_sensor_additional_info"
+    namespace: "virtual_devices"
+    description: "API for injecting SensorAdditionalInfo for VirtualSensor"
+    bug: "393517834"
+    is_exported: true
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 934a1a8..8d4acfc 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -16,12 +16,15 @@
 
 package android.companion.virtual.sensor;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
 import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,20 +40,33 @@
  */
 @SystemApi
 public final class VirtualSensor implements Parcelable {
+
     private final int mHandle;
     private final int mType;
     private final String mName;
+    private final int mFlags;
     private final IVirtualDevice mVirtualDevice;
     private final IBinder mToken;
+    // Only one additional info frame set at a time.
+    private final Object mAdditionalInfoLock = new Object();
 
     /**
      * @hide
      */
     public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice,
             IBinder token) {
+        this(handle, type, name, /*flags=*/0, virtualDevice, token);
+    }
+
+    /**
+     * @hide
+     */
+    public VirtualSensor(int handle, int type, String name, int flags, IVirtualDevice virtualDevice,
+            IBinder token) {
         mHandle = handle;
         mType = type;
         mName = name;
+        mFlags = flags;
         mVirtualDevice = virtualDevice;
         mToken = token;
     }
@@ -61,13 +77,14 @@
     @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
     @TestApi
     public VirtualSensor(int handle, int type, @NonNull String name) {
-        this(handle, type, name, /*virtualDevice=*/null, /*token=*/null);
+        this(handle, type, name, /*flags=*/0, /*virtualDevice=*/null, /*token=*/null);
     }
 
     private VirtualSensor(Parcel parcel) {
         mHandle = parcel.readInt();
         mType = parcel.readInt();
         mName = parcel.readString8();
+        mFlags = parcel.readInt();
         mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder());
         mToken = parcel.readStrongBinder();
     }
@@ -123,6 +140,7 @@
         parcel.writeInt(mHandle);
         parcel.writeInt(mType);
         parcel.writeString8(mName);
+        parcel.writeInt(mFlags);
         parcel.writeStrongBinder(mVirtualDevice.asBinder());
         parcel.writeStrongBinder(mToken);
     }
@@ -143,6 +161,33 @@
         }
     }
 
+    /**
+     * Send additional information about the sensor to the system.
+     *
+     * @param info the additional sensor information to send.
+     * @throws UnsupportedOperationException if the sensor does not support sending additional info.
+     * @see Sensor#isAdditionalInfoSupported()
+     * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+     * @see SensorAdditionalInfo
+     * @see VirtualSensorAdditionalInfo
+     */
+    @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+    public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) {
+        if (!Flags.virtualSensorAdditionalInfo()) {
+            return;
+        }
+        if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) {
+            throw new UnsupportedOperationException("Sensor additional info not supported.");
+        }
+        try {
+            synchronized (mAdditionalInfoLock) {
+                mVirtualDevice.sendSensorAdditionalInfo(mToken, info);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     @NonNull
     public static final Parcelable.Creator<VirtualSensor> CREATOR =
             new Parcelable.Creator<VirtualSensor>() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
copy to core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
index 3cec5a9..7267be8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package android.companion.virtual.sensor;
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-
-val Kosmos.keyguardServiceShowLockscreenInteractor by
-    Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+parcelable VirtualSensorAdditionalInfo;
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
new file mode 100644
index 0000000..a4fca50
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.companion.virtual.sensor;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.hardware.SensorAdditionalInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An additional information frame for a {@link VirtualSensor}, which is reported through listener
+ * callback {@link android.hardware.SensorEventCallback#onSensorAdditionalInfo}.
+ *
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+@SystemApi
+public final class VirtualSensorAdditionalInfo implements Parcelable {
+
+    private final int mType;
+    @NonNull
+    private final List<float[]> mValues;
+
+    /** @hide */
+    @IntDef(prefix = "TYPE_", value = {
+            SensorAdditionalInfo.TYPE_UNTRACKED_DELAY,
+            SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE,
+            SensorAdditionalInfo.TYPE_VEC3_CALIBRATION,
+            SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT,
+            SensorAdditionalInfo.TYPE_SAMPLING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    private VirtualSensorAdditionalInfo(int type, @NonNull List<float[]> values) {
+        mType = type;
+        mValues = values;
+    }
+
+    private VirtualSensorAdditionalInfo(@NonNull Parcel parcel) {
+        mType = parcel.readInt();
+        final int valuesLength = parcel.readInt();
+        mValues = new ArrayList<>(valuesLength);
+        for (int i = 0; i < valuesLength; ++i) {
+            mValues.add(parcel.createFloatArray());
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeInt(mType);
+        parcel.writeInt(mValues.size());
+        for (int i = 0; i < mValues.size(); ++i) {
+            parcel.writeFloatArray(mValues.get(i));
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the type of this information frame.
+     *
+     * @see SensorAdditionalInfo#type
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the float values of this information frame, if any.
+     *
+     * @see SensorAdditionalInfo#floatValues
+     */
+    @NonNull
+    public List<float[]> getValues() {
+        return mValues;
+    }
+
+    /**
+     * Builder for {@link VirtualSensorAdditionalInfo}.
+     */
+    public static final class Builder {
+
+        @VirtualSensorAdditionalInfo.Type
+        private final int mType;
+        @NonNull
+        private final ArrayList<float[]> mValues = new ArrayList<>();
+
+        /**
+         * Creates a new builder.
+         *
+         * @param type type of this additional info frame.
+         * @see SensorAdditionalInfo
+         */
+        public Builder(@VirtualSensorAdditionalInfo.Type int type) {
+            switch (type) {
+                case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+                case SensorAdditionalInfo.TYPE_SAMPLING:
+                case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+                case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+                case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported type " + type);
+            }
+            mType = type;
+        }
+
+        /**
+         * Additional info payload data represented in float values. Depending on the type of
+         * information, this may be null.
+         *
+         * @see SensorAdditionalInfo#floatValues
+         */
+        @NonNull
+        public Builder addValues(@NonNull float[] values) {
+            if (values.length > 14) {
+                throw new IllegalArgumentException("Maximum payload value size is 14.");
+            }
+            if (mValues.isEmpty()) {
+                switch (mType) {
+                    case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+                    case SensorAdditionalInfo.TYPE_SAMPLING:
+                        assertValuesLength(values, 2);
+                        break;
+                    case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+                        assertValuesLength(values, 1);
+                        break;
+                    case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+                    case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+                        assertValuesLength(values, 11);
+                        break;
+                }
+            } else if (values.length != mValues.getFirst().length) {
+                throw new IllegalArgumentException("All payload values must have the same length");
+            }
+
+            mValues.add(values);
+            return this;
+        }
+
+        private void assertValuesLength(float[] values, int expected) {
+            if (values.length != expected) {
+                throw new IllegalArgumentException(
+                        "Payload values must have size " + expected + " for type " + mType);
+            }
+        }
+
+        /**
+         * Creates a new {@link VirtualSensorAdditionalInfo}.
+         *
+         * @throws IllegalArgumentException if the payload doesn't match the expectation for the
+         *   given type, as documented in {@link SensorAdditionalInfo}.
+         */
+        @NonNull
+        public VirtualSensorAdditionalInfo build() {
+            if (mValues.isEmpty()) {
+                throw new IllegalArgumentException("Payload is required");
+            }
+            return new VirtualSensorAdditionalInfo(mType, mValues);
+        }
+    }
+
+    public static final @NonNull Creator<VirtualSensorAdditionalInfo> CREATOR =
+            new Creator<>() {
+                public VirtualSensorAdditionalInfo createFromParcel(Parcel source) {
+                    return new VirtualSensorAdditionalInfo(source);
+                }
+
+                public VirtualSensorAdditionalInfo[] newArray(int size) {
+                    return new VirtualSensorAdditionalInfo[size];
+                }
+            };
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 68bc9bc..be8974e 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -59,10 +59,14 @@
     private static final int REPORTING_MODE_MASK = 0xE;
     private static final int REPORTING_MODE_SHIFT = 1;
 
+    // Mask for indication bit of sensor additional information support, bit 6.
+    static final int ADDITIONAL_INFO_MASK = 0x40;
+
     // Mask for direct mode highest rate level, bit 7, 8, 9.
     private static final int DIRECT_REPORT_MASK = 0x380;
     private static final int DIRECT_REPORT_SHIFT = 7;
 
+
     // Mask for supported direct channel, bit 10, 11
     private static final int DIRECT_CHANNEL_SHIFT = 10;
 
@@ -253,6 +257,18 @@
     }
 
     /**
+     * Returns whether the sensor supports additional information.
+     *
+     * @see Builder#setAdditionalInfoSupported(boolean)
+     * @see Sensor#isAdditionalInfoSupported()
+     * @see android.hardware.SensorAdditionalInfo
+     */
+    @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+    public boolean isAdditionalInfoSupported() {
+        return (mFlags & ADDITIONAL_INFO_MASK) > 0;
+    }
+
+    /**
      * Returns the reporting mode of this sensor.
      *
      * @see Builder#setReportingMode(int)
@@ -450,6 +466,25 @@
         }
 
         /**
+         * Sets whether this sensor supports sensor additional information.
+         *
+         * @see Sensor#isAdditionalInfoSupported()
+         * @see android.hardware.SensorAdditionalInfo
+         * @see VirtualSensorAdditionalInfo
+         */
+        @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+        @NonNull
+        public VirtualSensorConfig.Builder setAdditionalInfoSupported(
+                boolean additionalInfoSupported) {
+            if (additionalInfoSupported) {
+                mFlags |= ADDITIONAL_INFO_MASK;
+            } else {
+                mFlags &= ~ADDITIONAL_INFO_MASK;
+            }
+            return this;
+        }
+
+        /**
          * Sets the reporting mode of this sensor.
          *
          * @throws IllegalArgumentException if the reporting mode is not one of
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 10d3051..ded35b2 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -31,13 +31,13 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseArrayMap;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -98,15 +98,16 @@
     @GuardedBy("mServicesLock")
     private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
 
-    @GuardedBy("mServiceInfoCaches")
-    private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+    @GuardedBy("mUserIdToServiceInfoCaches")
+    private final SparseArrayMap<ComponentName, ServiceInfo<V>> mUserIdToServiceInfoCaches =
+            new SparseArrayMap<>();
 
     private final Handler mBackgroundHandler;
 
     private final Runnable mClearServiceInfoCachesRunnable = new Runnable() {
         public void run() {
-            synchronized (mServiceInfoCaches) {
-                mServiceInfoCaches.clear();
+            synchronized (mUserIdToServiceInfoCaches) {
+                mUserIdToServiceInfoCaches.clear();
             }
         }
     };
@@ -537,8 +538,8 @@
                     Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e);
                 }
                 if (lastUpdateTime >= 0) {
-                    ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
-                            lastUpdateTime);
+                    ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(userId,
+                            componentName, lastUpdateTime);
                     if (serviceInfo != null) {
                         serviceInfos.add(serviceInfo);
                         continue;
@@ -553,8 +554,8 @@
                 }
                 serviceInfos.add(info);
                 if (Flags.optimizeParsingInRegisteredServicesCache()) {
-                    synchronized (mServiceInfoCaches) {
-                        mServiceInfoCaches.put(componentName, info);
+                    synchronized (mUserIdToServiceInfoCaches) {
+                        mUserIdToServiceInfoCaches.add(userId, componentName, info);
                     }
                 }
             } catch (XmlPullParserException | IOException e) {
@@ -563,8 +564,8 @@
         }
 
         if (Flags.optimizeParsingInRegisteredServicesCache()) {
-            synchronized (mServiceInfoCaches) {
-                if (!mServiceInfoCaches.isEmpty()) {
+            synchronized (mUserIdToServiceInfoCaches) {
+                if (mUserIdToServiceInfoCaches.numMaps() > 0) {
                     mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable);
                     mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable,
                             SERVICE_INFO_CACHES_TIMEOUT_MILLIS);
@@ -873,6 +874,11 @@
         synchronized (mServicesLock) {
             mUserServices.remove(userId);
         }
+        if (Flags.optimizeParsingInRegisteredServicesCache()) {
+            synchronized (mUserIdToServiceInfoCaches) {
+                mUserIdToServiceInfoCaches.delete(userId);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -916,10 +922,10 @@
         mContext.unregisterReceiver(mUserRemovedReceiver);
     }
 
-    private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName,
-            long lastUpdateTime) {
-        synchronized (mServiceInfoCaches) {
-            ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName);
+    private ServiceInfo<V> getServiceInfoFromServiceCache(int userId,
+            @NonNull ComponentName componentName, long lastUpdateTime) {
+        synchronized (mUserIdToServiceInfoCaches) {
+            ServiceInfo<V> serviceCache = mUserIdToServiceInfoCaches.get(userId, componentName);
             if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) {
                 return serviceCache;
             }
diff --git a/core/java/android/content/pm/SystemFeaturesCache.java b/core/java/android/content/pm/SystemFeaturesCache.java
index b3d70fa..57dd1e6 100644
--- a/core/java/android/content/pm/SystemFeaturesCache.java
+++ b/core/java/android/content/pm/SystemFeaturesCache.java
@@ -74,6 +74,11 @@
         return instance;
     }
 
+    /** Checks for existence of the process-global instance. */
+    public static boolean hasInstance() {
+        return sInstance != null;
+    }
+
     /** Clears the process-global cache instance for testing. */
     @VisibleForTesting
     public static void clearInstance() {
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 23f1ff8..92ae15a 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -136,6 +136,28 @@
             ]
         },
         {
+          "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+          "options":[
+              {
+                  "exclude-annotation":"androidx.test.filters.FlakyTest"
+              },
+              {
+                  "exclude-annotation":"org.junit.Ignore"
+              }
+          ]
+        },
+        {
+          "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+          "options":[
+              {
+                  "exclude-annotation":"androidx.test.filters.FlakyTest"
+              },
+              {
+                  "exclude-annotation":"org.junit.Ignore"
+              }
+          ]
+        },
+        {
             "name": "CtsPackageInstallerCUJMultiUsersTestCases",
             "options":[
                {
@@ -261,6 +283,28 @@
             ]
         },
         {
+            "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
             "name": "CtsPackageInstallerCUJMultiUsersTestCases",
             "options":[
                {
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 7f57f5dbf0..3411a48 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -636,3 +636,13 @@
     description: "Enables support for new supervising user type"
     bug: "389712089"
 }
+
+flag {
+    name: "use_unified_resources"
+    namespace: "multiuser"
+    description: "Use same resources"
+    bug: "392972139"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
index fdde205..de3bafb 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -75,7 +75,6 @@
             }
 
             Parcel parcel = Parcel.obtain();
-            byte[] parcelContents;
 
             try {
                 value.writeToParcel(parcel, /*flags*/0);
@@ -85,17 +84,15 @@
                             "Parcelable " + value + " must not have file descriptors");
                 }
 
-                parcelContents = parcel.marshall();
+                final int position = buffer.position();
+                parcel.marshall(buffer);
+                if (buffer.position() == position) {
+                    throw new AssertionError("No data marshaled for " + value);
+                }
             }
             finally {
                 parcel.recycle();
             }
-
-            if (parcelContents.length == 0) {
-                throw new AssertionError("No data marshaled for " + value);
-            }
-
-            buffer.put(parcelContents);
         }
 
         @Override
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 92a56fc..8216130 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -433,8 +433,9 @@
     public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
 
     /**
-     * Virtual display flag: Indicates that the display should support system decorations. Virtual
-     * displays without this flag shouldn't show home, navigation bar or wallpaper.
+     * Virtual display flag: Indicates that the display supports and should always show system
+     * decorations. Virtual displays without this flag shouldn't show home, navigation bar or
+     * wallpaper.
      * <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
      *
      * @see #createVirtualDisplay
diff --git a/core/java/android/hardware/serial/OWNERS b/core/java/android/hardware/serial/OWNERS
index bc2c66a..13f6774 100644
--- a/core/java/android/hardware/serial/OWNERS
+++ b/core/java/android/hardware/serial/OWNERS
@@ -3,4 +3,5 @@
 chominskib@google.com
 wzwonarz@google.com
 gstepniewski@google.com
-xutan@google.com
\ No newline at end of file
+xutan@google.com
+ovn@google.com
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index e888f52..2e7c3be 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -718,7 +718,7 @@
     }
 
     /**
-     * Enable or disable test mode.  The protocol requires that the mode toggle: for instance, it is
+     * Enable or disable testing.  The protocol requires that the mode toggle: for instance, it is
      * illegal to clear the test mode if the test mode is already off.  Enabling test mode puts
      * all caches in the process into test mode; all nonces are initialized to UNSET and
      * subsequent reads and writes are to process memory.  This has the effect of disabling all
@@ -726,11 +726,8 @@
      * operation.
      * @param mode The desired test mode.
      * @throws IllegalStateException if the supplied mode is already set.
-     * @throws IllegalStateException if the process is not running an instrumentation test.
      * @hide
      */
-    @FlaggedApi(android.os.Flags.FLAG_IPC_DATA_CACHE_TEST_APIS)
-    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
     @TestApi
     public static void setTestMode(boolean mode) {
         PropertyInvalidatedCache.setTestMode(mode);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 49d3f06..6cb49b3 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -52,6 +52,8 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import java.nio.BufferOverflowException;
+import java.nio.ReadOnlyBufferException;
 import libcore.util.SneakyThrow;
 
 import java.io.ByteArrayInputStream;
@@ -62,6 +64,7 @@
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
 import java.io.Serializable;
+import java.nio.ByteBuffer;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Array;
@@ -457,8 +460,15 @@
     private static native void nativeDestroy(long nativePtr);
 
     private static native byte[] nativeMarshall(long nativePtr);
+    private static native int nativeMarshallArray(
+            long nativePtr, byte[] data, int offset, int length);
+    private static native int nativeMarshallBuffer(
+            long nativePtr, ByteBuffer buffer, int offset, int length);
     private static native void nativeUnmarshall(
             long nativePtr, byte[] data, int offset, int length);
+    private static native void nativeUnmarshallBuffer(
+            long nativePtr, ByteBuffer buffer, int offset, int length);
+
     private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
     private static native boolean nativeCompareDataInRange(
             long ptrA, int offsetA, long ptrB, int offsetB, int length);
@@ -814,12 +824,80 @@
     }
 
     /**
+     * Writes the raw bytes of the parcel to a buffer.
+     *
+     * <p class="note">The data you retrieve here <strong>must not</strong>
+     * be placed in any kind of persistent storage (on local disk, across
+     * a network, etc).  For that, you should use standard serialization
+     * or another kind of general serialization mechanism.  The Parcel
+     * marshalled representation is highly optimized for local IPC, and as
+     * such does not attempt to maintain compatibility with data created
+     * in different versions of the platform.
+     *
+     * @param buffer The ByteBuffer to write the data to.
+     * @throws ReadOnlyBufferException if the buffer is read-only.
+     * @throws BufferOverflowException if the buffer is too small.
+     *
+     * @hide
+     */
+    public final void marshall(@NonNull ByteBuffer buffer) {
+        if (buffer == null) {
+            throw new NullPointerException();
+        }
+        if (buffer.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+
+        final int position = buffer.position();
+        final int remaining = buffer.remaining();
+
+        int marshalledSize = 0;
+        if (buffer.isDirect()) {
+            marshalledSize = nativeMarshallBuffer(mNativePtr, buffer, position, remaining);
+        } else if (buffer.hasArray()) {
+            marshalledSize = nativeMarshallArray(
+                    mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        buffer.position(position + marshalledSize);
+    }
+
+    /**
      * Fills the raw bytes of this Parcel with the supplied data.
      */
     public final void unmarshall(@NonNull byte[] data, int offset, int length) {
         nativeUnmarshall(mNativePtr, data, offset, length);
     }
 
+    /**
+     * Fills the raw bytes of this Parcel with data from the supplied buffer.
+     *
+     * @param buffer will read buffer.remaining() bytes from the buffer.
+     *
+     * @hide
+     */
+    public final void unmarshall(@NonNull ByteBuffer buffer) {
+        if (buffer == null) {
+            throw new NullPointerException();
+        }
+
+        final int position = buffer.position();
+        final int remaining = buffer.remaining();
+
+        if (buffer.isDirect()) {
+            nativeUnmarshallBuffer(mNativePtr, buffer, position, remaining);
+        } else if (buffer.hasArray()) {
+            nativeUnmarshall(
+                    mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        buffer.position(position + remaining);
+    }
+
     public final void appendFrom(Parcel parcel, int offset, int length) {
         nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
     }
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 5d80119..86acb2b 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -227,14 +227,6 @@
 }
 
 flag {
-     name: "ipc_data_cache_test_apis"
-     namespace: "system_performance"
-     description: "Expose IpcDataCache test apis to mainline modules."
-     bug: "396173886"
-     is_exported: true
-}
-
-flag {
      name: "mainline_vcn_platform_api"
      namespace: "vcn"
      description: "Expose platform APIs to mainline VCN"
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 561a2c9..0433c76 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1907,8 +1907,9 @@
     @Context.PermissionRequestState
     public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
             int deviceId) {
+        int actualDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permission);
         return sPermissionRequestStateCache.query(
-                new PermissionRequestStateQuery(packageName, permission, deviceId));
+                new PermissionRequestStateQuery(packageName, permission, actualDeviceId));
     }
 
     /**
@@ -2035,7 +2036,8 @@
      */
     public int checkPackageNamePermission(String permName, String pkgName,
             int deviceId, @UserIdInt int userId) {
-        String persistentDeviceId = getPersistentDeviceId(deviceId);
+        int actualDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permName);
+        String persistentDeviceId = getPersistentDeviceId(actualDeviceId);
         return sPackageNamePermissionCache.query(
                 new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b708b3..85f38c9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6484,6 +6484,14 @@
         public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
 
         /**
+         * Setting to enable CV (proprietary)
+         *
+         * @hide
+         */
+        public static final String CV_ENABLED =
+                "cv_enabled";
+
+        /**
          * Integer property that specifes the color for screen flash notification as a
          * packed 32-bit color.
          *
@@ -15514,7 +15522,8 @@
          * <ul>
          * <li><pre>secure</pre>: creates a secure display</li>
          * <li><pre>own_content_only</pre>: only shows this display's own content</li>
-         * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+         * <li><pre>should_show_system_decorations</pre>: always shows system decorations</li>
+         * <li><pre>fixed_content_mode</pre>: does not allow the content mode switch</li>
          * </ul>
          * </p><p>
          * Example:
@@ -20815,6 +20824,24 @@
             @Readable
             public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode";
 
+            /**
+             * Setting indicating whether the primary gesture input action has been enabled by the
+             * user.
+             *
+             * @hide
+             */
+            public static final String GESTURE_PRIMARY_ACTION_USER_PREFERENCE =
+                    "gesture_primary_action_user_preference";
+
+            /**
+             * Setting indicating whether the dismiss gesture input action has been enabled by the
+             * user.
+             *
+             * @hide
+             */
+            public static final String GESTURE_DISMISS_ACTION_USER_PREFERENCE =
+                    "gesture_dismiss_action_user_preference";
+
             /** Whether Wear Power Anomaly Service is enabled.
              *
              * (0 = false, 1 = true)
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 0b2239a..62b2bcf 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -124,6 +124,18 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface FeatureId {}
 
+    /** @hide */
+    public static String featureIdToString(@FeatureId int featureId) {
+        return switch(featureId) {
+            case FEATURE_ID_DISALLOW_CELLULAR_2G -> "DISALLOW_CELLULAR_2G";
+            case FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES -> "DISALLOW_INSTALL_UNKNOWN_SOURCES";
+            case FEATURE_ID_DISALLOW_USB -> "DISALLOW_USB";
+            case FEATURE_ID_DISALLOW_WEP -> "DISALLOW_WEP";
+            case FEATURE_ID_ENABLE_MTE -> "ENABLE_MTE";
+            default -> "UNKNOWN";
+        };
+    }
+
     private static final Set<Integer> ALL_FEATURE_IDS = Set.of(
             FEATURE_ID_DISALLOW_CELLULAR_2G,
             FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
@@ -147,7 +159,7 @@
             "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
 
     /**
-     * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+     * An int extra used with {@link #createSupportIntent} to identify the feature that needs to
      * show a support dialog explaining it was disabled by advanced protection.
      *
      * @hide */
@@ -156,7 +168,7 @@
             "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
 
     /**
-     * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+     * An int extra used with {@link #createSupportIntent} to identify the type of the action that
      * needs to be explained in the support dialog.
      *
      * @hide */
@@ -194,6 +206,16 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SupportDialogType {}
 
+    /** @hide */
+    public static String supportDialogTypeToString(@SupportDialogType int type) {
+        return switch(type) {
+            case SUPPORT_DIALOG_TYPE_UNKNOWN -> "UNKNOWN";
+            case SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION -> "BLOCKED_INTERACTION";
+            case SUPPORT_DIALOG_TYPE_DISABLED_SETTING -> "DISABLED_SETTING";
+            default -> "UNKNOWN";
+        };
+    }
+
     private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of(
             SUPPORT_DIALOG_TYPE_UNKNOWN,
             SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
@@ -372,6 +394,17 @@
         return createSupportIntent(featureId, type);
     }
 
+    /** @hide */
+    @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+    public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+            boolean learnMoreClicked) {
+        try {
+            mService.logDialogShown(featureId, type, learnMoreClicked);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * A callback class for monitoring changes to Advanced Protection state
      *
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index 1939f82..0fc0fbe 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -35,4 +35,6 @@
     void setAdvancedProtectionEnabled(boolean enabled);
     @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
     List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
+    @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
+    void logDialogShown(int featureId, int type, boolean learnMoreClicked);
 }
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 7660ed9..815444d 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -219,7 +219,7 @@
                 // Gets a read/write buffer mapping the entire shared memory region.
                 buffer = mRankingMapFd.mapReadWrite();
                 // Puts the ranking map into the shared memory region buffer.
-                buffer.put(mapParcel.marshall(), 0, mapSize);
+                mapParcel.marshall(buffer);
                 // Protects the region from being written to, by setting it to be read-only.
                 mRankingMapFd.setProtect(OsConstants.PROT_READ);
                 // Puts the SharedMemory object in the parcel.
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index dc7129cd..ea7ee4a 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
       "name": "CtsNotificationTestCases_notification"
     },
     {
-      "name": "FrameworksUiServicesTests_notification"
+      "name": "FrameworksUiServicesNotificationTests"
+    },
+    {
+      "name": "FrameworksUiServicesZenTests"
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1cf43d4..4cbd5be 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -2636,7 +2636,7 @@
             enabled = source.readInt() == 1;
             snoozing = source.readInt() == 1;
             if (source.readInt() == 1) {
-                name = source.readString8();
+                name = source.readString();
             }
             zenMode = source.readInt();
             conditionId = source.readParcelable(null, android.net.Uri.class);
@@ -2644,18 +2644,18 @@
             component = source.readParcelable(null, android.content.ComponentName.class);
             configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
             if (source.readInt() == 1) {
-                id = source.readString8();
+                id = source.readString();
             }
             creationTime = source.readLong();
             if (source.readInt() == 1) {
-                enabler = source.readString8();
+                enabler = source.readString();
             }
             zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
             zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
-            pkg = source.readString8();
+            pkg = source.readString();
             allowManualInvocation = source.readBoolean();
-            iconResName = source.readString8();
-            triggerDescription = source.readString8();
+            iconResName = source.readString();
+            triggerDescription = source.readString();
             type = source.readInt();
             userModifiedFields = source.readInt();
             zenPolicyUserModifiedFields = source.readInt();
@@ -2703,7 +2703,7 @@
             dest.writeInt(snoozing ? 1 : 0);
             if (name != null) {
                 dest.writeInt(1);
-                dest.writeString8(name);
+                dest.writeString(name);
             } else {
                 dest.writeInt(0);
             }
@@ -2714,23 +2714,23 @@
             dest.writeParcelable(configurationActivity, 0);
             if (id != null) {
                 dest.writeInt(1);
-                dest.writeString8(id);
+                dest.writeString(id);
             } else {
                 dest.writeInt(0);
             }
             dest.writeLong(creationTime);
             if (enabler != null) {
                 dest.writeInt(1);
-                dest.writeString8(enabler);
+                dest.writeString(enabler);
             } else {
                 dest.writeInt(0);
             }
             dest.writeParcelable(zenPolicy, 0);
             dest.writeParcelable(zenDeviceEffects, 0);
-            dest.writeString8(pkg);
+            dest.writeString(pkg);
             dest.writeBoolean(allowManualInvocation);
-            dest.writeString8(iconResName);
-            dest.writeString8(triggerDescription);
+            dest.writeString(iconResName);
+            dest.writeString(triggerDescription);
             dest.writeInt(type);
             dest.writeInt(userModifiedFields);
             dest.writeInt(zenPolicyUserModifiedFields);
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 6ed8c6d..929e39f 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -421,7 +421,12 @@
         Intent intent = new Intent(SERVICE_INTERFACE);
         intent.setComponent(serviceInfo.getComponentName());
         int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
-        mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+        if (mServiceInfo == null) {
+            mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+        } else {
+            mLifecycleExecutor.execute(() -> mContext.bindServiceAsUser(intent, this, flags,
+                    UserHandle.of(mServiceInfo.getUserId())));
+        }
         resetServiceConnectionTimeout();
     }
 
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3f45e29..5c81654 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -286,7 +286,7 @@
     /**
      * Display flag: Indicates that the display should show system decorations.
      * <p>
-     * This flag identifies secondary displays that should show system decorations, such as
+     * This flag identifies secondary displays that should always show system decorations, such as
      * navigation bar, home activity or wallpaper.
      * </p>
      * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
@@ -401,6 +401,18 @@
     public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 14;
 
     /**
+     * Display flag: Indicates that the display is allowed to switch the content mode between
+     * projected/extended and mirroring. This allows the display to dynamically add or remove the
+     * home and system decorations.
+     *
+     * Note that this flag shouldn't be enabled with {@link #FLAG_PRIVATE} or
+     * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} at the same time; otherwise it will be ignored.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 15;
+
+    /**
      * Display flag: Indicates that the contents of the display should not be scaled
      * to fit the physical screen dimensions.  Used for development only to emulate
      * devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d880072..bf000d5 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -1125,6 +1125,9 @@
         if ((flags & Display.FLAG_REAR) != 0) {
             result.append(", FLAG_REAR_DISPLAY");
         }
+        if ((flags & Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0) {
+            result.append(", FLAG_ALLOWS_CONTENT_MODE_SWITCH");
+        }
         return result.toString();
     }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4fc894c..421d28d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -699,7 +699,7 @@
     /**
      * Indicates the display should show system decors.
      * <p>
-     * System decors include status bar, navigation bar, launcher.
+     * System decors include status bar, navigation bar, launcher, and wallpaper.
      * </p>
      *
      * @param displayId The id of the display.
@@ -719,6 +719,23 @@
     void setShouldShowSystemDecors(int displayId, boolean shouldShow);
 
     /**
+     * Indicates that the display is eligible for the desktop mode from WindowManager's perspective.
+     * This includes:
+     * - The default display;
+     * - Any display that is allowed to switch the content mode between extended and mirroring
+     * (which means it can dynamically add or remove system decors), and it is now in extended mode
+     * (should currently show system decors).
+     * <p>
+     * System decors include status bar, navigation bar, launcher, and wallpaper.
+     * </p>
+     *
+     * @param displayId The id of the display.
+     * @return {@code true} if the display is eligible for the desktop mode from WindowManager's
+     * perspective.
+     */
+    boolean isEligibleForDesktopMode(int displayId);
+
+    /**
      * Indicates the policy for how the display should show IME.
      *
      * @param displayId The id of the display.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index da91e8d..2edce5d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -555,8 +555,6 @@
     @UiContext
     public final Context mContext;
 
-    private UiModeManager mUiModeManager;
-
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
     @NonNull Display mDisplay;
@@ -2079,8 +2077,7 @@
                 // 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 both
                 // force invert and the system's dark theme are enabled.
-                if (getUiModeManager().getForceInvertState() ==
-                        UiModeManager.FORCE_INVERT_TYPE_DARK) {
+                if (shouldApplyForceInvertDark()) {
                     final boolean isLightTheme =
                         a.getBoolean(R.styleable.Theme_isLightTheme, false);
                     // TODO: b/372558459 - Also check the background ColorDrawable color lightness
@@ -2108,6 +2105,14 @@
         }
     }
 
+    private boolean shouldApplyForceInvertDark() {
+        final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
+        if (uiModeManager == null) {
+            return false;
+        }
+        return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK;
+    }
+
     private void updateForceDarkMode() {
         if (mAttachInfo.mThreadedRenderer == null) return;
         if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
@@ -9413,13 +9418,6 @@
         return mAudioManager;
     }
 
-    private UiModeManager getUiModeManager() {
-        if (mUiModeManager == null) {
-            mUiModeManager = mContext.getSystemService(UiModeManager.class);
-        }
-        return mUiModeManager;
-    }
-
     private Vibrator getSystemVibrator() {
         if (mVibrator == null) {
             mVibrator = mContext.getSystemService(Vibrator.class);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a41ab36..b3bd89c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2454,6 +2454,7 @@
                                 & WindowInsets.Type.ime()) == 0
                         || viewRootImpl.getInsetsController()
                                 .isPredictiveBackImeHideAnimInProgress())) {
+                    Handler vh = view.getHandler();
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
                     if (resultReceiver != null) {
@@ -2464,8 +2465,17 @@
                                         : InputMethodManager.RESULT_SHOWN, null);
                     }
                     // TODO(b/322992891) handle case of SHOW_IMPLICIT
-                    viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
-                            false /* fromIme */, statsToken);
+                    if (vh.getLooper() != Looper.myLooper()) {
+                        // The view is running on a different thread than our own, so
+                        // we need to reschedule our work for over there.
+                        if (DEBUG) Log.v(TAG, "Show soft input: reschedule to view thread");
+                        final var finalStatsToken = statsToken;
+                        vh.post(() -> viewRootImpl.getInsetsController().show(
+                                WindowInsets.Type.ime(), false /* fromIme */, finalStatsToken));
+                    } else {
+                        viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
+                                false /* fromIme */, statsToken);
+                    }
                     return true;
                 }
                 ImeTracker.forLogging().onCancelled(statsToken,
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3557f16..ced27d6 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,6 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UiThread;
 import android.app.UriGrantsManager;
 import android.content.ContentProvider;
 import android.content.Intent;
@@ -38,6 +37,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.CancellationSignalBeamer;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -468,13 +468,27 @@
         });
     }
 
+    /**
+     * Returns {@code false} if there is a sessionId mismatch and logs the event.
+     */
+    private boolean checkSessionId(@NonNull InputConnectionCommandHeader header) {
+        if (header.mSessionId != mCurrentSessionId.get()) {
+            Log.w(TAG, "Session id mismatch header.sessionId: " + header.mSessionId
+                            + " currentSessionId: " + mCurrentSessionId.get() + " while calling "
+                    + Debug.getCaller());
+            //TODO(b/396066692): log metrics.
+            return false;  // cancelled
+        }
+        return true;
+    }
+
     @Dispatching(cancellable = true)
     @Override
     public void getTextAfterCursor(InputConnectionCommandHeader header, int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getTextAfterCursor", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -495,8 +509,8 @@
     public void getTextBeforeCursor(InputConnectionCommandHeader header, int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getTextBeforeCursor", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -517,8 +531,8 @@
     public void getSelectedText(InputConnectionCommandHeader header, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getSelectedText", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -539,8 +553,8 @@
     public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
             int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
         dispatchWithTracing("getSurroundingText", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -567,8 +581,8 @@
     public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
             AndroidFuture future /* T=Integer */) {
         dispatchWithTracing("getCursorCapsMode", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return 0;  // cancelled
+            if (!checkSessionId(header)) {
+                return 0;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -584,8 +598,8 @@
     public void getExtractedText(InputConnectionCommandHeader header, ExtractedTextRequest request,
             int flags, AndroidFuture future /* T=ExtractedText */) {
         dispatchWithTracing("getExtractedText", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -601,8 +615,8 @@
     public void commitText(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition) {
         dispatchWithTracing("commitText", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return;
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -618,8 +632,8 @@
     public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition, @Nullable TextAttribute textAttribute) {
         dispatchWithTracing("commitTextWithTextAttribute", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -634,8 +648,8 @@
     @Override
     public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
         dispatchWithTracing("commitCompletion", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -650,8 +664,8 @@
     @Override
     public void commitCorrection(InputConnectionCommandHeader header, CorrectionInfo info) {
         dispatchWithTracing("commitCorrection", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -670,8 +684,8 @@
     @Override
     public void setSelection(InputConnectionCommandHeader header, int start, int end) {
         dispatchWithTracing("setSelection", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -686,8 +700,8 @@
     @Override
     public void performEditorAction(InputConnectionCommandHeader header, int id) {
         dispatchWithTracing("performEditorAction", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -702,8 +716,8 @@
     @Override
     public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
         dispatchWithTracing("performContextMenuAction", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -718,8 +732,8 @@
     @Override
     public void setComposingRegion(InputConnectionCommandHeader header, int start, int end) {
         dispatchWithTracing("setComposingRegion", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -739,8 +753,8 @@
     public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
             int end, @Nullable TextAttribute textAttribute) {
         dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -756,8 +770,8 @@
     public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition) {
         dispatchWithTracing("setComposingText", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -773,8 +787,8 @@
     public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
             CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
         dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -826,8 +840,8 @@
                 }
                 return;
             }
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null && mDeactivateRequested.get()) {
@@ -842,8 +856,8 @@
     @Override
     public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
         dispatchWithTracing("sendKeyEvent", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -858,8 +872,8 @@
     @Override
     public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
         dispatchWithTracing("clearMetaKeyStates", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -875,8 +889,8 @@
     public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
             int afterLength) {
         dispatchWithTracing("deleteSurroundingText", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -892,8 +906,8 @@
     public void deleteSurroundingTextInCodePoints(InputConnectionCommandHeader header,
             int beforeLength, int afterLength) {
         dispatchWithTracing("deleteSurroundingTextInCodePoints", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -912,8 +926,8 @@
     @Override
     public void beginBatchEdit(InputConnectionCommandHeader header) {
         dispatchWithTracing("beginBatchEdit", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -928,8 +942,8 @@
     @Override
     public void endBatchEdit(InputConnectionCommandHeader header) {
         dispatchWithTracing("endBatchEdit", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -944,8 +958,8 @@
     @Override
     public void performSpellCheck(InputConnectionCommandHeader header) {
         dispatchWithTracing("performSpellCheck", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -961,8 +975,8 @@
     public void performPrivateCommand(InputConnectionCommandHeader header, String action,
             Bundle data) {
         dispatchWithTracing("performPrivateCommand", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -995,12 +1009,12 @@
             }
         }
         dispatchWithTracing("performHandwritingGesture", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
+            if (!checkSessionId(header)) {
                 if (resultReceiver != null) {
                     resultReceiver.send(
                             InputConnection.HANDWRITING_GESTURE_RESULT_CANCELLED, null);
                 }
-                return;  // cancelled
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1038,9 +1052,9 @@
                 (PreviewableHandwritingGesture) gestureContainer.get();
 
         dispatchWithTracing("previewHandwritingGesture", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()
+            if (!checkSessionId(header)
                     || (cancellationSignal != null && cancellationSignal.isCanceled())) {
-                return;  // cancelled
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1065,8 +1079,8 @@
     public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
             int imeDisplayId, AndroidFuture future /* T=Boolean */) {
         dispatchWithTracing("requestCursorUpdates", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return false;  // cancelled
+            if (!checkSessionId(header)) {
+                return false; // cancelled.
             }
             return requestCursorUpdatesInternal(
                     cursorUpdateMode, 0 /* cursorUpdateFilter */, imeDisplayId);
@@ -1079,8 +1093,8 @@
             int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
             AndroidFuture future /* T=Boolean */) {
         dispatchWithTracing("requestCursorUpdates", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return false;  // cancelled
+            if (!checkSessionId(header)) {
+                return false; // cancelled.
             }
             return requestCursorUpdatesInternal(
                     cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
@@ -1123,9 +1137,9 @@
             InputConnectionCommandHeader header, RectF bounds,
             @NonNull ResultReceiver resultReceiver) {
         dispatchWithTracing("requestTextBoundsInfo", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
+            if (!checkSessionId(header)) {
                 resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
-                return;  // cancelled
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1168,8 +1182,8 @@
                 return false;
             }
 
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return false;  // cancelled
+            if (!checkSessionId(header)) {
+                return false; // cancelled.
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1193,8 +1207,8 @@
     @Override
     public void setImeConsumesInput(InputConnectionCommandHeader header, boolean imeConsumesInput) {
         dispatchWithTracing("setImeConsumesInput", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1217,8 +1231,8 @@
         dispatchWithTracing(
                 "replaceText",
                 () -> {
-                    if (header.mSessionId != mCurrentSessionId.get()) {
-                        return; // cancelled
+                    if (!checkSessionId(header)) {
+                        return; // cancelled.
                     }
                     InputConnection ic = getInputConnection();
                     if (ic == null || mDeactivateRequested.get()) {
@@ -1236,8 +1250,8 @@
         public void commitText(InputConnectionCommandHeader header, CharSequence text,
                 int newCursorPosition, @Nullable TextAttribute textAttribute) {
             dispatchWithTracing("commitTextFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1256,8 +1270,8 @@
         @Override
         public void setSelection(InputConnectionCommandHeader header, int start, int end) {
             dispatchWithTracing("setSelectionFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1273,8 +1287,8 @@
         public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
                 int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
             dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return null;  // cancelled
+                if (!checkSessionId(header)) {
+                    return null; // cancelled.
                 }
                 final InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1301,8 +1315,8 @@
         public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
                 int afterLength) {
             dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1317,8 +1331,8 @@
         @Override
         public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
             dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1333,8 +1347,8 @@
         @Override
         public void performEditorAction(InputConnectionCommandHeader header, int id) {
             dispatchWithTracing("performEditorActionFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1349,8 +1363,8 @@
         @Override
         public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
             dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1366,8 +1380,8 @@
         public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
                 AndroidFuture future /* T=Integer */) {
             dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return 0;  // cancelled
+                if (!checkSessionId(header)) {
+                    return 0; // cancelled.
                 }
                 final InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1382,8 +1396,8 @@
         @Override
         public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
             dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index a4ea64e..67e5442 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -214,3 +214,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "invalidate_input_calls_restart"
+  namespace: "input_method"
+  description: "Feature flag to fix the race between invalidateInput and restartInput"
+  bug: "396066692"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index cc2afbc..d53c787 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.RemoteAnimationTarget;
@@ -39,8 +38,6 @@
 
     @BackEvent.SwipeEdge
     private final int mSwipeEdge;
-    @Nullable
-    private final RemoteAnimationTarget mDepartingAnimationTarget;
 
     /**
      * Creates a new {@link BackMotionEvent} instance.
@@ -53,8 +50,6 @@
      * @param progress Value between 0 and 1 on how far along the back gesture is.
      * @param triggerBack Indicates whether the back arrow is in the triggered state or not
      * @param swipeEdge Indicates which edge the swipe starts from.
-     * @param departingAnimationTarget The remote animation target of the departing
-     *                                 application window.
      */
     public BackMotionEvent(
             float touchX,
@@ -62,15 +57,13 @@
             long frameTimeMillis,
             float progress,
             boolean triggerBack,
-            @BackEvent.SwipeEdge int swipeEdge,
-            @Nullable RemoteAnimationTarget departingAnimationTarget) {
+            @BackEvent.SwipeEdge int swipeEdge) {
         mTouchX = touchX;
         mTouchY = touchY;
         mFrameTimeMillis = frameTimeMillis;
         mProgress = progress;
         mTriggerBack = triggerBack;
         mSwipeEdge = swipeEdge;
-        mDepartingAnimationTarget = departingAnimationTarget;
     }
 
     private BackMotionEvent(@NonNull Parcel in) {
@@ -79,7 +72,6 @@
         mProgress = in.readFloat();
         mTriggerBack = in.readBoolean();
         mSwipeEdge = in.readInt();
-        mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
         mFrameTimeMillis = in.readLong();
     }
 
@@ -108,7 +100,6 @@
         dest.writeFloat(mProgress);
         dest.writeBoolean(mTriggerBack);
         dest.writeInt(mSwipeEdge);
-        dest.writeTypedObject(mDepartingAnimationTarget, flags);
         dest.writeLong(mFrameTimeMillis);
     }
 
@@ -160,16 +151,6 @@
         return mFrameTimeMillis;
     }
 
-    /**
-     * Returns the {@link RemoteAnimationTarget} of the top departing application window,
-     * or {@code null} if the top window should not be moved for the current type of back
-     * destination.
-     */
-    @Nullable
-    public RemoteAnimationTarget getDepartingAnimationTarget() {
-        return mDepartingAnimationTarget;
-    }
-
     @Override
     public String toString() {
         return "BackMotionEvent{"
@@ -179,7 +160,6 @@
                 + ", mProgress=" + mProgress
                 + ", mTriggerBack=" + mTriggerBack
                 + ", mSwipeEdge=" + mSwipeEdge
-                + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
                 + "}";
     }
 }
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index 4908068..ea1b6406 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -20,7 +20,6 @@
 import android.os.SystemProperties;
 import android.util.MathUtils;
 import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
 
 import java.io.PrintWriter;
 
@@ -147,15 +146,14 @@
     }
 
     /** Creates a start {@link BackMotionEvent}. */
-    public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
+    public BackMotionEvent createStartEvent() {
         return new BackMotionEvent(
                 /* touchX = */ mInitTouchX,
                 /* touchY = */ mInitTouchY,
                 /* frameTimeMillis = */ 0,
                 /* progress = */ 0,
                 /* triggerBack = */ mTriggerBack,
-                /* swipeEdge = */ mSwipeEdge,
-                /* departingAnimationTarget = */ target);
+                /* swipeEdge = */ mSwipeEdge);
     }
 
     /** Creates a progress {@link BackMotionEvent}. */
@@ -239,8 +237,7 @@
                 /* frameTimeMillis = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ mTriggerBack,
-                /* swipeEdge = */ mSwipeEdge,
-                /* departingAnimationTarget = */ null);
+                /* swipeEdge = */ mSwipeEdge);
     }
 
     /** Sets the thresholds for computing progress. */
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 866c16c..5e8ce5e 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -56,9 +56,13 @@
     ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
             com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
             true),
+    ENABLE_DISPLAY_DISCONNECT_INTERACTION(Flags::enableDisplayDisconnectInteraction, false),
     ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, true),
+    ENABLE_DISPLAY_RECONNECT_INTERACTION(Flags::enableDisplayReconnectInteraction, false),
     ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, true),
     ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
+    ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false),
+    ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false),
     ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true),
     ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
     ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index aecf6eb..5b3044e 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -146,6 +146,8 @@
             Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
     INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES(
             Flags::inheritTaskBoundsForTrampolineTaskLaunches, false),
+    SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX(
+            Flags::skipDecorViewRelayoutWhenClosingBugfix, false),
     // go/keep-sorted end
     ;
 
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 6f4dd4e..0b84070 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -66,17 +66,6 @@
     void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t);
 
     /**
-     * Starts a legacy transition.
-     * @param type The transition type.
-     * @param adapter The animation to use.
-     * @param syncCallback A sync callback for the contents of `t`
-     * @param t Operations that are part of the transition.
-     * @return sync-id or -1 if this no-op'd because a transition is already running.
-     */
-    int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
-            in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
-
-    /**
      * Finishes a transition. This must be called for all created transitions.
      * @param transitionToken Which transition to finish
      * @param t Changes to make before finishing but in the same SF Transaction. Can be null.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index d478108..69613a7 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -270,8 +270,7 @@
                 }
                 mIOnBackInvokedCallback.onBackStarted(
                         new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
-                                backEvent.getProgress(), false, backEvent.getSwipeEdge(),
-                                null));
+                                backEvent.getProgress(), false, backEvent.getSwipeEdge()));
             } catch (RemoteException e) {
                 Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
             }
@@ -286,8 +285,7 @@
                 }
                 mIOnBackInvokedCallback.onBackProgressed(
                         new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
-                                backEvent.getProgress(), false, backEvent.getSwipeEdge(),
-                                null));
+                                backEvent.getProgress(), false, backEvent.getSwipeEdge()));
             } catch (RemoteException e) {
                 Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
             }
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 5c5da49..6e56b63 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -130,27 +130,6 @@
     }
 
     /**
-     * Start a legacy transition.
-     * @param type The type of the transition. This is ignored if a transitionToken is provided.
-     * @param adapter An existing transition to start. If null, a new transition is created.
-     * @param t The set of window operations that are part of this transition.
-     * @return true on success, false if a transition was already running.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
-    @NonNull
-    public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
-            @NonNull WindowContainerTransactionCallback syncCallback,
-            @NonNull WindowContainerTransaction t) {
-        try {
-            return getWindowOrganizerController().startLegacyTransition(
-                    type, adapter, syncCallback.mInterface, t);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Register an ITransitionPlayer to handle transition animations.
      * @hide
      */
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e706af9..afc9660 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -602,6 +602,13 @@
 }
 
 flag {
+    name: "keyboard_shortcuts_to_switch_desks"
+    namespace: "lse_desktop_experience"
+    description: "Enable switching the active desk with keyboard shortcuts"
+    bug: "389957556"
+}
+
+flag {
     name: "enable_connected_displays_dnd"
     namespace: "lse_desktop_experience"
     description: "Enable drag-and-drop between connected displays."
@@ -926,6 +933,16 @@
 }
 
 flag {
+    name: "skip_decor_view_relayout_when_closing_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables bugfix to skip DecorView relayout when the corresponding window is closing."
+    bug: "394502142"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_size_compat_mode_improvements_for_connected_displays"
     namespace: "lse_desktop_experience"
     description: "Enable some improvements in size compat mode for connected displays."
@@ -945,3 +962,20 @@
     description: "Enable restart menu UI, which is shown when an app moves between displays."
     bug: "397804287"
 }
+
+flag {
+    name: "enable_dynamic_radius_computation_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context."
+    bug: "399630464"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "show_home_behind_desktop"
+    namespace: "lse_desktop_experience"
+    description: "Enables the home to be shown behind the desktop."
+    bug: "375644149"
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserContentManager.java b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
new file mode 100644
index 0000000..09c6f5e
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.android.internal.R;
+
+public class MediaRouteChooserContentManager {
+    Context mContext;
+
+    private final boolean mShowProgressBarWhenEmpty;
+
+    public MediaRouteChooserContentManager(Context context, boolean showProgressBarWhenEmpty) {
+        mContext = context;
+        mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+    }
+
+    /**
+     * Starts binding all the views (list view, empty view, etc.) using the
+     * given container view.
+     */
+    public void bindViews(View containerView) {
+        View emptyView = containerView.findViewById(android.R.id.empty);
+        ListView listView = containerView.findViewById(R.id.media_route_list);
+        listView.setEmptyView(emptyView);
+
+        if (!mShowProgressBarWhenEmpty) {
+            containerView.findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+            // Center the empty view when the progress bar is not shown.
+            LinearLayout.LayoutParams params =
+                    (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+            params.gravity = Gravity.CENTER;
+            emptyView.setLayoutParams(params);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 23d966fd..5030a14 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -23,14 +23,12 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.TypedValue;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -52,15 +50,15 @@
 public class MediaRouteChooserDialog extends AlertDialog {
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
-    private final boolean mShowProgressBarWhenEmpty;
 
     private int mRouteTypes;
     private View.OnClickListener mExtendedSettingsClickListener;
     private RouteAdapter mAdapter;
-    private ListView mListView;
     private Button mExtendedSettingsButton;
     private boolean mAttachedToWindow;
 
+    private final MediaRouteChooserContentManager mContentManager;
+
     public MediaRouteChooserDialog(Context context, int theme) {
         this(context, theme, true);
     }
@@ -70,7 +68,7 @@
 
         mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
         mCallback = new MediaRouterCallback();
-        mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+        mContentManager = new MediaRouteChooserContentManager(context, showProgressBarWhenEmpty);
     }
 
     /**
@@ -128,8 +126,9 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         // Note: setView must be called before super.onCreate().
-        setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
-                null));
+        View containerView = LayoutInflater.from(getContext()).inflate(
+                R.layout.media_route_chooser_dialog, null);
+        setView(containerView);
 
         setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
                 ? R.string.media_route_chooser_title_for_remote_display
@@ -140,25 +139,15 @@
 
         super.onCreate(savedInstanceState);
 
-        View emptyView = findViewById(android.R.id.empty);
         mAdapter = new RouteAdapter(getContext());
-        mListView = (ListView) findViewById(R.id.media_route_list);
-        mListView.setAdapter(mAdapter);
-        mListView.setOnItemClickListener(mAdapter);
-        mListView.setEmptyView(emptyView);
+        ListView listView = findViewById(R.id.media_route_list);
+        listView.setAdapter(mAdapter);
+        listView.setOnItemClickListener(mAdapter);
 
-        mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
+        mExtendedSettingsButton = findViewById(R.id.media_route_extended_settings_button);
         updateExtendedSettingsButton();
 
-        if (!mShowProgressBarWhenEmpty) {
-            findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
-
-            // Center the empty view when the progress bar is not shown.
-            LinearLayout.LayoutParams params =
-                    (LinearLayout.LayoutParams) emptyView.getLayoutParams();
-            params.gravity = Gravity.CENTER;
-            emptyView.setLayoutParams(params);
-        }
+        mContentManager.bindViews(containerView);
     }
 
     private void updateExtendedSettingsButton() {
@@ -240,8 +229,8 @@
                 view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
             }
             MediaRouter.RouteInfo route = getItem(position);
-            TextView text1 = (TextView)view.findViewById(android.R.id.text1);
-            TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+            TextView text1 = view.findViewById(android.R.id.text1);
+            TextView text2 = view.findViewById(android.R.id.text2);
             text1.setText(route.getName());
             CharSequence description = route.getDescription();
             if (TextUtils.isEmpty(description)) {
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
index a53d6b8..3fe6873 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
@@ -132,8 +132,13 @@
             }
         }
 
-        // TODO correct values
-        nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+        if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
+            nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1, -1, false));
+            nodeInfo.setClassName("android.widget.HorizontalScrollView");
+        } else {
+            nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+            nodeInfo.setClassName("android.widget.ScrollView");
+        }
 
         if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
             nodeInfo.setClassName("android.widget.HorizontalScrollView");
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index f70f4cb..db2c460 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -33,6 +33,7 @@
 import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
 import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
 import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;
+import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent.ScrollDirection;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -104,9 +105,9 @@
             if (isClickAction(action)) {
                 return performClick(component);
             } else if (isScrollForwardAction(action)) {
-                return scrollByOffset(mRemoteContext, component, -500) != 0;
+                return scrollDirection(mRemoteContext, component, ScrollDirection.FORWARD);
             } else if (isScrollBackwardAction(action)) {
-                return scrollByOffset(mRemoteContext, component, 500) != 0;
+                return scrollDirection(mRemoteContext, component, ScrollDirection.BACKWARD);
             } else if (isShowOnScreenAction(action)) {
                 return showOnScreen(mRemoteContext, component);
             } else {
@@ -141,19 +142,32 @@
     }
 
     private boolean showOnScreen(RemoteContext context, Component component) {
-        if (component.getParent() instanceof LayoutComponent) {
-            LayoutComponent parent = (LayoutComponent) component.getParent();
-            ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
+        ScrollableComponent scrollable = findScrollable(component);
 
-            if (scrollable != null) {
-                scrollable.showOnScreen(context, component.getComponentId());
-                return true;
-            }
+        if (scrollable != null) {
+            return scrollable.showOnScreen(context, component);
         }
 
         return false;
     }
 
+    @Nullable
+    private static ScrollableComponent findScrollable(Component component) {
+        Component parent = component.getParent();
+
+        while (parent != null) {
+            ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
+
+            if (scrollable != null) {
+                return scrollable;
+            } else {
+                parent = parent.getParent();
+            }
+        }
+
+        return null;
+    }
+
     /**
      * scroll content by the given offset
      *
@@ -173,6 +187,25 @@
     }
 
     /**
+     * scroll content in a given direction
+     *
+     * @param context
+     * @param component
+     * @param direction
+     * @return
+     */
+    public boolean scrollDirection(
+            RemoteContext context, Component component, ScrollDirection direction) {
+        ScrollableComponent scrollable = component.selfOrModifier(ScrollableComponent.class);
+
+        if (scrollable != null) {
+            return scrollable.scrollDirection(context, direction);
+        }
+
+        return false;
+    }
+
+    /**
      * Perform a click on the given component
      *
      * @param component
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index c38a44a..da4e8d6 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -39,6 +39,7 @@
     private final RemoteComposeDocumentAccessibility mRemoteDocA11y;
 
     private final SemanticNodeApplier<AccessibilityNodeInfo> mApplier;
+    private final View mHost;
 
     public PlatformRemoteComposeTouchHelper(
             View host,
@@ -47,6 +48,7 @@
         super(host);
         this.mRemoteDocA11y = remoteDocA11y;
         this.mApplier = applier;
+        this.mHost = host;
     }
 
     public static PlatformRemoteComposeTouchHelper forRemoteComposePlayer(
@@ -150,6 +152,7 @@
             boolean performed = mRemoteDocA11y.performAction(component, action, arguments);
 
             if (performed) {
+                mHost.invalidate();
                 invalidateRoot();
             }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index caf19e1..e5c20eb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -73,7 +73,9 @@
 
     // We also keep a more fine-grained BUILD number, exposed as
     // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
-    static final float BUILD = 0.5f;
+    static final float BUILD = 0.6f;
+
+    private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false;
 
     @NonNull ArrayList<Operation> mOperations = new ArrayList<>();
 
@@ -892,7 +894,10 @@
 
         registerVariables(context, mOperations);
         context.mMode = RemoteContext.ContextMode.UNSET;
-        mFirstPaint = true;
+
+        if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+            mFirstPaint = true;
+        }
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -1241,11 +1246,13 @@
         context.mRemoteComposeState = mRemoteComposeState;
         context.mRemoteComposeState.setContext(context);
 
-        // Update any dirty variables
-        if (mFirstPaint) {
-            mFirstPaint = false;
-        } else {
-            updateVariables(context, theme, mOperations);
+        if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+            // Update any dirty variables
+            if (mFirstPaint) {
+                mFirstPaint = false;
+            } else {
+                updateVariables(context, theme, mOperations);
+            }
         }
 
         // If we have a content sizing set, we are going to take the original document
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index ac9f98b..add9d5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -111,6 +111,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DrawContentOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
@@ -257,6 +258,7 @@
     public static final int MODIFIER_HEIGHT = 67;
     public static final int MODIFIER_WIDTH_IN = 231;
     public static final int MODIFIER_HEIGHT_IN = 232;
+    public static final int MODIFIER_COLLAPSIBLE_PRIORITY = 235;
     public static final int MODIFIER_BACKGROUND = 55;
     public static final int MODIFIER_BORDER = 107;
     public static final int MODIFIER_PADDING = 58;
@@ -368,6 +370,7 @@
         map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
         map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
         map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
+        map.put(MODIFIER_COLLAPSIBLE_PRIORITY, CollapsiblePriorityModifierOperation::read);
         map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
         map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
         map.put(MODIFIER_BORDER, BorderModifierOperation::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index e37833f..b297a02 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -46,7 +46,7 @@
             new CoreDocument(); // todo: is this a valid way to initialize? bbade@
     public @NonNull RemoteComposeState mRemoteComposeState =
             new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
-
+    private long mDocLoadTime = System.currentTimeMillis();
     @Nullable protected PaintContext mPaintContext = null;
     protected float mDensity = Float.NaN;
 
@@ -83,6 +83,20 @@
         }
     }
 
+    /**
+     * Get the time the document was loaded
+     *
+     * @return time in ms since the document was loaded
+     */
+    public long getDocLoadTime() {
+        return mDocLoadTime;
+    }
+
+    /** Set the time the document was loaded */
+    public void setDocLoadTime() {
+        mDocLoadTime = System.currentTimeMillis();
+    }
+
     public boolean isAnimationEnabled() {
         return mAnimate;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
index e9cc26f..dee79a4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
@@ -85,6 +85,9 @@
     /** the year */
     public static final short TIME_YEAR = 12;
 
+    /** (value - doc_load_time) * 1E-3 */
+    public static final short TIME_FROM_LOAD_SEC = 14;
+
     /**
      * creates a new operation
      *
@@ -226,6 +229,7 @@
         int val = mType & 255;
         int flags = mType >> 8;
         RemoteContext ctx = context.getContext();
+        long load_time = ctx.getDocLoadTime();
         LongConstant longConstant = (LongConstant) ctx.getObject(mTimeId);
         long value = longConstant.getValue();
         long delta = 0;
@@ -292,6 +296,9 @@
             case TIME_YEAR:
                 ctx.loadFloat(mId, time.getYear());
                 break;
+            case TIME_FROM_LOAD_SEC:
+                ctx.loadFloat(mId, (value - load_time) * 1E-3f);
+                break;
         }
     }
 
@@ -334,6 +341,8 @@
                 return "TIME_DAY_OF_WEEK";
             case TIME_YEAR:
                 return "TIME_YEAR";
+            case TIME_FROM_LOAD_SEC:
+                return "TIME_FROM_LOAD_SEC";
             default:
                 return "INVALID_TIME_TYPE";
         }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index f1158d9..2a809c6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -824,15 +824,27 @@
      *
      * @param value a 2 dimension float array that will receive the horizontal and vertical position
      *     of the component.
+     * @param forSelf whether the location is for this container or a child, relevant for scrollable
+     *     items.
      */
-    public void getLocationInWindow(@NonNull float[] value) {
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
         value[0] += mX;
         value[1] += mY;
         if (mParent != null) {
-            mParent.getLocationInWindow(value);
+            mParent.getLocationInWindow(value, false);
         }
     }
 
+    /**
+     * Returns the location of the component relative to the root component
+     *
+     * @param value a 2 dimension float array that will receive the horizontal and vertical position
+     *     of the component.
+     */
+    public void getLocationInWindow(@NonNull float[] value) {
+        getLocationInWindow(value, true);
+    }
+
     @NonNull
     @Override
     public String toString() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index 6163d80..bc099e3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -289,11 +289,11 @@
     }
 
     @Override
-    public void getLocationInWindow(@NonNull float[] value) {
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
         value[0] += mX + mPaddingLeft;
         value[1] += mY + mPaddingTop;
         if (mParent != null) {
-            mParent.getLocationInWindow(value);
+            mParent.getLocationInWindow(value, false);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
index b008952..00ec605 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -24,10 +24,13 @@
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class CollapsibleColumnLayout extends ColumnLayout {
@@ -153,7 +156,7 @@
     }
 
     @Override
-    protected boolean hasVerticalIntrinsicDimension() {
+    public boolean hasVerticalIntrinsicDimension() {
         return true;
     }
 
@@ -166,25 +169,72 @@
             boolean verticalWrap,
             @NonNull MeasurePass measure,
             @NonNull Size size) {
+        computeVisibleChildren(
+                context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
+    }
+
+    @Override
+    public void computeSize(
+            @NonNull PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            @NonNull MeasurePass measure) {
+        computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+    }
+
+    @Override
+    public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+        // if needed, take care of weight calculations
+        super.internalLayoutMeasure(context, measure);
+        // Check again for visibility
+        ComponentMeasure m = measure.get(this);
+        computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+    }
+
+    private void computeVisibleChildren(
+            @NonNull PaintContext context,
+            float maxWidth,
+            float maxHeight,
+            boolean horizontalWrap,
+            boolean verticalWrap,
+            @NonNull MeasurePass measure,
+            @Nullable Size size) {
         int visibleChildren = 0;
         ComponentMeasure self = measure.get(this);
         self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
         float currentMaxHeight = maxHeight;
+        boolean hasPriorities = false;
         for (Component c : mChildrenComponents) {
-            if (c instanceof CollapsibleColumnLayout) {
-                c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
-            } else {
-                c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+            if (!measure.contains(c.getComponentId())) {
+                // No need to remeasure here if already done
+                if (c instanceof CollapsibleColumnLayout) {
+                    c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
+                } else {
+                    c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+                }
             }
+
             ComponentMeasure m = measure.get(c);
             if (!m.isGone()) {
-                size.setWidth(Math.max(size.getWidth(), m.getW()));
-                size.setHeight(size.getHeight() + m.getH());
+                if (size != null) {
+                    size.setWidth(Math.max(size.getWidth(), m.getW()));
+                    size.setHeight(size.getHeight() + m.getH());
+                }
                 visibleChildren++;
                 currentMaxHeight -= m.getH();
             }
+            if (c instanceof LayoutComponent) {
+                LayoutComponent lc = (LayoutComponent) c;
+                CollapsiblePriorityModifierOperation priority =
+                        lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+                if (priority != null) {
+                    hasPriorities = true;
+                }
+            }
         }
-        if (!mChildrenComponents.isEmpty()) {
+        if (!mChildrenComponents.isEmpty() && size != null) {
             size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildren - 1)));
         }
 
@@ -192,7 +242,14 @@
         float childrenHeight = 0f;
 
         boolean overflow = false;
-        for (Component child : mChildrenComponents) {
+        ArrayList<Component> children = mChildrenComponents;
+        if (hasPriorities) {
+            // TODO: We need to cache this.
+            children =
+                    CollapsiblePriority.sortWithPriorities(
+                            mChildrenComponents, CollapsiblePriority.VERTICAL);
+        }
+        for (Component child : children) {
             ComponentMeasure childMeasure = measure.get(child);
             if (overflow || childMeasure.isGone()) {
                 childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
@@ -209,10 +266,10 @@
                 visibleChildren++;
             }
         }
-        if (verticalWrap) {
+        if (verticalWrap && size != null) {
             size.setHeight(Math.min(maxHeight, childrenHeight));
         }
-        if (visibleChildren == 0 || size.getHeight() <= 0f) {
+        if (visibleChildren == 0 || (size != null && size.getHeight() <= 0f)) {
             self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
         }
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
new file mode 100644
index 0000000..46cd45e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+
+import java.util.ArrayList;
+
+/** Utility class to manage collapsible priorities on components */
+public class CollapsiblePriority {
+
+    public static final int HORIZONTAL = 0;
+    public static final int VERTICAL = 1;
+
+    /**
+     * Returns the priority of a child component
+     *
+     * @param c the child component
+     * @return priority value, or 0f if not found
+     */
+    static float getPriority(Component c, int orientation) {
+        if (c instanceof LayoutComponent) {
+            LayoutComponent lc = (LayoutComponent) c;
+            CollapsiblePriorityModifierOperation priority =
+                    lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+            if (priority != null && priority.getOrientation() == orientation) {
+                return priority.getPriority();
+            }
+        }
+        return Float.MAX_VALUE;
+    }
+
+    /**
+     * Allocate and return a sorted array of components by their priorities
+     *
+     * @param components the children components
+     * @return list of components sorted by their priority in decreasing order
+     */
+    static ArrayList<Component> sortWithPriorities(
+            ArrayList<Component> components, int orientation) {
+        ArrayList<Component> sorted = new ArrayList<>(components);
+        sorted.sort(
+                (t1, t2) -> {
+                    float p1 = getPriority(t1, orientation);
+                    float p2 = getPriority(t2, orientation);
+                    return (int) (p2 - p1);
+                });
+        return sorted;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
index 05f3329..e3632f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -24,10 +24,13 @@
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class CollapsibleRowLayout extends RowLayout {
@@ -135,8 +138,12 @@
     }
 
     @Override
-    protected boolean hasHorizontalIntrinsicDimension() {
-        return true;
+    public float minIntrinsicHeight(@NonNull RemoteContext context) {
+        float height = computeModifierDefinedHeight(context);
+        if (!mChildrenComponents.isEmpty()) {
+            height += mChildrenComponents.get(0).minIntrinsicHeight(context);
+        }
+        return height;
     }
 
     @Override
@@ -149,12 +156,8 @@
     }
 
     @Override
-    public float minIntrinsicHeight(@NonNull RemoteContext context) {
-        float height = computeModifierDefinedHeight(context);
-        if (!mChildrenComponents.isEmpty()) {
-            height += mChildrenComponents.get(0).minIntrinsicHeight(context);
-        }
-        return height;
+    public boolean hasHorizontalIntrinsicDimension() {
+        return true;
     }
 
     @Override
@@ -166,45 +169,107 @@
             boolean verticalWrap,
             @NonNull MeasurePass measure,
             @NonNull Size size) {
-        super.computeWrapSize(
-                context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+        computeVisibleChildren(
+                context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
     }
 
     @Override
-    public boolean applyVisibility(
-            float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
-        float childrenWidth = 0f;
-        float childrenHeight = 0f;
-        boolean changedVisibility = false;
+    public void computeSize(
+            @NonNull PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            @NonNull MeasurePass measure) {
+        computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+    }
+
+    @Override
+    public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+        // if needed, take care of weight calculations
+        super.internalLayoutMeasure(context, measure);
+        // Check again for visibility
+        ComponentMeasure m = measure.get(this);
+        computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+    }
+
+    private void computeVisibleChildren(
+            @NonNull PaintContext context,
+            float maxWidth,
+            float maxHeight,
+            boolean horizontalWrap,
+            boolean verticalWrap,
+            @NonNull MeasurePass measure,
+            @Nullable Size size) {
         int visibleChildren = 0;
         ComponentMeasure self = measure.get(this);
-        self.clearVisibilityOverride();
-        if (selfWidth <= 0 || selfHeight <= 0) {
-            self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
-            return true;
+        self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
+        float currentMaxWidth = maxWidth;
+        boolean hasPriorities = false;
+        for (Component c : mChildrenComponents) {
+            if (!measure.contains(c.getComponentId())) {
+                // No need to remeasure here if already done
+                if (c instanceof CollapsibleRowLayout) {
+                    c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure);
+                } else {
+                    c.measure(context, 0f, Float.MAX_VALUE, 0f, maxHeight, measure);
+                }
+            }
+            ComponentMeasure m = measure.get(c);
+            if (!m.isGone()) {
+                if (size != null) {
+                    size.setHeight(Math.max(size.getHeight(), m.getH()));
+                    size.setWidth(size.getWidth() + m.getW());
+                }
+                visibleChildren++;
+                currentMaxWidth -= m.getW();
+            }
+            if (c instanceof LayoutComponent) {
+                LayoutComponent lc = (LayoutComponent) c;
+                CollapsiblePriorityModifierOperation priority =
+                        lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+                if (priority != null) {
+                    hasPriorities = true;
+                }
+            }
         }
-        for (Component child : mChildrenComponents) {
+        if (!mChildrenComponents.isEmpty() && size != null) {
+            size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildren - 1)));
+        }
+
+        float childrenWidth = 0f;
+        float childrenHeight = 0f;
+
+        boolean overflow = false;
+        ArrayList<Component> children = mChildrenComponents;
+        if (hasPriorities) {
+            // TODO: We need to cache this.
+            children =
+                    CollapsiblePriority.sortWithPriorities(
+                            mChildrenComponents, CollapsiblePriority.HORIZONTAL);
+        }
+        for (Component child : children) {
             ComponentMeasure childMeasure = measure.get(child);
-            int visibility = childMeasure.getVisibility();
-            childMeasure.clearVisibilityOverride();
-            if (!childMeasure.isVisible()) {
+            if (overflow || childMeasure.isGone()) {
+                childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
                 continue;
             }
-            if (childrenWidth + childMeasure.getW() > selfWidth
-                    && childrenHeight + childMeasure.getH() > selfHeight) {
+            float childWidth = childMeasure.getW();
+            boolean childDoesNotFits = childrenWidth + childWidth > maxWidth;
+            if (childDoesNotFits) {
                 childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
-                if (visibility != childMeasure.getVisibility()) {
-                    changedVisibility = true;
-                }
+                overflow = true;
             } else {
-                childrenWidth += childMeasure.getW();
+                childrenWidth += childWidth;
                 childrenHeight = Math.max(childrenHeight, childMeasure.getH());
                 visibleChildren++;
             }
         }
-        if (visibleChildren == 0) {
+        if (horizontalWrap && size != null) {
+            size.setWidth(Math.min(maxWidth, childrenWidth));
+        }
+        if (visibleChildren == 0 || (size != null && size.getWidth() <= 0f)) {
             self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
         }
-        return changedVisibility;
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index cda90c2..9566242 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -33,6 +33,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
@@ -372,6 +373,17 @@
         DebugLog.e();
     }
 
+    @Override
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+        super.getLocationInWindow(value, forSelf);
+
+        if (!forSelf && mVerticalScrollDelegate instanceof ScrollModifierOperation) {
+            ScrollModifierOperation smo = (ScrollModifierOperation) mVerticalScrollDelegate;
+
+            value[1] += smo.getScrollY();
+        }
+    }
+
     /**
      * The name of the class
      *
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 5b66b95..eb10ead 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -226,9 +226,17 @@
                         measure,
                         mCachedWrapSize);
                 float w = mCachedWrapSize.getWidth();
-                computeSize(context, 0f, w, 0, measuredHeight, measure);
                 if (hasHorizontalScroll()) {
+                    computeSize(context, 0f, w, 0, measuredHeight, measure);
                     mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+                } else {
+                    computeSize(
+                            context,
+                            0f,
+                            Math.min(measuredWidth, insetMaxWidth),
+                            0,
+                            Math.min(measuredHeight, insetMaxHeight),
+                            measure);
                 }
             } else if (hasVerticalIntrinsicDimension()) {
                 mCachedWrapSize.setWidth(0f);
@@ -236,9 +244,17 @@
                 computeWrapSize(
                         context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
                 float h = mCachedWrapSize.getHeight();
-                computeSize(context, 0f, measuredWidth, 0, h, measure);
                 if (hasVerticalScroll()) {
+                    computeSize(context, 0f, measuredWidth, 0, h, measure);
                     mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+                } else {
+                    computeSize(
+                            context,
+                            0f,
+                            Math.min(measuredWidth, insetMaxWidth),
+                            0,
+                            Math.min(measuredHeight, insetMaxHeight),
+                            measure);
                 }
             } else {
                 float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index d5d2e03..15b54a3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -32,6 +32,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -386,6 +387,17 @@
         DebugLog.e();
     }
 
+    @Override
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+        super.getLocationInWindow(value, forSelf);
+
+        if (!forSelf && mHorizontalScrollDelegate instanceof ScrollModifierOperation) {
+            ScrollModifierOperation smo = (ScrollModifierOperation) mHorizontalScrollDelegate;
+
+            value[0] += smo.getScrollX();
+        }
+    }
+
     /**
      * The name of the class
      *
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index d383ee9..120c740 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -77,6 +77,7 @@
     private final Size mCachedSize = new Size(0f, 0f);
 
     @Nullable private String mCachedString = "";
+    @Nullable private String mNewString;
 
     Platform.ComputedTextLayout mComputedTextLayout;
 
@@ -99,7 +100,7 @@
         if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) {
             return;
         }
-        mCachedString = cachedString;
+        mNewString = cachedString;
         if (mType == -1) {
             if (mFontFamilyId != -1) {
                 String fontFamily = context.getText(mFontFamilyId);
@@ -119,8 +120,6 @@
                 mType = 0;
             }
         }
-        mTextW = -1;
-        mTextH = -1;
 
         if (mHorizontalScrollDelegate != null) {
             mHorizontalScrollDelegate.reset();
@@ -351,6 +350,9 @@
         mPaint.setColor(mColor);
         context.replacePaint(mPaint);
         float[] bounds = new float[4];
+        if (mNewString != null && !mNewString.equals(mCachedString)) {
+            mCachedString = mNewString;
+        }
         if (mCachedString == null) {
             return;
         }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
new file mode 100644
index 0000000..b1f2d2d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Set an optional priority on a component within a collapsible layout */
+public class CollapsiblePriorityModifierOperation extends Operation
+        implements ModifierOperation, Serializable {
+    private static final int OP_CODE = Operations.MODIFIER_COLLAPSIBLE_PRIORITY;
+    public static final String CLASS_NAME = "CollapsiblePriorityModifierOperation";
+
+    private float mPriority;
+    private int mOrientation;
+
+    public CollapsiblePriorityModifierOperation(int orientation, float priority) {
+        mOrientation = orientation;
+        mPriority = priority;
+    }
+
+    public float getPriority() {
+        return mPriority;
+    }
+
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mOrientation, mPriority);
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {
+        // nothing
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return "";
+    }
+
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
+    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+        int orientation = buffer.readInt();
+        float priority = buffer.readFloat();
+        operations.add(new CollapsiblePriorityModifierOperation(orientation, priority));
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
+    public static void documentation(@NonNull DocumentationBuilder doc) {
+        doc.operation("Layout Operations", OP_CODE, "CollapsiblePriorityModifier")
+                .description("Add additional priority to children of Collapsible layouts")
+                .field(DocumentedOperation.INT, "orientation", "Horizontal(0) or Vertical (1)")
+                .field(DocumentedOperation.FLOAT, "priority", "The associated priority");
+    }
+
+    /**
+     * Writes out the CollapsiblePriorityModifier to the buffer
+     *
+     * @param buffer buffer to write to
+     * @param priority priority value
+     * @param orientation orientation (HORIZONTAL or VERTICAL)
+     */
+    public static void apply(@NonNull WireBuffer buffer, int orientation, float priority) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(orientation);
+        buffer.writeFloat(priority);
+    }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer
+                .addTags(SerializeTags.MODIFIER)
+                .addType(name())
+                .add("orientation", mOrientation)
+                .add("priority", mPriority);
+    }
+
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(indent, "PRIORITY = [" + getPriority() + "] (" + mOrientation + ")");
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index 3e1f32d..42692f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -430,9 +430,35 @@
     }
 
     @Override
-    public boolean showOnScreen(RemoteContext context, int childId) {
-        // TODO correct this when we trust the bounds in parent
-        return scrollByOffset(context, -1000) != 0;
+    public boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+        float offset = mHostDimension * 0.7f;
+
+        if (direction == ScrollDirection.FORWARD
+                || direction == ScrollDirection.DOWN
+                || direction == ScrollDirection.RIGHT) {
+            offset *= -1;
+        }
+
+        return scrollByOffset(context, (int) offset) != 0;
+    }
+
+    @Override
+    public boolean showOnScreen(RemoteContext context, Component child) {
+        float[] locationInWindow = new float[2];
+        child.getLocationInWindow(locationInWindow);
+
+        int offset = 0;
+        if (handlesVerticalScroll()) {
+            offset = (int) -locationInWindow[1];
+        } else {
+            offset = (int) -locationInWindow[0];
+        }
+
+        if (offset == 0) {
+            return true;
+        } else {
+            return scrollByOffset(context, offset) != 0;
+        }
     }
 
     @Nullable
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index a95a175..120c7ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -35,7 +35,10 @@
     @NonNull
     public static String floatToString(
             float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
-
+        boolean isNeg = value < 0;
+        if (isNeg) {
+            value = -value;
+        }
         int integerPart = (int) value;
         float fractionalPart = value % 1;
 
@@ -54,14 +57,13 @@
             integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
         }
         if (afterDecimalPoint == 0) {
-            return integerPartString;
+            return ((isNeg) ? "-" : "") + integerPartString;
         }
         // Convert fractional part to string and pad with zeros
 
         for (int i = 0; i < afterDecimalPoint; i++) {
             fractionalPart *= 10;
         }
-
         fractionalPart = Math.round(fractionalPart);
 
         for (int i = 0; i < afterDecimalPoint; i++) {
@@ -87,6 +89,6 @@
             fact = fact + new String(c);
         }
 
-        return integerPartString + "." + fact;
+        return ((isNeg) ? "-" : "") + integerPartString + "." + fact;
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
index 3d1bd12..1610e63 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
@@ -18,6 +18,7 @@
 import android.annotation.Nullable;
 
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 
 /**
  * Interface for components that support scrolling.
@@ -48,13 +49,23 @@
     }
 
     /**
+     * Scrolls the content in the specified direction.
+     *
+     * @param direction the direction to scroll
+     * @return whether a scroll was possible
+     */
+    default boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+        return false;
+    }
+
+    /**
      * Show a child with the given ID on the screen, typically scrolling so it's fully on screen.
      *
-     * @param childId The ID of the child to check for visibility.
+     * @param child The child (including nested) to check for visibility.
      * @return {@code true} if the child with the given ID could be shown on screen; {@code false}
      *     otherwise.
      */
-    default boolean showOnScreen(RemoteContext context, int childId) {
+    default boolean showOnScreen(RemoteContext context, Component child) {
         return false;
     }
 
@@ -108,4 +119,13 @@
             return mCanScrollBackwards;
         }
     }
+
+    enum ScrollDirection {
+        FORWARD,
+        BACKWARD,
+        UP,
+        DOWN,
+        LEFT,
+        RIGHT,
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index e1f2924..575a6b2 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -22,7 +22,6 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.TouchListener;
 import com.android.internal.widget.remotecompose.core.VariableSupport;
@@ -43,7 +42,6 @@
  *
  * <p>This is used to play the RemoteCompose operations on Android.
  */
-@VisibleForTesting
 public class AndroidRemoteContext extends RemoteContext {
 
     public void useCanvas(Canvas canvas) {
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 0bc99ab..17f4fc8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -102,6 +102,7 @@
         mDocument = value;
         mDocument.initializeContext(mARContext);
         mDisable = false;
+        mARContext.setDocLoadTime();
         mARContext.setAnimationEnabled(true);
         mARContext.setDensity(mDensity);
         mARContext.setUseChoreographer(true);
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index dec724b..e1b3479 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -558,8 +558,7 @@
     delete parcel;
 }
 
-static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
-{
+static Parcel* parcel_for_marshall(JNIEnv* env, jlong nativePtr) {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel == NULL) {
        return NULL;
@@ -577,6 +576,16 @@
         return NULL;
     }
 
+    return parcel;
+}
+
+static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
+{
+    Parcel* parcel = parcel_for_marshall(env, nativePtr);
+    if (parcel == NULL) {
+       return NULL;
+    }
+
     jbyteArray ret = env->NewByteArray(parcel->dataSize());
 
     if (ret != NULL)
@@ -592,6 +601,56 @@
     return ret;
 }
 
+static long ensure_capacity(JNIEnv* env, Parcel* parcel, jint remaining) {
+    long dataSize = parcel->dataSize();
+    if (remaining < dataSize) {
+        jnihelp::ThrowException(env, "java/nio/BufferOverflowException", "()V");
+        return -1;
+    }
+    return dataSize;
+}
+
+static int android_os_Parcel_marshall_array(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                            jbyteArray data, jint offset, jint remaining)
+{
+    Parcel* parcel = parcel_for_marshall(env, nativePtr);
+    if (parcel == NULL) {
+       return 0;
+    }
+
+    long data_size = ensure_capacity(env, parcel, remaining);
+    if (data_size < 0) {
+        return 0;
+    }
+
+    jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
+    if (array != NULL)
+    {
+        memcpy(array + offset, parcel->data(), data_size);
+        env->ReleasePrimitiveArrayCritical(data, array, 0);
+    }
+    return data_size;
+}
+
+static int android_os_Parcel_marshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                             jobject javaBuffer, jint offset, jint remaining) {
+    Parcel* parcel = parcel_for_marshall(env, nativePtr);
+    if (parcel == NULL) {
+       return 0;
+    }
+
+    long data_size = ensure_capacity(env, parcel, remaining);
+    if (data_size < 0) {
+        return 0;
+    }
+
+    jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+    if (buffer != NULL) {
+        memcpy(buffer + offset, parcel->data(), data_size);
+    }
+    return data_size;
+}
+
 static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
                                           jbyteArray data, jint offset, jint length)
 {
@@ -613,6 +672,25 @@
     }
 }
 
+static void android_os_Parcel_unmarshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                                jobject javaBuffer, jint offset, jint length)
+{
+    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+    if (parcel == NULL || length < 0) {
+       return;
+    }
+
+    jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+    if (buffer)
+    {
+        parcel->setDataSize(length);
+        parcel->setDataPosition(0);
+
+        void* raw = parcel->writeInplace(length);
+        memcpy(raw, (buffer + offset), length);
+    }
+}
+
 static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr,
                                           jlong otherNativePtr)
 {
@@ -911,7 +989,10 @@
     {"nativeDestroy",             "(J)V", (void*)android_os_Parcel_destroy},
 
     {"nativeMarshall",            "(J)[B", (void*)android_os_Parcel_marshall},
+    {"nativeMarshallArray",       "(J[BII)I", (void*)android_os_Parcel_marshall_array},
+    {"nativeMarshallBuffer",      "(JLjava/nio/ByteBuffer;II)I", (void*)android_os_Parcel_marshall_buffer},
     {"nativeUnmarshall",          "(J[BII)V", (void*)android_os_Parcel_unmarshall},
+    {"nativeUnmarshallBuffer",    "(JLjava/nio/ByteBuffer;II)V", (void*)android_os_Parcel_unmarshall_buffer},
     {"nativeCompareData",         "(JJ)I", (void*)android_os_Parcel_compareData},
     {"nativeCompareDataInRange",  "(JIJII)Z", (void*)android_os_Parcel_compareDataInRange},
     {"nativeAppendFrom",          "(JJII)V", (void*)android_os_Parcel_appendFrom},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 59e01bf..9df351f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -35,7 +35,6 @@
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
 import "frameworks/base/core/proto/android/server/alarm/alarmmanagerservice.proto";
-import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
 import "frameworks/base/core/proto/android/server/fingerprint.proto";
 import "frameworks/base/core/proto/android/server/jobscheduler.proto";
 import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -483,10 +482,8 @@
         (section).args = "connmetrics --proto"
     ];
 
-    optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
-        (section).type = SECTION_DUMPSYS,
-        (section).args = "bluetooth_manager --proto"
-    ];
+    // Deprecated BluetoothManagerServiceDumpProto
+    reserved 3050;
 
     optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
         (section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 325790c..8393f8b 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -290,7 +290,16 @@
 
     optional SettingProto apply_ramping_ringer = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    message Display {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto cv_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Display display = 39;
+
+
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 39;
+    // Next tag = 40;
 }
diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp
deleted file mode 100644
index 362daa7..0000000
--- a/core/proto/android/server/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-  name: "srcs_bluetooth_manager_service_proto",
-  srcs: [
-      "bluetooth_manager_service.proto",
-  ],
-  visibility: ["//packages/modules/Bluetooth:__subpackages__"],
-}
-
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
deleted file mode 100644
index c33f66a..0000000
--- a/core/proto/android/server/bluetooth_manager_service.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package com.android.server;
-
-import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/bluetooth/enums.proto";
-
-option java_multiple_files = true;
-
-message BluetoothManagerServiceDumpProto {
-   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-   message ActiveLog {
-      option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-      optional int64 timestamp_ms = 1;
-      optional bool enable = 2;
-      optional string package_name = 3;
-      optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
-   }
-
-   optional bool enabled = 1;
-   optional int32 state = 2;
-   optional string state_name = 3;
-   optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
-   optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
-   optional int64 last_enabled_time_ms = 6;
-   optional int64 curr_timestamp_ms = 7;
-   repeated ActiveLog active_logs = 8;
-   optional int32 num_crashes = 9;
-   optional bool crash_log_maxed = 10;
-   repeated int64 crash_timestamps_ms = 11;
-   optional int32 num_ble_apps = 12;
-   repeated string ble_app_package_names = 13;
-}
\ No newline at end of file
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
index 902ef7f..615af6f 100644
--- a/core/res/res/layout/accessibility_autoclick_type_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -49,7 +49,8 @@
                     android:id="@+id/accessibility_autoclick_drag_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_drag"
-                    android:src="@drawable/accessibility_autoclick_drag" />
+                    android:src="@drawable/accessibility_autoclick_drag"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -60,7 +61,8 @@
                     android:id="@+id/accessibility_autoclick_double_click_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_double_click"
-                    android:src="@drawable/accessibility_autoclick_double_click" />
+                    android:src="@drawable/accessibility_autoclick_double_click"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -71,7 +73,8 @@
                     android:id="@+id/accessibility_autoclick_right_click_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_right_click"
-                    android:src="@drawable/accessibility_autoclick_right_click" />
+                    android:src="@drawable/accessibility_autoclick_right_click"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -82,7 +85,8 @@
                     android:id="@+id/accessibility_autoclick_scroll_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_scroll"
-                    android:src="@drawable/accessibility_autoclick_scroll" />
+                    android:src="@drawable/accessibility_autoclick_scroll"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -93,7 +97,8 @@
                     android:id="@+id/accessibility_autoclick_left_click_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_left_click"
-                    android:src="@drawable/accessibility_autoclick_left_click" />
+                    android:src="@drawable/accessibility_autoclick_left_click"
+                    android:clickable="false" />
             </LinearLayout>
 
         </LinearLayout>
@@ -114,7 +119,8 @@
                 android:id="@+id/accessibility_autoclick_pause_button"
                 style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                 android:contentDescription="@string/accessibility_autoclick_pause"
-                android:src="@drawable/accessibility_autoclick_pause" />
+                android:src="@drawable/accessibility_autoclick_pause"
+                android:clickable="false" />
         </LinearLayout>
 
         <LinearLayout
@@ -125,7 +131,8 @@
                 android:id="@+id/accessibility_autoclick_position_button"
                 style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                 android:contentDescription="@string/accessibility_autoclick_position"
-                android:src="@drawable/accessibility_autoclick_position" />
+                android:src="@drawable/accessibility_autoclick_position"
+                android:clickable="false" />
         </LinearLayout>
 
     </LinearLayout>
diff --git a/core/res/res/layout/notification_2025_action_list.xml b/core/res/res/layout/notification_2025_action_list.xml
index 053aca0..6c07ec1 100644
--- a/core/res/res/layout/notification_2025_action_list.xml
+++ b/core/res/res/layout/notification_2025_action_list.xml
@@ -22,6 +22,7 @@
     android:layout_height="wrap_content"
     android:layout_gravity="bottom"
     android:layout_marginBottom="@dimen/notification_2025_action_list_margin_bottom"
+    android:minHeight="@dimen/notification_2025_action_list_min_height"
     >
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
index 7ec2450..16c9500 100644
--- a/core/res/res/layout/notification_2025_conversation_icon_container.xml
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -29,7 +29,8 @@
     <FrameLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_margin="@dimen/notification_2025_margin"
+        android:layout_marginHorizontal="@dimen/notification_2025_margin"
+        android:layout_marginTop="@dimen/notification_2025_margin"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:layout_gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_2025_reply_history_container.xml b/core/res/res/layout/notification_2025_reply_history_container.xml
index 6923b59..256f7b8 100644
--- a/core/res/res/layout/notification_2025_reply_history_container.xml
+++ b/core/res/res/layout/notification_2025_reply_history_container.xml
@@ -28,16 +28,16 @@
             android:layout_width="match_parent"
             android:layout_height="1dip"
             android:id="@+id/action_divider"
-            android:layout_marginTop="@dimen/notification_content_margin"
-            android:layout_marginBottom="@dimen/notification_content_margin"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginTop="@dimen/notification_2025_margin"
+            android:layout_marginBottom="@dimen/notification_2025_margin"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:background="@drawable/notification_template_divider" />
 
     <TextView
             android:id="@+id/notification_material_reply_text_3"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:visibility="gone"
             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
             android:singleLine="true" />
@@ -46,7 +46,7 @@
             android:id="@+id/notification_material_reply_text_2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:visibility="gone"
             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
             android:singleLine="true" />
@@ -56,13 +56,13 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:layout_marginEnd="@dimen/notification_content_margin_end">
+            android:layout_marginEnd="@dimen/notification_2025_margin">
         <TextView
                 android:id="@+id/notification_material_reply_text_1"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:layout_gravity="center"
                 android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
                 android:singleLine="true" />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 63f32e3..57c89b9 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -74,7 +74,6 @@
             android:id="@+id/notification_headerless_view_column"
             android:layout_width="0px"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
             android:layout_weight="1"
             android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
             android:orientation="vertical"
@@ -157,7 +156,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
 
             <include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 732021c6..c57196e 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -35,7 +35,6 @@
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_2025_min_height"
             android:clipChildren="false"
             android:layout_weight="1"
             >
@@ -78,9 +77,7 @@
                     android:id="@+id/notification_headerless_view_column"
                     android:layout_width="0px"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
                     android:layout_weight="1"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginTop="@dimen/notification_2025_margin"
                     android:clipChildren="false"
                     android:orientation="vertical"
@@ -152,7 +149,7 @@
                     android:id="@+id/expand_button_touch_container"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    android:minWidth="@dimen/notification_content_margin_end"
+                    android:minWidth="@dimen/notification_2025_margin"
                     >
 
                     <include layout="@layout/notification_2025_expand_button"
@@ -173,21 +170,13 @@
 
         </FrameLayout>
 
-        <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
+        <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="-20dp"
-            android:clipChildren="false"
-            android:orientation="vertical">
-            <include layout="@layout/notification_template_smart_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
-                android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
-            <include layout="@layout/notification_2025_action_list" />
-        </LinearLayout>
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+            android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
+        <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
 
 </com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
index 1ee7ddc..1c6fcb5 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
@@ -35,7 +35,6 @@
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_2025_min_height"
             android:clipChildren="false"
             android:layout_weight="1"
             >
@@ -78,17 +77,11 @@
                 android:clipChildren="false"
                 >
 
-                <!--
-                  NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
-                  have the id/notification_headerless_view_column, as that is used for modifying
-                   vertical margins to accommodate the single-line state that base supports
-                  -->
                 <LinearLayout
+                    android:id="@+id/notification_headerless_view_column"
                     android:layout_width="0px"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
                     android:layout_weight="1"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginTop="@dimen/notification_2025_margin"
                     android:layout_marginStart="@dimen/notification_2025_content_margin_start"
                     android:clipChildren="false"
@@ -150,7 +143,6 @@
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
                     android:layout_marginTop="@dimen/notification_2025_margin"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:forceHasOverlappingRendering="false"
                     android:spacing="0dp"
@@ -175,7 +167,7 @@
                     android:id="@+id/expand_button_touch_container"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    android:minWidth="@dimen/notification_content_margin_end"
+                    android:minWidth="@dimen/notification_2025_margin"
                     >
 
                     <include layout="@layout/notification_2025_expand_button"
@@ -196,20 +188,13 @@
 
         </FrameLayout>
 
-    <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
+        <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="-20dp"
-            android:clipChildren="false"
-            android:orientation="vertical">
-        <include layout="@layout/notification_template_smart_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
-                android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+            android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
         <include layout="@layout/notification_2025_action_list" />
+
     </LinearLayout>
-</LinearLayout>
 </com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 629af77..de82f9f 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -76,7 +76,6 @@
             android:id="@+id/notification_headerless_view_column"
             android:layout_width="0px"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
             android:layout_weight="1"
             android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
             android:orientation="vertical"
@@ -178,7 +177,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
 
             <include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index af66025..8e2cb23 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -38,7 +38,6 @@
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_2025_min_height"
             android:clipChildren="false"
             android:layout_weight="1"
             >
@@ -61,7 +60,8 @@
                 android:layout_width="@dimen/notification_2025_icon_circle_size"
                 android:layout_height="@dimen/notification_2025_icon_circle_size"
                 android:layout_alignParentStart="true"
-                android:layout_margin="@dimen/notification_2025_margin"
+                android:layout_marginHorizontal="@dimen/notification_2025_margin"
+                android:layout_marginTop="@dimen/notification_2025_margin"
                 android:background="@drawable/notification_icon_circle"
                 android:padding="@dimen/notification_2025_icon_circle_padding"
                 />
@@ -89,17 +89,11 @@
                 android:clipChildren="false"
                 >
 
-                <!--
-                  NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
-                  have the id/notification_headerless_view_column, as that is used for modifying
-                   vertical margins to accommodate the single-line state that base supports
-                  -->
                 <LinearLayout
+                    android:id="@+id/notification_headerless_view_column"
                     android:layout_width="0px"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
                     android:layout_weight="1"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginTop="@dimen/notification_2025_margin"
                     android:layout_marginStart="@dimen/notification_2025_content_margin_start"
                     android:clipChildren="false"
@@ -161,7 +155,6 @@
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
                     android:layout_marginTop="@dimen/notification_2025_margin"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:forceHasOverlappingRendering="false"
                     android:spacing="0dp"
@@ -186,7 +179,7 @@
                     android:id="@+id/expand_button_touch_container"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    android:minWidth="@dimen/notification_content_margin_end"
+                    android:minWidth="@dimen/notification_2025_margin"
                     >
 
                     <include layout="@layout/notification_2025_expand_button"
@@ -207,20 +200,13 @@
 
         </FrameLayout>
 
-    <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
+        <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="-20dp"
-            android:clipChildren="false"
-            android:orientation="vertical">
-        <include layout="@layout/notification_template_smart_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
-                android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+            android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
         <include layout="@layout/notification_2025_action_list" />
+
     </LinearLayout>
-</LinearLayout>
 </com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
index 52bc7b8..b32a778 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
@@ -76,7 +76,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
             <include layout="@layout/notification_2025_expand_button"
                 android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
index be640460..268396f 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
@@ -103,7 +103,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
             <include layout="@layout/notification_2025_expand_button"
                 android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml
index 76a8581..2871107 100644
--- a/core/res/res/layout/notification_2025_template_expanded_base.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_base.xml
@@ -24,10 +24,8 @@
     >
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_content_margin"
         android:orientation="vertical"
         >
 
@@ -47,7 +45,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:orientation="vertical"
                 >
 
@@ -78,7 +76,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
index 999afa6..ead6d4c 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
@@ -28,7 +28,6 @@
     <include layout="@layout/notification_2025_right_icon" />
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="top"
@@ -43,7 +42,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             >
 
@@ -67,7 +66,7 @@
             android:layout_weight="1"
             android:layout_marginTop="13dp"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:background="@drawable/notification_big_picture_outline"
             android:clipToOutline="true"
             android:scaleType="centerCrop"
@@ -85,7 +84,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
index c9206ed..de5e71d 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
@@ -26,11 +26,9 @@
     <include layout="@layout/notification_2025_template_header" />
 
     <com.android.internal.widget.RemeasuringLinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
-        android:layout_marginBottom="@dimen/notification_content_margin"
         android:clipToPadding="false"
         android:orientation="vertical"
         >
@@ -43,7 +41,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:paddingStart="@dimen/notification_2025_content_margin_start"
-            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:paddingEnd="@dimen/notification_2025_margin"
             android:clipToPadding="false"
             android:orientation="vertical"
             android:layout_weight="1"
@@ -84,7 +82,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index ec21455..c096bc8 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -30,7 +30,6 @@
     <include layout="@layout/notification_2025_conversation_header"/>
 
     <com.android.internal.widget.RemeasuringLinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
@@ -46,7 +45,7 @@
             android:layout_gravity="top"
             android:layout_weight="1"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             android:clipChildren="false"
             >
@@ -62,7 +61,7 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end" />
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
 
         <include layout="@layout/notification_2025_action_list" />
 
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
index 6ee82fa..6eea8cc 100644
--- a/core/res/res/layout/notification_2025_template_expanded_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -29,7 +29,6 @@
     <include layout="@layout/notification_2025_conversation_header"/>
 
     <com.android.internal.widget.RemeasuringLinearLayout
-            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
@@ -44,7 +43,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_weight="1"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             android:clipChildren="false"
             >
@@ -64,7 +63,7 @@
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
+                android:layout_marginEnd="@dimen/notification_2025_margin" />
 
         <include layout="@layout/notification_2025_action_list" />
 
diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
index 1eaef22..327cd7a 100644
--- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
@@ -24,7 +24,6 @@
     >
     <include layout="@layout/notification_2025_template_header" />
     <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="top"
@@ -39,7 +38,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:paddingStart="@dimen/notification_2025_content_margin_start"
-            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:paddingEnd="@dimen/notification_2025_margin"
             android:layout_weight="1"
             android:clipToPadding="false"
             android:orientation="vertical"
@@ -126,7 +125,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin" />
         <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml
index 801e339..565a558a 100644
--- a/core/res/res/layout/notification_2025_template_expanded_media.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_media.xml
@@ -41,7 +41,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             >
             <include layout="@layout/notification_template_part_line1"/>
@@ -53,7 +53,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_media_actions_margin_start"
-            android:minHeight="@dimen/notification_content_margin"
+            android:minHeight="@dimen/notification_2025_margin"
             >
 
             <!-- Nesting in FrameLayout is required to ensure that the marginStart actually applies
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index 62059af..df48479 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -29,7 +29,6 @@
     <include layout="@layout/notification_2025_template_header"/>
 
     <com.android.internal.widget.RemeasuringLinearLayout
-            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
@@ -44,7 +43,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_weight="1"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             android:clipChildren="false"
             >
@@ -64,7 +63,7 @@
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
+                android:layout_marginEnd="@dimen/notification_2025_margin" />
 
         <include layout="@layout/notification_2025_action_list" />
 
diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml
index cf39d8b..b929b9e 100644
--- a/core/res/res/layout/notification_2025_template_expanded_progress.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml
@@ -25,10 +25,8 @@
     >
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_content_margin"
         android:orientation="vertical"
         >
 
@@ -48,7 +46,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:orientation="vertical"
                 >
 
@@ -114,7 +112,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml
index 4d3b245..e416c50 100644
--- a/core/res/res/layout/notification_2025_template_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml
@@ -56,7 +56,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
                 />
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7a38dce..43486f8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2831,6 +2831,10 @@
     <!-- Whether dreams are disabled when ambient mode is suppressed. -->
     <bool name="config_dreamsDisabledByAmbientModeSuppressionConfig">false</bool>
 
+    <!-- The default for the setting that controls when to auto-start hub mode.
+          0 means "never" -->
+    <integer name="config_whenToStartHubModeDefault">0</integer>
+
     <!-- The duration in milliseconds of the dream opening animation.  -->
     <integer name="config_dreamOpenAnimationDuration">250</integer>
     <!-- The duration in milliseconds of the dream closing animation.  -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6e54083..b5ed28f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -295,6 +295,11 @@
     <!-- The margin of the notification action list at the bottom in the 2025 redesign -->
     <dimen name="notification_2025_action_list_margin_bottom">6dp</dimen>
 
+    <!-- The minimum height of the notification action container, to act as a bottom padding for the
+         notification when there are no actions. This should always be equal to
+         notification_2025_margin - notification_2025_action_list_margin_bottom. -->
+    <dimen name="notification_2025_action_list_min_height">10dp</dimen>
+
     <!-- The overall height of the emphasized notification action -->
     <dimen name="notification_action_emphasized_height">48dp</dimen>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 922d59d..bab4a3d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2072,6 +2072,7 @@
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
   <java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
   <java-symbol type="bool" name="config_glanceableHubEnabled" />
+  <java-symbol type="integer" name="config_whenToStartHubModeDefault" />
   <java-symbol type="integer" name="config_keyguardDrawnTimeout" />
   <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
   <java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
index e8b295b..0287e6c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.hardware.usb.UsbDevice;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -118,6 +118,6 @@
 
         verify(mBrailleDisplayCallback).onConnectionFailed(
                 BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS);
-        verifyZeroInteractions(mAccessibilityServiceConnection);
+        verifyNoMoreInteractions(mAccessibilityServiceConnection);
     }
 }
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index dccbf40..70b4150 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -233,7 +233,7 @@
     /**
      * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
      */
-    private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
+    public class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
         static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
         static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
         static final String ATTRIBUTES_NAME = "test";
@@ -245,12 +245,6 @@
                     SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
         }
 
-        TestServicesCache(Injector<TestServiceType> injector,
-                XmlSerializerAndParser<TestServiceType> serializerAndParser) {
-            super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
-                    serializerAndParser);
-        }
-
         @Override
         public TestServiceType parseServiceAttributes(Resources res, String packageName,
                 AttributeSet attrs) {
@@ -338,7 +332,7 @@
         }
     }
 
-    static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
+    public static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
 
         public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException {
             out.attribute(null, "type", item.type);
@@ -353,7 +347,7 @@
         }
     }
 
-    static class TestServiceType implements Parcelable {
+    public static class TestServiceType implements Parcelable {
         final String type;
         final String value;
 
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
new file mode 100644
index 0000000..8349659
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.pm.Flags.FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.RegisteredServicesCacheTest.TestServiceType;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.os.BackgroundThread;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.content.pm.RegisteredServicesCache}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE)
+public class RegisteredServicesCacheUnitTest {
+    private static final String TAG = "RegisteredServicesCacheUnitTest";
+    private static final int U0 = 0;
+    private static final int U1 = 1;
+    private static final int UID1 = 1;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private final ResolveInfo mResolveInfo1 = new ResolveInfo();
+    private final ResolveInfo mResolveInfo2 = new ResolveInfo();
+    private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1");
+    private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2");
+    @Mock
+    RegisteredServicesCache.Injector<TestServiceType> mMockInjector;
+    @Mock
+    Context mMockContext;
+    Handler mMockBackgroundHandler;
+    @Mock
+    PackageManager mMockPackageManager;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockInjector.getContext()).thenReturn(mMockContext);
+        mMockBackgroundHandler = spy(BackgroundThread.getHandler());
+        when(mMockInjector.getBackgroundHandler()).thenReturn(mMockBackgroundHandler);
+        doReturn(mock(Intent.class)).when(mMockContext).registerReceiverAsUser(any(), any(), any(),
+                any(), any());
+        doReturn(mock(Intent.class)).when(mMockContext).registerReceiver(any(), any(), any(),
+                any());
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+        addServiceInfoIntoResolveInfo(mResolveInfo1, "r1.package.name" /* packageName */,
+                "r1.service.name" /* serviceName */);
+        addServiceInfoIntoResolveInfo(mResolveInfo2, "r2.package.name" /* packageName */,
+                "r2.service.name" /* serviceName */);
+    }
+
+    @Test
+    public void testSaveServiceInfoIntoCaches() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+        PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName),
+                anyInt(), eq(U1))).thenReturn(packageInfo2);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        int u1uid = UserHandle.getUid(U1, UID1);
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+                mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(),
+                2000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        testServicesCache.getAllServices(U1);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.invalidateCache(U1);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        testServicesCache.getAllServices(U1);
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+    }
+
+    @Test
+    public void testClearServiceInfoCachesAfterRemoveUserId() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.onUserRemoved(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+    }
+
+    @Test
+    public void testGetServiceInfoCachesForMultiUser() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U1))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.clearServicesForQuerying();
+        int u1uid = UserHandle.getUid(U1, UID1);
+        assertThat(u1uid).isNotEqualTo(UID1);
+
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+                mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2);
+
+        testServicesCache.getAllServices(U1);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.invalidateCache(U1);
+
+        // There is a bug to return the same info from the cache for different users. Make sure it
+        // will return the different info from the cache for different users.
+        Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> serviceInfos;
+        serviceInfos = testServicesCache.getAllServices(U0);
+        // Make sure the service info is retrieved from the cache for U0.
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+            assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(UID1);
+        }
+
+        serviceInfos = testServicesCache.getAllServices(U1);
+        // Make sure the service info is retrieved from the cache for U1.
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+        for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+            assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(u1uid);
+        }
+    }
+
+    @Test
+    public void testUpdateServiceInfoIntoCachesWhenPackageInfoNotFound() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+        reset(mMockPackageManager);
+
+        doThrow(new SecurityException("")).when(mMockPackageManager).getPackageInfoAsUser(
+                eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0));
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), anyLong());
+    }
+
+    @Test
+    public void testUpdateServiceInfoIntoCachesWhenTheApplicationHasBeenUpdated() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+        reset(mMockPackageManager);
+
+        PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo2);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(2000L));
+    }
+
+    @Test
+    public void testClearServiceInfoCachesAfterTimeout() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        // Immediately invoke run on the Runnable posted to the handler
+        doAnswer(invocation -> {
+            Message message = invocation.getArgument(0);
+            message.getCallback().run();
+            return true;
+        }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
+
+        reset(testServicesCache);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+    }
+
+    private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
+            TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) {
+        final ComponentInfo info = new ComponentInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.uid = uid;
+        return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime);
+    }
+
+    private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName,
+            String serviceName) {
+        final ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = packageName;
+        serviceInfo.name = serviceName;
+        resolveInfo.serviceInfo = serviceInfo;
+    }
+
+    private PackageInfo createPackageInfo(long lastUpdateTime) {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.lastUpdateTime = lastUpdateTime;
+        return packageInfo;
+    }
+
+    /**
+     * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
+     */
+    public class TestRegisteredServicesCache extends RegisteredServicesCache<TestServiceType> {
+        static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest";
+        static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest";
+        static final String ATTRIBUTES_NAME = "test";
+        private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices =
+                new SparseArray<>();
+
+        public TestRegisteredServicesCache(Injector<TestServiceType> injector,
+                XmlSerializerAndParser<TestServiceType> serializerAndParser) {
+            super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
+                    serializerAndParser);
+        }
+
+        @Override
+        public TestServiceType parseServiceAttributes(Resources res, String packageName,
+                AttributeSet attrs) {
+            return null;
+        }
+
+        @Override
+        protected List<ResolveInfo> queryIntentServices(int userId) {
+            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
+                    new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
+            return new ArrayList<>(map.keySet());
+        }
+
+        void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
+                ServiceInfo<TestServiceType> serviceInfo) {
+            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
+            if (map == null) {
+                map = new HashMap<>();
+                mServices.put(userId, map);
+            }
+            map.put(resolveInfo, serviceInfo);
+        }
+
+        void clearServicesForQuerying() {
+            mServices.clear();
+        }
+
+        @Override
+        protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo,
+                long lastUpdateTime) throws XmlPullParserException, IOException {
+            int size = mServices.size();
+            for (int i = 0; i < size; i++) {
+                Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
+                ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
+                if (serviceInfo != null) {
+                    return serviceInfo;
+                }
+            }
+            throw new IllegalArgumentException("Unexpected service " + resolveInfo);
+        }
+
+        @Override
+        public void onUserRemoved(int userId) {
+            super.onUserRemoved(userId);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
index 8b513cb..524e355 100644
--- a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
@@ -136,9 +136,11 @@
         SystemFeaturesCache cache = new SystemFeaturesCache(features);
 
         SystemFeaturesCache.clearInstance();
+        assertThat(SystemFeaturesCache.hasInstance()).isFalse();
         assertThrows(IllegalStateException.class, () -> SystemFeaturesCache.getInstance());
 
         SystemFeaturesCache.setInstance(cache);
+        assertThat(SystemFeaturesCache.hasInstance()).isTrue();
         assertThat(SystemFeaturesCache.getInstance()).isEqualTo(cache);
 
         assertThrows(
@@ -149,6 +151,7 @@
     @Test
     public void testSingletonAutomaticallySetWithFeatureEnabled() {
         assumeTrue(android.content.pm.Flags.cacheSdkSystemFeatures());
+        assertThat(SystemFeaturesCache.hasInstance()).isTrue();
         assertThat(SystemFeaturesCache.getInstance()).isNotNull();
     }
 
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index de5f0ff..34650be 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -264,7 +264,7 @@
                         /* isEventFilterExplicit */ true);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
         waitForHandler();
-        Mockito.verifyZeroInteractions(mDisplayListener);
+        Mockito.verifyNoMoreInteractions(mDisplayListener);
 
         mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
                 ALL_DISPLAY_EVENTS
@@ -272,7 +272,7 @@
                         /* isEventFilterExplicit */ true);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
         waitForHandler();
-        Mockito.verifyZeroInteractions(mDisplayListener);
+        Mockito.verifyNoMoreInteractions(mDisplayListener);
 
         mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
                 ALL_DISPLAY_EVENTS
@@ -280,7 +280,7 @@
                         /* isEventFilterExplicit */ true);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
         waitForHandler();
-        Mockito.verifyZeroInteractions(mDisplayListener);
+        Mockito.verifyNoMoreInteractions(mDisplayListener);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 3e652010..bb05910 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,6 +29,8 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -416,4 +418,63 @@
         int binderEndPos = pA.dataPosition();
         assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
     }
+
+    private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
+
+    // Allow for some Parcel overhead
+    private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
+
+    @Test
+    public void testMarshall_ByteBuffer_wrapped() {
+        ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
+        testMarshall_ByteBuffer(bb);
+    }
+
+    @Test
+    public void testMarshall_DirectByteBuffer() {
+        ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
+        testMarshall_ByteBuffer(bb);
+    }
+
+    private void testMarshall_ByteBuffer(ByteBuffer bb) {
+        // Ensure that Parcel respects the starting offset by not starting at 0
+        bb.position(1);
+        bb.mark();
+
+        // Parcel test data, then marshall into the ByteBuffer
+        Parcel p1 = Parcel.obtain();
+        p1.writeByteArray(TEST_DATA);
+        p1.marshall(bb);
+        p1.recycle();
+
+        assertTrue(bb.position() > 1);
+        bb.reset();
+
+        // Unmarshall test data into a new Parcel
+        Parcel p2 = Parcel.obtain();
+        bb.reset();
+        p2.unmarshall(bb);
+        assertTrue(bb.position() > 1);
+        p2.setDataPosition(0);
+        byte[] marshalled = p2.marshall();
+
+        bb.reset();
+        for (int i = 0; i < TEST_DATA.length; i++) {
+            assertEquals(bb.get(), marshalled[i]);
+        }
+
+        byte[] testDataCopy = new byte[TEST_DATA.length];
+        p2.setDataPosition(0);
+        p2.readByteArray(testDataCopy);
+        for (int i = 0; i < TEST_DATA.length; i++) {
+            assertEquals(TEST_DATA[i], testDataCopy[i]);
+        }
+
+        // Test that overflowing the buffer throws an exception
+        bb.reset();
+        // Leave certainly not enough room for the test data
+        bb.limit(bb.position() + TEST_DATA.length - 1);
+        assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
+        p2.recycle();
+    }
 }
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 8ac9292..50cd4c0 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.os.CancellationSignal;
@@ -230,7 +230,7 @@
         InsetsController secondController = mock(InsetsController.class);
         mPendingInsetsController.replayAndAttach(secondController);
         verify(mReplayedController).show(eq(systemBars()));
-        verifyZeroInteractions(secondController);
+        verifyNoMoreInteractions(secondController);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index eb482f2e..f811d8e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.MockitoAnnotations.initMocks;
 
 import android.os.Bundle;
@@ -74,7 +74,7 @@
                 MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
         assertEquals("Node got lost along the way", nodeFromConnection, node);
 
-        verifyZeroInteractions(mMockCache);
+        verifyNoMoreInteractions(mMockCache);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 5f89f9c..8bbe81d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
@@ -130,7 +130,7 @@
         mTestableLooper.processAllMessages();
 
         assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
     }
 
     @Test
@@ -151,7 +151,7 @@
         mTestableLooper.processAllMessages();
 
         assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
     }
 
     @Test
@@ -172,7 +172,7 @@
         mTestableLooper.processAllMessages();
 
         assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
     }
 
     @Test
@@ -197,7 +197,7 @@
         session.sendEvent(EVENT);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -227,7 +227,7 @@
         session.sendEvent(EVENT);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNotNull();
         assertThat(session.mEvents).containsExactly(EVENT);
     }
@@ -255,7 +255,7 @@
         session.sendEvent(EVENT);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -272,8 +272,8 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
         assertThat(session.mEvents).containsExactly(EVENT);
     }
 
@@ -289,8 +289,8 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
         assertThat(session.mEvents).containsExactly(EVENT);
     }
 
@@ -307,7 +307,7 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isEmpty();
         assertEventFlushedContentCapture(options);
     }
@@ -325,7 +325,7 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isEmpty();
         assertEventFlushedContentCapture(options);
     }
@@ -339,7 +339,7 @@
         mTestableLooper.processAllMessages();
 
         verify(mMockSystemServerInterface).finishSession(anyInt());
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mDirectServiceInterface).isNull();
         assertThat(session.mContentProtectionEventProcessor).isNull();
     }
@@ -352,8 +352,8 @@
         session.resetSession(/* newState= */ 0);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockSystemServerInterface);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockSystemServerInterface);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mDirectServiceInterface).isNull();
         assertThat(session.mContentProtectionEventProcessor).isNull();
     }
@@ -370,8 +370,8 @@
         notifyContentCaptureEvents(session);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -388,8 +388,8 @@
         notifyContentCaptureEvents(session);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -407,8 +407,8 @@
         notifyContentCaptureEvents(session);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index ba0dbf4..e75452c 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -443,7 +442,7 @@
         mTestLooper.dispatchAll();
         verify(mMockEventBuffer, never()).clear();
         verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
+        verifyNoMoreInteractions(mMockContentCaptureManager);
     }
 
     private void assertLoginDetected() throws Exception {
diff --git a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
index b61d868..3570c2e 100644
--- a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
@@ -33,7 +33,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -159,7 +158,7 @@
         ContentInfo payload =
                 new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
     }
 
     @Test
@@ -180,19 +179,19 @@
         ContentInfo payload =
                 new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
 
         payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
 
         payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
 
         payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
     }
 
     private static class MyInputConnection extends InputConnectionWrapper {
diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
index 381b566..ad68e38 100644
--- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
+++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
@@ -37,7 +37,7 @@
     fun generatesProgress_onStart() {
         val linearTracker = linearTouchTracker()
         linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
-        val event = linearTracker.createStartEvent(null)
+        val event = linearTracker.createStartEvent()
         assertEquals(0f, event.progress, 0f)
     }
 
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 215c162..66524d1 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -695,8 +695,7 @@
                 /* frameTimeMillis = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ false,
-                /* swipeEdge = */ BackEvent.EDGE_LEFT,
-                /* departingAnimationTarget = */ null);
+                /* swipeEdge = */ BackEvent.EDGE_LEFT);
     }
 
     private void verifyImeCallackRegistrations() throws RemoteException {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 8900745..1977ff5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -44,7 +44,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -522,7 +522,7 @@
                 AccessibilityShortcutController.DialogStatus.SHOWN);
         getController().performAccessibilityShortcut();
 
-        verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+        verifyNoMoreInteractions(mAlertDialogBuilder, mAlertDialog);
         verify(mToast).show();
         verify(mAccessibilityManagerService).performAccessibilityShortcut(
                 Display.DEFAULT_DISPLAY, HARDWARE, null);
@@ -615,7 +615,7 @@
                 AccessibilityShortcutController.DialogStatus.SHOWN);
         getController().performAccessibilityShortcut();
 
-        verifyZeroInteractions(mToast);
+        verifyNoMoreInteractions(mToast);
         verify(mAccessibilityManagerService).performAccessibilityShortcut(
                 Display.DEFAULT_DISPLAY, HARDWARE, null);
     }
@@ -632,7 +632,7 @@
                 AccessibilityShortcutController.DialogStatus.SHOWN);
         getController().performAccessibilityShortcut();
 
-        verifyZeroInteractions(mToast);
+        verifyNoMoreInteractions(mToast);
         verify(mAccessibilityManagerService).performAccessibilityShortcut(
                 Display.DEFAULT_DISPLAY, HARDWARE, null);
     }
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 17fe15c..21ef391 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -28,7 +28,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.annotation.EnforcePermission;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
@@ -207,7 +207,7 @@
 
         mService.setSupportedStates(List.of(OTHER_DEVICE_STATE));
         mService.setBaseState(OTHER_DEVICE_STATE);
-        verifyZeroInteractions(callback);
+        verifyNoMoreInteractions(callback);
     }
 
     @Test
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 3b40148..f25ceb1 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -139,4 +139,10 @@
     <install-in-user-type package="com.android.multiuser">
         <install-in user-type="FULL" />
     </install-in-user-type>
+
+    <!-- PrivateSpace App, only install in private profile -->
+    <install-in-user-type package="com.android.privatespace">
+        <install-in user-type="android.os.usertype.profile.PRIVATE" />
+    </install-in-user-type>
+
 </config>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cd5a54c..a4ba2b3 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -755,6 +755,9 @@
         if (b != null) {
             b.setPremultiplied(mRequestPremultiplied);
             b.mDensity = mDensity;
+            if (hasGainmap()) {
+                b.setGainmap(getGainmap().asShared());
+            }
         }
         return b;
     }
@@ -767,7 +770,8 @@
      */
     @NonNull
     public Bitmap asShared() {
-        if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)) {
+        if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)
+                && (!hasGainmap() || getGainmap().asShared() == getGainmap())) {
             return this;
         }
         Bitmap shared = createAshmemBitmap();
@@ -2091,7 +2095,7 @@
      */
     public void setGainmap(@Nullable Gainmap gainmap) {
         checkRecycled("Bitmap is recycled");
-        mGainmap = null;
+        mGainmap = gainmap;
         nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr);
     }
 
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 7fc13db..2417a12 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -161,6 +161,18 @@
     }
 
     /**
+     * @hide
+     */
+    public Gainmap asShared() {
+        final Bitmap sharedContents = mGainmapContents.asShared();
+        if (sharedContents == mGainmapContents) {
+            return this;
+        } else {
+            return new Gainmap(sharedContents, nCreateCopy(mNativePtr));
+        }
+    }
+
+    /**
      * @return Returns the image data of the gainmap represented as a Bitmap. This is represented
      * as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored
      * such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 4daaf9c..225303b 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -103,4 +103,35 @@
 
     </LinearLayout>
 
+    <!-- Menu option to move a bubble to fullscreen; only visible if bubble anything is enabled. -->
+    <LinearLayout
+        android:id="@+id/bubble_manage_menu_fullscreen_container"
+        android:background="@drawable/bubble_manage_menu_row"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:minHeight="@dimen/bubble_menu_item_height"
+        android:gravity="center_vertical"
+        android:paddingStart="@dimen/bubble_menu_padding"
+        android:paddingEnd="@dimen/bubble_menu_padding"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/bubble_manage_menu_fullscreen_icon"
+            android:layout_width="@dimen/bubble_menu_icon_size"
+            android:layout_height="@dimen/bubble_menu_icon_size"
+            android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
+            android:tint="@color/bubbles_icon_tint"/>
+
+        <TextView
+            android:id="@+id/bubble_manage_menu_fullscreen_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:text="@string/bubble_fullscreen_text"
+            android:textColor="@androidprv:color/materialColorOnSurface"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+    </LinearLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index e11babe..bfaa407 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -22,8 +22,8 @@
     android:layout_height="wrap_content"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation"
-    android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation"
+    android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
+    android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
     android:orientation="vertical">
 
     <LinearLayout
@@ -43,8 +43,7 @@
             android:layout_height="@dimen/desktop_mode_handle_menu_icon_radius"
             android:layout_marginStart="10dp"
             android:layout_marginEnd="12dp"
-            android:contentDescription="@string/app_icon_text"
-            android:importantForAccessibility="no"/>
+            android:contentDescription="@string/app_icon_text" />
 
         <com.android.wm.shell.windowdecor.MarqueedTextView
             android:id="@+id/application_name"
@@ -142,7 +141,6 @@
             android:contentDescription="@string/screenshot_text"
             android:text="@string/screenshot_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
-            android:importantForAccessibility="no"
             style="@style/DesktopModeHandleMenuActionButton"/>
 
         <com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -150,7 +148,6 @@
             android:contentDescription="@string/new_window_text"
             android:text="@string/new_window_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
-            android:importantForAccessibility="no"
             style="@style/DesktopModeHandleMenuActionButton"/>
 
         <com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -158,7 +155,6 @@
             android:contentDescription="@string/manage_windows_text"
             android:text="@string/manage_windows_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
-            android:importantForAccessibility="no"
             style="@style/DesktopModeHandleMenuActionButton"/>
 
         <com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -166,7 +162,6 @@
             android:contentDescription="@string/change_aspect_ratio_text"
             android:text="@string/change_aspect_ratio_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
-            android:importantForAccessibility="no"
             style="@style/DesktopModeHandleMenuActionButton"/>
     </LinearLayout>
 
@@ -186,7 +181,6 @@
             android:text="@string/open_in_browser_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
             style="@style/DesktopModeHandleMenuActionButton"
-            android:importantForAccessibility="no"
             android:layout_width="0dp"
             android:layout_weight="1"/>
 
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
index de38e6f..35e7de0 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -22,8 +22,7 @@
     android:layout_height="match_parent"
     android:gravity="start|center_vertical"
     android:paddingHorizontal="16dp"
-    android:clickable="true"
-    android:focusable="true"
+    android:importantForAccessibility="yes"
     android:orientation="horizontal"
     android:background="?android:attr/selectableItemBackground">
 
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9ebbf71..e1bf663 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -291,6 +291,9 @@
     <!-- Corner radius for expanded view drop target -->
     <dimen name="bubble_bar_expanded_view_drop_target_corner">28dp</dimen>
     <dimen name="bubble_bar_expanded_view_drop_target_padding">24dp</dimen>
+    <dimen name="bubble_bar_expanded_view_drop_target_padding_top">60dp</dimen>
+    <dimen name="bubble_bar_expanded_view_drop_target_padding_bottom">24dp</dimen>
+    <dimen name="bubble_bar_expanded_view_drop_target_padding_horizontal">48dp</dimen>
     <!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
     <dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
     <!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
@@ -525,17 +528,21 @@
     <!-- The radius of the Maximize menu shadow. -->
     <dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
 
-    <!-- The width of the handle menu in desktop mode.  -->
-    <dimen name="desktop_mode_handle_menu_width">216dp</dimen>
+    <!-- The width of the handle menu in desktop mode plus the 2dp added for padding to account for
+         pill elevation. -->
+    <dimen name="desktop_mode_handle_menu_width">218dp</dimen>
 
-    <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
-         additional actions pill 208dp, plus 2dp spacing between them plus 4dp top padding.
-         52*3 + 52*4 + (4-1)*2 + 4 = 374 -->
-    <dimen name="desktop_mode_handle_menu_height">374dp</dimen>
+    <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
+         additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
+         plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
+    <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
 
     <!-- The elevation set on the handle menu pills. -->
     <dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
 
+    <!-- The padding added to account for the handle menu's pills' elevation. -->
+    <dimen name="desktop_mode_handle_menu_pill_elevation_padding">2dp</dimen>
+
     <!-- The height of the handle menu's "App Info" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_app_info_pill_height">52dp</dimen>
 
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 637b47a..5f1db83 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -45,6 +45,7 @@
         <item name="android:layout_height">52dp</item>
         <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
         <item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
+        <item name="android:importantForAccessibility">no</item>
     </style>
 
     <style name="DesktopModeHandleMenuActionButtonImage">
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index b87c205..529203f 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -23,6 +23,7 @@
 import android.window.DesktopModeFlags
 import com.android.internal.R
 import com.android.internal.policy.DesktopModeCompatUtils
+import java.util.function.Supplier
 
 /**
  * Class to decide whether to apply app compat policies in desktop mode.
@@ -34,9 +35,11 @@
     private val pkgManager: PackageManager
         get() = context.getPackageManager()
     private val defaultHomePackage: String?
-        get() = pkgManager.getHomeActivities(ArrayList())?.packageName
+        get() = defaultHomePackageSupplier?.get()
+            ?: pkgManager.getHomeActivities(ArrayList())?.packageName
     private val packageInfoCache = mutableMapOf<String, Boolean>()
 
+    var defaultHomePackageSupplier: Supplier<String?>? = null
 
     /**
      * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -46,15 +49,21 @@
      */
     fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
         isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
-            task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+            task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
+            task.userId)
 
-    fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
-        numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+    fun isTopActivityExemptFromDesktopWindowing(
+        packageName: String?,
+        numActivities: Int,
+        isTopActivityNoDisplay: Boolean,
+        isActivityStackTransparent: Boolean,
+        userId: Int
+    ) =
         DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
                 ((isSystemUiTask(packageName) ||
                         isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
                         (isTransparentTask(isActivityStackTransparent, numActivities) &&
-                                hasFullscreenTransparentPermission(packageName))) &&
+                                hasFullscreenTransparentPermission(packageName, userId))) &&
                         !isTopActivityNoDisplay)
 
     /** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
@@ -77,16 +86,17 @@
     private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
 
     // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
-    private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+    private fun hasFullscreenTransparentPermission(packageName: String?, userId: Int): Boolean {
         if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
             if (packageName == null) {
                 return false
             }
-            return packageInfoCache.getOrPut(packageName) {
+            return packageInfoCache.getOrPut("$userId@$packageName") {
                 try {
-                    val packageInfo = pkgManager.getPackageInfo(
+                    val packageInfo = pkgManager.getPackageInfoAsUser(
                         packageName,
-                        PackageManager.GET_PERMISSIONS
+                        PackageManager.GET_PERMISSIONS,
+                        userId
                     )
                     packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
                 } catch (e: PackageManager.NameNotFoundException) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 53dede6..7f8cfae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -448,7 +448,7 @@
         final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
         if (!shouldDispatchToAnimator && mActiveCallback != null) {
             mCurrentTracker.updateStartLocation();
-            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent());
             if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
                 tryPilferPointers();
             }
@@ -604,7 +604,7 @@
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
             // App is handling back animation. Cancel system animation latency tracking.
             cancelLatencyTracking();
-            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent());
             if (!isAppProgressGenerationAllowed()) {
                 tryPilferPointers();
             }
@@ -1041,7 +1041,7 @@
                 () -> mShellExecutor.execute(this::onBackAnimationFinished));
 
         if (mApps.length >= 1) {
-            BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
+            BackMotionEvent startEvent = mCurrentTracker.createStartEvent();
             dispatchOnBackStarted(mActiveCallback, startEvent);
             if (startEvent.getSwipeEdge() == EDGE_NONE) {
                 // TODO(b/373544911): onBackStarted is dispatched here so that
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 8ac9230..cbd1e96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -606,6 +606,10 @@
         updateManageButtonIfExists();
     }
 
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
     /**
      * Updates the size and visuals of the pointer if {@link #mPointerView} is initialized.
      * Does nothing otherwise.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 70340d7..03d6b0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -103,7 +103,9 @@
     private int mManageButtonHeight;
     private int mOverflowHeight;
     private int mMinimumFlyoutWidthLargeScreen;
-    private int mBubbleBarExpandedViewDropTargetPadding;
+    private int mBarExpViewDropTargetPaddingTop;
+    private int mBarExpViewDropTargetPaddingBottom;
+    private int mBarExpViewDropTargetPaddingHorizontal;
 
     private PointF mRestingStackPosition;
 
@@ -173,8 +175,12 @@
                 res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
                 mPositionRect.width() - 2 * mExpandedViewPadding
         );
-        mBubbleBarExpandedViewDropTargetPadding = res.getDimensionPixelSize(
-                R.dimen.bubble_bar_expanded_view_drop_target_padding);
+        mBarExpViewDropTargetPaddingTop = res.getDimensionPixelSize(
+                R.dimen.bubble_bar_expanded_view_drop_target_padding_top);
+        mBarExpViewDropTargetPaddingBottom = res.getDimensionPixelSize(
+                R.dimen.bubble_bar_expanded_view_drop_target_padding_bottom);
+        mBarExpViewDropTargetPaddingHorizontal = res.getDimensionPixelSize(
+                R.dimen.bubble_bar_expanded_view_drop_target_padding_horizontal);
 
         if (mShowingInBubbleBar) {
             mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
@@ -986,8 +992,15 @@
     public Rect getBubbleBarExpandedViewDropTargetBounds(boolean onLeft) {
         Rect bounds = new Rect();
         getBubbleBarExpandedViewBounds(onLeft, false, bounds);
-        bounds.inset(mBubbleBarExpandedViewDropTargetPadding,
-                mBubbleBarExpandedViewDropTargetPadding);
+        // Drop target bounds are based on expanded view bounds with some padding added
+        int leftPadding = onLeft ? 0 : mBarExpViewDropTargetPaddingHorizontal;
+        int rightPadding = onLeft ? mBarExpViewDropTargetPaddingHorizontal : 0;
+        bounds.inset(
+                leftPadding,
+                mBarExpViewDropTargetPaddingTop,
+                rightPadding,
+                mBarExpViewDropTargetPaddingBottom
+        );
         return bounds;
     }
 }
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 9272417..dd5a23a 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
@@ -91,6 +91,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.DeviceConfig;
 import com.android.wm.shell.shared.bubbles.DismissView;
 import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
@@ -1319,7 +1320,7 @@
         mBubbleContainer.bringToFront();
     }
 
-    // TODO: Create ManageMenuView and move setup / animations there
+    // TODO (b/402196554) : Create ManageMenuView and move setup / animations there
     private void setUpManageMenu() {
         if (mManageMenu != null) {
             removeView(mManageMenu);
@@ -1377,6 +1378,22 @@
         mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
         mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
 
+        View fullscreenView = mManageMenu.findViewById(
+                R.id.bubble_manage_menu_fullscreen_container);
+        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+            fullscreenView.setVisibility(VISIBLE);
+            fullscreenView.setOnClickListener(
+                    view -> {
+                        showManageMenu(false /* show */);
+                        BubbleExpandedView expandedView = getExpandedView();
+                        if (expandedView != null && expandedView.getTaskView() != null) {
+                            expandedView.getTaskView().moveToFullscreen();
+                        }
+                    });
+        } else {
+            fullscreenView.setVisibility(GONE);
+        }
+
         // The menu itself should respect locale direction so the icons are on the correct side.
         mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
         addView(mManageMenu);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index 8cd6ce0..c1841c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -612,8 +612,7 @@
             mTaskLeash = taskChg.getLeash();
             mRootLeash = info.getRoot(0).getLeash();
 
-            SurfaceControl dest =
-                    mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl();
+            SurfaceControl dest = getExpandedView(mBubble).getViewRootImpl().getSurfaceControl();
             final Runnable onPlucked = () -> {
                 // Need to remove the taskview AFTER applying the startTransaction because
                 // it isn't synchronized.
@@ -623,12 +622,12 @@
                 mBubbleData.setExpanded(false /* expanded */);
             };
             if (dest != null) {
-                pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest,
+                pluck(mTaskLeash, getExpandedView(mBubble), dest,
                         taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x,
                         taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y,
-                        mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction,
+                        getCornerRadius(mBubble), startTransaction,
                         onPlucked);
-                mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition(
+                getExpandedView(mBubble).post(() -> mTransitions.dispatchTransition(
                         mTransition, info, startTransaction, finishTransaction, finishCallback,
                         null));
             } else {
@@ -649,6 +648,20 @@
             t.reparent(mTaskLeash, mRootLeash);
             t.apply();
         }
+
+        private View getExpandedView(@NonNull Bubble bubble) {
+            if (bubble.getBubbleBarExpandedView() != null) {
+                return bubble.getBubbleBarExpandedView();
+            }
+            return bubble.getExpandedView();
+        }
+
+        private float getCornerRadius(@NonNull Bubble bubble) {
+            if (bubble.getBubbleBarExpandedView() != null) {
+                return bubble.getBubbleBarExpandedView().getCornerRadius();
+            }
+            return bubble.getExpandedView().getCornerRadius();
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index b7761ec..69009fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -28,9 +28,9 @@
 import android.view.ViewGroup;
 
 import com.android.app.animation.Interpolators;
-import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 
 import java.util.ArrayList;
 
@@ -263,7 +263,7 @@
                 }
         ));
 
-        if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
             menuActions.add(new BubbleBarMenuView.MenuAction(
                     Icon.createWithResource(resources,
                             R.drawable.desktop_mode_ic_handle_menu_fullscreen),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index c4696d5..a8e6b59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -20,17 +20,14 @@
 
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
-import android.os.RemoteException;
 import android.util.Slog;
 import android.view.SurfaceControl;
-import android.view.WindowManager;
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
 import android.window.WindowOrganizer;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.transition.LegacyTransitions;
 
 import java.util.ArrayList;
 
@@ -87,25 +84,6 @@
     }
 
     /**
-     * Queues a legacy transition to be sent serially to WM
-     */
-    public void queue(LegacyTransitions.ILegacyTransition transition,
-            @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
-        if (wct.isEmpty()) {
-            if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
-            return;
-        }
-        SyncCallback cb = new SyncCallback(transition, type, wct);
-        synchronized (mQueue) {
-            if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
-            mQueue.add(cb);
-            if (mQueue.size() == 1) {
-                cb.send();
-            }
-        }
-    }
-
-    /**
      * Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
      * Otherwise just returns without queueing.
      * @return {@code true} if queued, {@code false} if not.
@@ -168,17 +146,9 @@
     private class SyncCallback extends WindowContainerTransactionCallback {
         int mId = -1;
         final WindowContainerTransaction mWCT;
-        final LegacyTransitions.LegacyTransition mLegacyTransition;
 
         SyncCallback(WindowContainerTransaction wct) {
             mWCT = wct;
-            mLegacyTransition = null;
-        }
-
-        SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
-                @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
-            mWCT = wct;
-            mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
         }
 
         // Must be sychronized on mQueue
@@ -194,12 +164,7 @@
             }
             if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
             try {
-                if (mLegacyTransition != null) {
-                    mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
-                            mLegacyTransition.getAdapter(), this, mWCT);
-                } else {
-                    mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
-                }
+                mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
             } catch (RuntimeException e) {
                 Slog.e(TAG, "Send failed", e);
                 // Finish current sync callback immediately.
@@ -228,18 +193,10 @@
                     if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
                     mQueue.remove(this);
                     onTransactionReceived(t);
-                    if (mLegacyTransition != null) {
-                        try {
-                            mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
-                        }
-                    } else {
-                        ProtoLog.v(WM_SHELL,
-                                "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
-                        t.apply();
-                        t.close();
-                    }
+                    ProtoLog.v(WM_SHELL,
+                            "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
+                    t.apply();
+                    t.close();
                     if (!mQueue.isEmpty()) {
                         mQueue.get(0).send();
                     }
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 318cdee..f62fd819 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
@@ -95,6 +95,7 @@
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.desktopmode.common.DefaultHomePackageSupplier;
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
@@ -260,8 +261,14 @@
 
     @WMSingleton
     @Provides
-    static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
-        return new DesktopModeCompatPolicy(context);
+    static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(
+            Context context,
+            ShellInit shellInit,
+            @ShellMainThread Handler mainHandler) {
+        final DesktopModeCompatPolicy policy = new DesktopModeCompatPolicy(context);
+        policy.setDefaultHomePackageSupplier(new DefaultHomePackageSupplier(
+                context, shellInit, mainHandler));
+        return policy;
     }
 
     @WMSingleton
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 3dbc151..67a4d6c 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
@@ -825,7 +825,7 @@
                 overviewToDesktopTransitionObserver,
                 desksOrganizer,
                 desksTransitionObserver.get(),
-                desktopPipTransitionObserver.get(),
+                desktopPipTransitionObserver,
                 userProfileContexts,
                 desktopModeCompatPolicy,
                 dragToDisplayTransitionHandler,
@@ -1241,7 +1241,7 @@
                                         transitions,
                                         shellTaskOrganizer,
                                         desktopMixedTransitionHandler.get(),
-                                        desktopPipTransitionObserver.get(),
+                                        desktopPipTransitionObserver,
                                         backAnimationController.get(),
                                         desktopWallpaperActivityTokenProvider,
                                         shellInit)));
@@ -1266,7 +1266,7 @@
     static Optional<DesktopPipTransitionObserver> provideDesktopPipTransitionObserver(
             Context context
     ) {
-        if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)
+        if (DesktopModeStatus.canEnterDesktopMode(context)
                 && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
             return Optional.of(
                     new DesktopPipTransitionObserver());
@@ -1492,6 +1492,7 @@
             ShellTaskOrganizer shellTaskOrganizer,
             DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
             InputManager inputManager,
+            DisplayController displayController,
             @ShellMainThread Handler mainHandler
     ) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
@@ -1506,6 +1507,7 @@
                         shellTaskOrganizer,
                         desktopWallpaperActivityTokenProvider,
                         inputManager,
+                        displayController,
                         mainHandler));
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index 904d862..489e4f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -35,9 +35,11 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.transition.Transitions
 
 /** Controls the display windowing mode in desktop mode */
@@ -49,6 +51,7 @@
     private val shellTaskOrganizer: ShellTaskOrganizer,
     private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
     private val inputManager: InputManager,
+    private val displayController: DisplayController,
     @ShellMainThread private val mainHandler: Handler,
 ) {
 
@@ -111,37 +114,70 @@
         transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
     }
 
-    @VisibleForTesting
-    fun getTargetWindowingModeForDefaultDisplay(): Int {
-        if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
-            return WINDOWING_MODE_FREEFORM
-        }
-        if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
-            if (isInClamshellMode()) {
-                return WINDOWING_MODE_FREEFORM
+    // Do not directly use this method to check the state of desktop-first mode. Check the display
+    // windowing mode instead.
+    private fun canDesktopFirstModeBeEnabledOnDefaultDisplay(): Boolean {
+        if (isDefaultDisplayDesktopEligible()) {
+            if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+                return true
             }
-            return WINDOWING_MODE_FULLSCREEN
+            if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+                if (isInClamshellMode()) {
+                    return true
+                }
+            }
         }
-
-        // If form factor-based desktop first switch is disabled, use the default display windowing
-        // mode here to keep the freeform mode for some form factors (e.g., FEATURE_PC).
-        return windowManager.getWindowingMode(DEFAULT_DISPLAY)
+        return false
     }
 
-    // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
-    private fun isExtendedDisplayEnabled() =
-        0 !=
+    @VisibleForTesting
+    fun getTargetWindowingModeForDefaultDisplay(): Int {
+        if (canDesktopFirstModeBeEnabledOnDefaultDisplay()) {
+            return WINDOWING_MODE_FREEFORM
+        }
+
+        return if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+            WINDOWING_MODE_FULLSCREEN
+        } else {
+            // If form factor-based desktop first switch is disabled, use the default display
+            // windowing mode here to keep the freeform mode for some form factors (e.g.,
+            // FEATURE_PC).
+            windowManager.getWindowingMode(DEFAULT_DISPLAY)
+        }
+    }
+
+    private fun isExtendedDisplayEnabled(): Boolean {
+        if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) {
+            return rootTaskDisplayAreaOrganizer
+                .getDisplayIds()
+                .filter { it != DEFAULT_DISPLAY }
+                .any { displayId ->
+                    displayController.getDisplay(displayId)?.let { display ->
+                        DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+                    } ?: false
+                }
+        }
+
+        return 0 !=
             Settings.Global.getInt(
                 context.contentResolver,
                 DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                 0,
             )
+    }
 
     private fun hasExternalDisplay() =
         rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
 
     private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
 
+    private fun isDefaultDisplayDesktopEligible(): Boolean {
+        val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+            "Display object of DEFAULT_DISPLAY must be non-null."
+        }
+        return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+    }
+
     private fun logV(msg: String, vararg arguments: Any?) {
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 0c2ee46..6cb26b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -332,7 +332,7 @@
         val affectedDisplays = mutableSetOf<Int>()
         desktopData
             .desksSequence()
-            .filter { desk -> desk.displayId != excludedDeskId }
+            .filter { desk -> desk.deskId != excludedDeskId }
             .forEach { desk ->
                 val removed = removeActiveTaskFromDesk(desk.deskId, taskId, notifyListeners = false)
                 if (removed) {
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 1f0774c..50f5beb 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
@@ -32,8 +32,6 @@
 import android.app.WindowConfiguration.WindowingMode
 import android.content.Context
 import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.graphics.Point
 import android.graphics.PointF
 import android.graphics.Rect
@@ -45,6 +43,7 @@
 import android.os.SystemProperties
 import android.os.UserHandle
 import android.util.Slog
+import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.DragEvent
 import android.view.MotionEvent
@@ -215,7 +214,7 @@
     private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
     private val desksOrganizer: DesksOrganizer,
     private val desksTransitionObserver: DesksTransitionObserver,
-    private val desktopPipTransitionObserver: DesktopPipTransitionObserver,
+    private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
     private val userProfileContexts: UserProfileContexts,
     private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
     private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
@@ -466,6 +465,10 @@
 
     /** Creates a new desk in the given display. */
     fun createDesk(displayId: Int) {
+        if (displayId == Display.INVALID_DISPLAY) {
+            logW("createDesk attempt with invalid displayId", displayId)
+            return
+        }
         if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
             desksOrganizer.createDesk(displayId) { deskId ->
                 taskRepository.addDesk(displayId = displayId, deskId = deskId)
@@ -654,6 +657,7 @@
         taskInfo: RunningTaskInfo,
         dragToDesktopValueAnimator: MoveToDesktopAnimator,
         taskSurface: SurfaceControl,
+        dragInterruptedCallback: Runnable,
     ) {
         logV("startDragToDesktop taskId=%d", taskInfo.taskId)
         val jankConfigBuilder =
@@ -669,6 +673,7 @@
             taskInfo,
             dragToDesktopValueAnimator,
             visualIndicator,
+            dragInterruptedCallback,
         )
     }
 
@@ -811,6 +816,7 @@
             }
         val isMinimizingToPip =
             DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
+                desktopPipTransitionObserver.isPresent &&
                 (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
 
         // If task is going to PiP, start a PiP transition instead of a minimize transition
@@ -827,7 +833,7 @@
             val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
             wct.merge(requestRes.second, true)
 
-            desktopPipTransitionObserver.addPendingPipTransition(
+            desktopPipTransitionObserver.get().addPendingPipTransition(
                 DesktopPipTransitionObserver.PendingPipTransition(
                     token = freeformTaskTransitionStarter.startPipTransition(wct),
                     taskId = taskInfo.taskId,
@@ -1262,8 +1268,7 @@
             wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
         }
 
-        // TODO: b/391485148 - pass in the moving-to-desk |task| here to apply task-limit policy.
-        val activationRunnable = addDeskActivationChanges(destinationDeskId, wct)
+        val activationRunnable = addDeskActivationChanges(destinationDeskId, wct, task)
 
         if (Flags.enableDisplayFocusInShellTransitions()) {
             // Bring the destination display to top with includingParents=true, so that the
@@ -2981,6 +2986,11 @@
         removeDesk(displayId = displayId, deskId = deskId)
     }
 
+    /** Removes all the available desks on all displays. */
+    fun removeAllDesks() {
+        taskRepository.getAllDeskIds().forEach { deskId -> removeDesk(deskId) }
+    }
+
     private fun removeDesk(displayId: Int, deskId: Int) {
         if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
         logV("removeDesk deskId=%d from displayId=%d", deskId, displayId)
@@ -3747,6 +3757,18 @@
             }
         }
 
+        override fun removeDesk(deskId: Int) {
+            executeRemoteCallWithTaskPermission(controller, "removeDesk") { c ->
+                c.removeDesk(deskId)
+            }
+        }
+
+        override fun removeAllDesks() {
+            executeRemoteCallWithTaskPermission(controller, "removeAllDesks") { c ->
+                c.removeAllDesks()
+            }
+        }
+
         override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
             executeRemoteCallWithTaskPermission(controller, "activateDesk") { c ->
                 c.activateDesk(deskId, remoteTransition)
@@ -3810,8 +3832,8 @@
             }
         }
 
-        override fun removeDesktop(displayId: Int) {
-            executeRemoteCallWithTaskPermission(controller, "removeDesktop") { c ->
+        override fun removeDefaultDeskInDisplay(displayId: Int) {
+            executeRemoteCallWithTaskPermission(controller, "removeDefaultDeskInDisplay") { c ->
                 c.removeDefaultDeskInDisplay(displayId)
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index c670ac3..df4d18f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -37,9 +37,12 @@
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.TransitionUtil.isClosingMode
+import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
+import java.util.Optional
 
 /**
  * A [Transitions.TransitionObserver] that observes shell transitions and updates the
@@ -52,7 +55,7 @@
     private val transitions: Transitions,
     private val shellTaskOrganizer: ShellTaskOrganizer,
     private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
-    private val desktopPipTransitionObserver: DesktopPipTransitionObserver,
+    private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
     private val backAnimationController: BackAnimationController,
     private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
     shellInit: ShellInit,
@@ -94,7 +97,7 @@
             removeTaskIfNeeded(info)
         }
         removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
-        desktopPipTransitionObserver.onTransitionReady(transition, info)
+        desktopPipTransitionObserver.ifPresent { it.onTransitionReady(transition, info) }
     }
 
     private fun removeTaskIfNeeded(info: TransitionInfo) {
@@ -303,18 +306,29 @@
     }
 
     private fun updateTopTransparentFullscreenTaskId(info: TransitionInfo) {
-        info.changes.forEach { change ->
-            change.taskInfo?.let { task ->
-                val desktopRepository = desktopUserRepositories.getProfile(task.userId)
-                val displayId = task.displayId
-                // Clear `topTransparentFullscreenTask` information from repository if task
-                // is closed or sent to back.
-                if (
-                    TransitionUtil.isClosingMode(change.mode) &&
-                        task.taskId ==
-                            desktopRepository.getTopTransparentFullscreenTaskId(displayId)
-                ) {
-                    desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+        run forEachLoop@{
+            info.changes.forEach { change ->
+                change.taskInfo?.let { task ->
+                    val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+                    val displayId = task.displayId
+                    val transparentTaskId =
+                        desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+                    if (transparentTaskId == null) return@forEachLoop
+                    val changeMode = change.mode
+                    val taskId = task.taskId
+                    val isTopTransparentFullscreenTaskClosing =
+                        taskId == transparentTaskId && isClosingMode(changeMode)
+                    val isNonTopTransparentFullscreenTaskOpening =
+                        taskId != transparentTaskId && isOpeningMode(changeMode)
+                    // Clear `topTransparentFullscreenTask` information from repository if task
+                    // is closed, sent to back or if a different task is opened, brought to front.
+                    if (
+                        isTopTransparentFullscreenTaskClosing ||
+                            isNonTopTransparentFullscreenTaskOpening
+                    ) {
+                        desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+                        return@forEachLoop
+                    }
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 24b2e48..c6f74728 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -2,6 +2,7 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
 import android.animation.RectEvaluator
 import android.animation.ValueAnimator
 import android.app.ActivityManager.RunningTaskInfo
@@ -23,6 +24,7 @@
 import android.os.SystemClock
 import android.os.SystemProperties
 import android.os.UserHandle
+import android.view.Choreographer
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_CLOSE
@@ -48,6 +50,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.animation.Interpolators
 import com.android.wm.shell.shared.animation.PhysicsAnimator
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -122,6 +125,7 @@
         taskInfo: RunningTaskInfo,
         dragToDesktopAnimator: MoveToDesktopAnimator,
         visualIndicator: DesktopModeVisualIndicator?,
+        dragCancelCallback: Runnable,
     ) {
         if (inProgress) {
             logV("Drag to desktop transition already in progress.")
@@ -168,6 +172,7 @@
                     startTransitionToken = startTransitionToken,
                     otherSplitTask = otherTask,
                     visualIndicator = visualIndicator,
+                    dragCancelCallback = dragCancelCallback,
                 )
             } else {
                 TransitionState.FromFullscreen(
@@ -175,6 +180,7 @@
                     dragAnimator = dragToDesktopAnimator,
                     startTransitionToken = startTransitionToken,
                     visualIndicator = visualIndicator,
+                    dragCancelCallback = dragCancelCallback,
                 )
             }
     }
@@ -203,8 +209,9 @@
         }
         if (state.startInterrupted) {
             logV("finishDragToDesktop: start was interrupted, returning")
-            // We should only have interrupted the start transition after receiving a cancel/end
-            // request, let that existing request play out and just return here.
+            // If start was interrupted we've either already requested a cancel/end transition - so
+            // we should let that request play out, or we're cancelling the drag-to-desktop
+            // transition altogether, so just return here.
             return null
         }
         state.endTransitionToken =
@@ -221,6 +228,7 @@
      */
     fun cancelDragToDesktopTransition(cancelState: CancelState) {
         if (!inProgress) {
+            logV("cancelDragToDesktop: not in progress, returning")
             // Don't attempt to cancel a drag to desktop transition since there is no transition in
             // progress which means that the drag to desktop transition was never successfully
             // started.
@@ -228,14 +236,17 @@
         }
         val state = requireTransitionState()
         if (state.startAborted) {
+            logV("cancelDragToDesktop: start was aborted, clearing state")
             // Don't attempt to cancel the drag-to-desktop since the start transition didn't
             // succeed as expected. Just reset the state as if nothing happened.
             clearState()
             return
         }
         if (state.startInterrupted) {
-            // We should only have interrupted the start transition after receiving a cancel/end
-            // request, let that existing request play out and just return here.
+            logV("cancelDragToDesktop: start was interrupted, returning")
+            // If start was interrupted we've either already requested a cancel/end transition - so
+            // we should let that request play out, or we're cancelling the drag-to-desktop
+            // transition altogether, so just return here.
             return
         }
         state.cancelState = cancelState
@@ -706,11 +717,7 @@
         // end-transition, or if the end-transition is running on its own, then just wait until that
         // finishes instead. If we've merged the cancel-transition we've finished the
         // start-transition and won't reach this code.
-        if (
-            mergeTarget == state.startTransitionToken &&
-                isCancelOrEndTransitionRequested(state) &&
-                !state.mergedEndTransition
-        ) {
+        if (mergeTarget == state.startTransitionToken && !state.mergedEndTransition) {
             interruptStartTransition(state)
         }
     }
@@ -722,9 +729,23 @@
         if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue) {
             return
         }
-        logV("interruptStartTransition")
-        state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
-        state.dragAnimator.cancelAnimator()
+        if (isCancelOrEndTransitionRequested(state)) {
+            logV("interruptStartTransition, bookend requested -> finish start transition")
+            // Finish the start-drag transition, we will finish the overall transition properly when
+            // receiving #startAnimation for Cancel/End.
+            state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+            state.dragAnimator.cancelAnimator()
+        } else {
+            logV("interruptStartTransition, bookend not requested -> animate to Home")
+            // Animate to Home, and then finish the start-drag transition. Since there is no other
+            // (end/cancel) transition requested that will be the end of the overall transition.
+            state.dragAnimator.cancelAnimator()
+            state.dragCancelCallback?.run()
+            createInterruptToHomeAnimator(transactionSupplier.get(), state) {
+                state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+                clearState()
+            }
+        }
         state.activeCancelAnimation?.removeAllListeners()
         state.activeCancelAnimation?.cancel()
         state.activeCancelAnimation = null
@@ -738,6 +759,46 @@
             .onActionCancel(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG)
     }
 
+    private fun createInterruptToHomeAnimator(
+        transaction: Transaction,
+        state: TransitionState,
+        endCallback: Runnable,
+    ) {
+        val homeLeash = state.homeChange?.leash ?: error("Expected home leash to be non-null")
+        val draggedTaskLeash =
+            state.draggedTaskChange?.leash ?: error("Expected dragged leash to be non-null")
+        val homeAnimator = createInterruptAlphaAnimator(transaction, homeLeash, toShow = true)
+        val draggedTaskAnimator =
+            createInterruptAlphaAnimator(transaction, draggedTaskLeash, toShow = false)
+        val animatorSet = AnimatorSet()
+        animatorSet.playTogether(homeAnimator, draggedTaskAnimator)
+        animatorSet.addListener(
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    endCallback.run()
+                }
+            }
+        )
+        animatorSet.start()
+    }
+
+    private fun createInterruptAlphaAnimator(
+        transaction: Transaction,
+        leash: SurfaceControl,
+        toShow: Boolean,
+    ) =
+        ValueAnimator.ofFloat(if (toShow) 0f else 1f, if (toShow) 1f else 0f).apply {
+            transaction.show(leash)
+            duration = DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
+            interpolator = Interpolators.LINEAR
+            addUpdateListener { animation ->
+                transaction
+                    .setAlpha(leash, animation.animatedValue as Float)
+                    .setFrameTimeline(Choreographer.getInstance().vsyncId)
+                    .apply()
+            }
+        }
+
     protected open fun setupEndDragToDesktop(
         info: TransitionInfo,
         startTransaction: SurfaceControl.Transaction,
@@ -1060,6 +1121,7 @@
         abstract var endTransitionToken: IBinder?
         abstract var mergedEndTransition: Boolean
         abstract var activeCancelAnimation: Animator?
+        abstract var dragCancelCallback: Runnable?
 
         data class FromFullscreen(
             override val draggedTaskId: Int,
@@ -1079,6 +1141,7 @@
             override var endTransitionToken: IBinder? = null,
             override var mergedEndTransition: Boolean = false,
             override var activeCancelAnimation: Animator? = null,
+            override var dragCancelCallback: Runnable? = null,
             var otherRootChanges: MutableList<Change> = mutableListOf(),
         ) : TransitionState()
 
@@ -1100,6 +1163,7 @@
             override var endTransitionToken: IBinder? = null,
             override var mergedEndTransition: Boolean = false,
             override var activeCancelAnimation: Animator? = null,
+            override var dragCancelCallback: Runnable? = null,
             var splitRootChange: Change? = null,
             var otherSplitTask: Int,
         ) : TransitionState()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 44f7e16..5f7fbd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -35,6 +35,12 @@
     /** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
     oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
 
+    /** Removes the desk with the given `deskId`. */
+    oneway void removeDesk(int deskId);
+
+    /** Removes all the available desks on all displays. */
+    oneway void removeAllDesks();
+
     /** Show apps on the desktop on the given display */
     void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
 
@@ -64,8 +70,11 @@
                         in @nullable RemoteTransition remoteTransition,
                         in @nullable IMoveToDesktopCallback callback);
 
-    /** Remove desktop on the given display */
-    oneway void removeDesktop(int displayId);
+    /**
+     * Removes the default desktop on the given display.
+     * @deprecated with multi-desks, we should use `removeDesk()`.
+     */
+    oneway void removeDefaultDeskInDisplay(int displayId);
 
     /** Move a task with given `taskId` to external display */
     void moveToExternalDisplay(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
new file mode 100644
index 0000000..8ce624e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.desktopmode.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import java.util.function.Supplier
+
+/**
+ * This supplies the package name of default home in an efficient way. The query to package manager
+ * only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ */
+class DefaultHomePackageSupplier(
+    private val context: Context,
+    shellInit: ShellInit,
+    @ShellMainThread private val mainHandler: Handler,
+) : BroadcastReceiver(), Supplier<String?> {
+
+    private var defaultHomePackage: String? = null
+
+    init {
+        shellInit.addInitCallback({ onInit() }, this)
+    }
+
+    private fun onInit() {
+        context.registerReceiver(
+            this,
+            IntentFilter(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED),
+            null /* broadcastPermission */,
+            mainHandler,
+        )
+    }
+
+    private fun updateDefaultHomePackage(): String? {
+        defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+        return defaultHomePackage
+    }
+
+    override fun onReceive(contxt: Context?, intent: Intent?) {
+        updateDefaultHomePackage()
+    }
+
+    override fun get(): String? {
+        return defaultHomePackage ?: updateDefaultHomePackage()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index f89ba0a..0bf2ea6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -174,7 +174,7 @@
             SurfaceControl.Transaction finishT) {
         mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
     }
 
     private void onToFrontTransitionReady(
@@ -184,7 +184,7 @@
         mTaskChangeListener.ifPresent(
                 listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
     }
 
     private void onToBackTransitionReady(
@@ -194,7 +194,7 @@
         mTaskChangeListener.ifPresent(
                 listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 75c09829..a7cba76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -123,6 +123,7 @@
 import android.view.WindowManager;
 import android.widget.Toast;
 import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
 import android.window.DisplayAreaInfo;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
@@ -675,7 +676,8 @@
         if (!enteredSplitSelect) {
             return null;
         }
-        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()
+                && !DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
             mTaskOrganizer.applyTransaction(wct);
             return null;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 938885c..23dfb41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DesktopModeFlags.ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
@@ -50,6 +51,7 @@
 
     private @NonNull final Context mContext;
     private @NonNull final ShellExecutor mMainExecutor;
+    private IBinder mPendingStartDragTransition;
     private Boolean mPendingHomeVisibilityUpdate;
 
     public HomeTransitionObserver(@NonNull Context context,
@@ -63,31 +65,42 @@
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction) {
-        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
-            handleTransitionReadyWithBubbleAnything(info);
-        } else {
-            handleTransitionReady(info);
+        Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info);
+
+        if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
+            // Do not apply at the start of desktop drag as that updates launcher UI visibility.
+            // Store the value and apply with a next transition or when cancelling the
+            // desktop-drag transition.
+            storePendingHomeVisibilityUpdate(transition, homeVisibilityUpdate);
+            return;
+        }
+
+        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+                && info.getType() == TRANSIT_CONVERT_TO_BUBBLE
+                && homeVisibilityUpdate == null) {
+            // We are converting to bubble and we did not get a change to home visibility in this
+            // transition. Apply the value from start of drag.
+            homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
+        }
+
+        if (homeVisibilityUpdate != null) {
+            mPendingHomeVisibilityUpdate = null;
+            mPendingStartDragTransition = null;
+            notifyHomeVisibilityChanged(homeVisibilityUpdate);
         }
     }
 
-    private void handleTransitionReady(@NonNull TransitionInfo info) {
-        for (TransitionInfo.Change change : info.getChanges()) {
-            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-            if (taskInfo == null
-                    || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
-                    || taskInfo.displayId != DEFAULT_DISPLAY
-                    || taskInfo.taskId == -1
-                    || !taskInfo.isRunning) {
-                continue;
-            }
-            Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
-            if (homeVisibilityUpdate != null) {
-                notifyHomeVisibilityChanged(homeVisibilityUpdate);
-            }
+    private void storePendingHomeVisibilityUpdate(
+            IBinder transition, Boolean homeVisibilityUpdate) {
+        if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+                && !ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+            return;
         }
+        mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
+        mPendingStartDragTransition = transition;
     }
 
-    private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
+    private Boolean getHomeVisibilityUpdate(TransitionInfo info) {
         Boolean homeVisibilityUpdate = null;
         for (TransitionInfo.Change change : info.getChanges()) {
             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -97,29 +110,12 @@
                     || !taskInfo.isRunning) {
                 continue;
             }
-
             Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
             if (update != null) {
                 homeVisibilityUpdate = update;
             }
         }
-
-        if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
-            // Do not apply at the start of desktop drag as that updates launcher UI visibility.
-            // Store the value and apply with a next transition if needed.
-            mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
-            return;
-        }
-
-        if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
-            // We are converting to bubble and we did not get a change to home visibility in this
-            // transition. Apply the value from start of drag.
-            homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
-        }
-        if (homeVisibilityUpdate != null) {
-            mPendingHomeVisibilityUpdate = null;
-            notifyHomeVisibilityChanged(homeVisibilityUpdate);
-        }
+        return homeVisibilityUpdate;
     }
 
     private Boolean getHomeVisibilityUpdate(TransitionInfo info,
@@ -146,7 +142,24 @@
 
     @Override
     public void onTransitionFinished(@NonNull IBinder transition,
-            boolean aborted) {}
+            boolean aborted) {
+        if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+            return;
+        }
+        // Handle the case where the DragToDesktop START transition is interrupted and we never
+        // receive a CANCEL/END transition.
+        if (mPendingStartDragTransition == null
+                || mPendingStartDragTransition != transition) {
+            return;
+        }
+        mPendingStartDragTransition = null;
+        if (aborted) return;
+
+        if (mPendingHomeVisibilityUpdate != null) {
+            notifyHomeVisibilityChanged(mPendingHomeVisibilityUpdate);
+            mPendingHomeVisibilityUpdate = null;
+        }
+    }
 
     /**
      * Sets the home transition listener that receives any transitions resulting in a change of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
deleted file mode 100644
index 978b8da..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.transition;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerTransactionCallback;
-
-import com.android.internal.protolog.ProtoLog;
-
-/**
- * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
- * synctransaction tools.
- */
-public class LegacyTransitions {
-
-    /**
-     * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
-     * into one callback.
-     */
-    public interface ILegacyTransition {
-        /**
-         * Called when both the associated sync transaction finishes and the remote animation is
-         * ready.
-         */
-        void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
-                RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-                IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
-    }
-
-    /**
-     * Makes sure that a remote animation and corresponding sync callback are called together
-     * such that the sync callback is called first. This assumes that both the callback receiver
-     * and the remoteanimation are in the same process so that order is preserved on both ends.
-     */
-    public static class LegacyTransition {
-        private final ILegacyTransition mLegacyTransition;
-        private int mSyncId = -1;
-        private SurfaceControl.Transaction mTransaction;
-        private int mTransit;
-        private RemoteAnimationTarget[] mApps;
-        private RemoteAnimationTarget[] mWallpapers;
-        private RemoteAnimationTarget[] mNonApps;
-        private IRemoteAnimationFinishedCallback mFinishCallback = null;
-        private boolean mCancelled = false;
-        private final SyncCallback mSyncCallback = new SyncCallback();
-        private final RemoteAnimationAdapter mAdapter =
-                new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
-
-        public LegacyTransition(@WindowManager.TransitionType int type,
-                @NonNull ILegacyTransition legacyTransition) {
-            mLegacyTransition = legacyTransition;
-            mTransit = type;
-        }
-
-        public @WindowManager.TransitionType int getType() {
-            return mTransit;
-        }
-
-        public IWindowContainerTransactionCallback getSyncCallback() {
-            return mSyncCallback;
-        }
-
-        public RemoteAnimationAdapter getAdapter() {
-            return mAdapter;
-        }
-
-        private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
-            @Override
-            public void onTransactionReady(int id, SurfaceControl.Transaction t)
-                    throws RemoteException {
-                ProtoLog.v(WM_SHELL_TRANSITIONS,
-                        "LegacyTransitions.onTransactionReady(): syncId=%d", id);
-                mSyncId = id;
-                mTransaction = t;
-                checkApply(true /* log */);
-            }
-        }
-
-        private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
-            @Override
-            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
-                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-                    IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
-                mTransit = transit;
-                mApps = apps;
-                mWallpapers = wallpapers;
-                mNonApps = nonApps;
-                mFinishCallback = finishedCallback;
-                checkApply(false /* log */);
-            }
-
-            @Override
-            public void onAnimationCancelled() throws RemoteException {
-                mCancelled = true;
-                mApps = mWallpapers = mNonApps = null;
-                checkApply(false /* log */);
-            }
-        }
-
-
-        private void checkApply(boolean log) throws RemoteException {
-            if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) {
-                if (log) {
-                    ProtoLog.v(WM_SHELL_TRANSITIONS, "\tSkipping hasFinishedCb=%b canceled=%b",
-                            mFinishCallback != null, mCancelled);
-                }
-                return;
-            }
-            if (log) {
-                ProtoLog.v(WM_SHELL_TRANSITIONS, "\tapply");
-            }
-            mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
-                    mNonApps, mFinishCallback, mTransaction);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 7871179..42321e56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -49,6 +49,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.window.DisplayAreaInfo;
+import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -233,7 +234,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode) {
         final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
 
         if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 2b2cdf8..4511fbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -31,6 +31,7 @@
 import android.view.KeyEvent;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -159,7 +160,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode) {
         final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
 
         if (!shouldShowWindowDecor(taskInfo)) {
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 a1d2774..f2ff396 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
@@ -27,6 +27,7 @@
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
 
 import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
 import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
@@ -79,6 +80,7 @@
 import android.view.ViewRootImpl;
 import android.window.DesktopModeFlags;
 import android.window.TaskSnapshot;
+import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -150,18 +152,19 @@
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
 import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 
 import kotlin.Pair;
 import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 
-import org.jetbrains.annotations.NotNull;
-
 import kotlinx.coroutines.CoroutineScope;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 import kotlinx.coroutines.MainCoroutineDispatcher;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -206,6 +209,7 @@
     private final AppToWebEducationController mAppToWebEducationController;
     private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+    private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -331,6 +335,7 @@
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
                 new AppHeaderViewHolder.Factory(),
+                new AppHandleViewHolder.Factory(),
                 rootTaskDisplayAreaOrganizer,
                 new SparseArray<>(),
                 interactionJankMonitor,
@@ -380,6 +385,7 @@
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
             AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+            AppHandleViewHolder.Factory appHandleViewHolderFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
             InteractionJankMonitor interactionJankMonitor,
@@ -422,6 +428,7 @@
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
         mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+        mAppHandleViewHolderFactory = appHandleViewHolderFactory;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mGenericLinksParser = genericLinksParser;
         mInputManager = mContext.getSystemService(InputManager.class);
@@ -486,7 +493,8 @@
                 new DesktopModeOnTaskResizeAnimationListener());
         mDesktopTasksController.setOnTaskRepositionAnimationListener(
                 new DesktopModeOnTaskRepositionAnimationListener());
-        if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
+        if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()
+                || DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
             mRecentsTransitionHandler.addTransitionStateListener(
                     new DesktopModeRecentsTransitionStateListener());
         }
@@ -587,7 +595,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode) {
         final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (!shouldShowWindowDecor(taskInfo)) {
             if (decoration != null) {
@@ -601,8 +610,8 @@
         } else {
             decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
                     false /* shouldSetTaskPositionAndCrop */,
-                    mFocusTransitionObserver.hasGlobalFocus(taskInfo),
-                    mExclusionRegion);
+                    mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion,
+                    /*isMovingToBack= */ changeMode == TRANSIT_TO_BACK);
         }
     }
 
@@ -617,7 +626,7 @@
         decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
                 false /* shouldSetTaskPositionAndCrop */,
                 mFocusTransitionObserver.hasGlobalFocus(taskInfo),
-                mExclusionRegion);
+                mExclusionRegion, /* isMovingToBack= */ false);
     }
 
     @Override
@@ -817,9 +826,6 @@
             return;
         }
         decoration.closeHandleMenu();
-        // When the app enters split-select, the handle will no longer be visible, meaning
-        // we shouldn't receive input for it any longer.
-        decoration.disposeStatusBarInputLayer();
         mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
         mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
                 DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN);
@@ -979,6 +985,7 @@
         private boolean mIsCustomHeaderGesture;
         private boolean mIsResizeGesture;
         private boolean mIsDragging;
+        private boolean mDragInterrupted;
         private boolean mLongClickDisabled;
         private int mDragPointerId = -1;
         private MotionEvent mMotionEvent;
@@ -1216,7 +1223,12 @@
                 View v, MotionEvent e) {
             final int id = v.getId();
             if (id == R.id.caption_handle) {
-                handleCaptionThroughStatusBar(e, decoration);
+                handleCaptionThroughStatusBar(e, decoration,
+                        /* interruptDragCallback= */
+                        () -> {
+                            mDragInterrupted = true;
+                            setIsDragging(decoration, /* isDragging= */ false);
+                        });
                 final boolean wasDragging = mIsDragging;
                 updateDragStatus(decoration, e);
                 final boolean upOrCancel = e.getActionMasked() == ACTION_UP
@@ -1333,11 +1345,14 @@
                 case MotionEvent.ACTION_DOWN:
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
+                    mDragInterrupted = false;
                     setIsDragging(decor, false /* isDragging */);
                     break;
                 }
                 case MotionEvent.ACTION_MOVE: {
-                    setIsDragging(decor, true /* isDragging */);
+                    if (!mDragInterrupted) {
+                        setIsDragging(decor, true /* isDragging */);
+                    }
                     break;
                 }
             }
@@ -1458,7 +1473,8 @@
             if (!mInImmersiveMode && (relevantDecor == null
                     || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
                     || mTransitionDragActive)) {
-                handleCaptionThroughStatusBar(ev, relevantDecor);
+                handleCaptionThroughStatusBar(ev, relevantDecor,
+                        /* interruptDragCallback= */ () -> {});
             }
         }
         handleEventOutsideCaption(ev, relevantDecor);
@@ -1498,7 +1514,7 @@
      * Turn on desktop mode if handle is dragged below status bar.
      */
     private void handleCaptionThroughStatusBar(MotionEvent ev,
-            DesktopModeWindowDecoration relevantDecor) {
+            DesktopModeWindowDecoration relevantDecor, Runnable interruptDragCallback) {
         if (relevantDecor == null) {
             if (ev.getActionMasked() == ACTION_UP) {
                 mMoveToDesktopAnimator = null;
@@ -1599,7 +1615,16 @@
                                     mContext, mDragToDesktopAnimationStartBounds,
                                     relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
                             mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
-                                    mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
+                                    mMoveToDesktopAnimator, relevantDecor.mTaskSurface,
+                                    /* dragInterruptedCallback= */ () -> {
+                                        // Don't call into DesktopTasksController to cancel the
+                                        // transition here - the transition handler already handles
+                                        // that (including removing the visual indicator).
+                                        mTransitionDragActive = false;
+                                        mMoveToDesktopAnimator = null;
+                                        relevantDecor.handleDragInterrupted();
+                                        interruptDragCallback.run();
+                                    });
                         }
                     }
                     if (mMoveToDesktopAnimator != null) {
@@ -1761,6 +1786,7 @@
                         mMainChoreographer,
                         mSyncQueue,
                         mAppHeaderViewHolderFactory,
+                        mAppHandleViewHolderFactory,
                         mRootTaskDisplayAreaOrganizer,
                         mGenericLinksParser,
                         mAssistContentRequester,
@@ -1852,12 +1878,13 @@
         windowDecoration.relayout(taskInfo, startT, finishT,
                 false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
                 mFocusTransitionObserver.hasGlobalFocus(taskInfo),
-                mExclusionRegion);
+                mExclusionRegion, /* isMovingToBack= */ false);
         if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
             incrementEventReceiverTasks(taskInfo.displayId);
         }
     }
 
+    @Nullable
     private RunningTaskInfo getOtherSplitTask(int taskId) {
         @SplitPosition int remainingTaskPosition = mSplitScreenController
                 .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index bd21f8d..bcf9396 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -190,6 +190,7 @@
     private ExclusionRegionListener mExclusionRegionListener;
 
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+    private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
     private final MaximizeMenuFactory mMaximizeMenuFactory;
     private final HandleMenuFactory mHandleMenuFactory;
@@ -212,6 +213,7 @@
     private boolean mIsDragging = false;
     private Runnable mLoadAppInfoRunnable;
     private Runnable mSetAppInfoRunnable;
+    private boolean mIsMovingToBack;
 
     public DesktopModeWindowDecoration(
             Context context,
@@ -231,6 +233,7 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+            AppHandleViewHolder.Factory appHandleViewHolderFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
@@ -242,10 +245,10 @@
         this (context, userContext, displayController, taskResourceLoader, splitScreenController,
                 desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
                 mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
-                appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
-                assistContentRequester, SurfaceControl.Builder::new,
-                SurfaceControl.Transaction::new, WindowContainerTransaction::new,
-                SurfaceControl::new, new WindowManagerWrapper(
+                appHeaderViewHolderFactory, appHandleViewHolderFactory,
+                rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+                SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+                WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
                         context.getSystemService(WindowManager.class)),
                 new SurfaceControlViewHostFactory() {},
                 windowDecorViewHostSupplier,
@@ -273,6 +276,7 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+            AppHandleViewHolder.Factory appHandleViewHolderFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
@@ -302,6 +306,7 @@
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
         mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+        mAppHandleViewHolderFactory = appHandleViewHolderFactory;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mGenericLinksParser = genericLinksParser;
         mAssistContentRequester = assistContentRequester;
@@ -462,7 +467,7 @@
         // causes flickering. See b/270202228.
         final boolean applyTransactionOnDraw = taskInfo.isFreeform();
         relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
-                hasGlobalFocus, displayExclusionRegion);
+                hasGlobalFocus, displayExclusionRegion, mIsMovingToBack);
         if (!applyTransactionOnDraw) {
             t.apply();
         }
@@ -489,7 +494,8 @@
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
-            boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
+            boolean hasGlobalFocus, @NonNull Region displayExclusionRegion,
+            boolean isMovingToBack) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
 
         if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
@@ -512,12 +518,17 @@
 
         final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
                 .isTaskInFullImmersiveState(taskInfo.taskId);
+        mIsMovingToBack = isMovingToBack;
         updateRelayoutParams(mRelayoutParams, mContext, taskInfo, mSplitScreenController,
                 applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
                 mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
                 mIsDragging, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
-                displayExclusionRegion, mIsRecentsTransitionRunning,
-                mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
+                displayExclusionRegion,
+                /* shouldIgnoreCornerRadius= */ mIsRecentsTransitionRunning
+                        && DesktopModeFlags
+                        .ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue(),
+                mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo),
+                mIsRecentsTransitionRunning, mIsMovingToBack);
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -542,7 +553,9 @@
             return;
         }
 
-        if (oldRootView != mResult.mRootView) {
+        if (DesktopModeFlags.SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX.isTrue()
+                ? (oldRootView != mResult.mRootView && taskInfo.isVisibleRequested)
+                : oldRootView != mResult.mRootView) {
             disposeStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
             // Load these only when first creating the view.
@@ -558,29 +571,15 @@
             });
         }
 
-        final Point position = new Point();
-        if (isAppHandle(mWindowDecorViewHolder)) {
-            position.set(determineHandlePosition());
-        }
         if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyCaptionStateChanged();
         }
 
         Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
         if (isAppHandle(mWindowDecorViewHolder)) {
-            mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
-                    mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
-                    isCaptionVisible()
-            ));
+            updateAppHandleViewHolder();
         } else {
-            mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
-                    mTaskInfo,
-                    DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
-                    inFullImmersive,
-                    hasGlobalFocus,
-                    /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
-                            /* animatingTaskResizeOrReposition= */ false)
-            ));
+            updateAppHeaderViewHolder(inFullImmersive, hasGlobalFocus);
         }
         Trace.endSection();
 
@@ -857,9 +856,31 @@
         asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
     }
 
+    /** Update the view holder for app handle. */
+    private void updateAppHandleViewHolder() {
+        if (!isAppHandle(mWindowDecorViewHolder)) return;
+        asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
+                mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
+                mResult.mCaptionHeight, isCaptionVisible()
+        ));
+    }
+
+    /** Update the view holder for app header. */
+    private void updateAppHeaderViewHolder(boolean inFullImmersive, boolean hasGlobalFocus) {
+        if (!isAppHeader(mWindowDecorViewHolder)) return;
+        asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+                mTaskInfo,
+                DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
+                inFullImmersive,
+                hasGlobalFocus,
+                /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+                    /* animatingTaskResizeOrReposition= */ false)
+        ));
+    }
+
     private WindowDecorationViewHolder createViewHolder() {
         if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
-            return new AppHandleViewHolder(
+            return mAppHandleViewHolderFactory.create(
                     mResult.mRootView,
                     mOnCaptionTouchListener,
                     mOnCaptionButtonClickListener,
@@ -886,6 +907,10 @@
         return viewHolder instanceof AppHandleViewHolder;
     }
 
+    private boolean isAppHeader(WindowDecorationViewHolder viewHolder) {
+        return viewHolder instanceof AppHeaderViewHolder;
+    }
+
     @Nullable
     private AppHandleViewHolder asAppHandle(WindowDecorationViewHolder viewHolder) {
         if (viewHolder instanceof AppHandleViewHolder) {
@@ -918,7 +943,9 @@
             boolean hasGlobalFocus,
             @NonNull Region displayExclusionRegion,
             boolean shouldIgnoreCornerRadius,
-            boolean shouldExcludeCaptionFromAppBounds) {
+            boolean shouldExcludeCaptionFromAppBounds,
+            boolean isRecentsTransitionRunning,
+            boolean isMovingToBack) {
         final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
         final boolean isAppHeader =
                 captionLayoutId == R.layout.desktop_mode_app_header;
@@ -935,11 +962,20 @@
         // the first frame.
         relayoutParams.mAsyncViewHost = isAppHandle;
 
-        final boolean showCaption;
-        if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
+        boolean showCaption;
+        // If this relayout is occurring from an observed TRANSIT_TO_BACK transition, do not
+        // show caption (this includes split select transition).
+        if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+                && isMovingToBack && !isDragging) {
+            showCaption = false;
+        } else if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
             // If the task is being dragged, the caption should not be hidden so that it continues
             // receiving input
             showCaption = true;
+        } else if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+                && isRecentsTransitionRunning) {
+            // Caption should not be visible in recents.
+            showCaption = false;
         } else if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
             if (inFullImmersiveMode) {
                 showCaption = (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -1683,6 +1719,17 @@
         }
     }
 
+    /**
+     * Indicates that an app handle drag has been interrupted, this can happen e.g. if we receive an
+     * unknown transition during the drag-to-desktop transition.
+     */
+    void handleDragInterrupted() {
+        if (mResult.mRootView == null) return;
+        final View handle = mResult.mRootView.findViewById(R.id.caption_handle);
+        handle.setHovered(false);
+        handle.setPressed(false);
+    }
+
     private boolean pointInView(View v, float x, float y) {
         return v != null && v.getLeft() <= x && v.getRight() >= x
                 && v.getTop() <= y && v.getBottom() >= y;
@@ -1800,9 +1847,18 @@
      * <p> When a Recents transition is active we allow that transition to take ownership of the
      * corner radius of its task surfaces, so each window decoration should stop updating the corner
      * radius of its task surface during that time.
+     *
+     * We should not allow input to reach the input layer during a Recents transition, so
+     * update the handle view holder accordingly if transition status changes.
      */
     void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
-        mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+        if (mIsRecentsTransitionRunning != isRecentsTransitionRunning) {
+            mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+            if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+                // We don't relayout decor on recents transition, so we need to call it directly.
+                relayout(mTaskInfo, mHasGlobalFocus, mRelayoutParams.mDisplayExclusionRegion);
+            }
+        }
     }
 
     /**
@@ -1867,6 +1923,7 @@
                 Choreographer choreographer,
                 SyncTransactionQueue syncQueue,
                 AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+                AppHandleViewHolder.Factory appHandleViewHolderFactory,
                 RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
                 AppToWebGenericLinksParser genericLinksParser,
                 AssistContentRequester assistContentRequester,
@@ -1894,6 +1951,7 @@
                     choreographer,
                     syncQueue,
                     appHeaderViewHolderFactory,
+                    appHandleViewHolderFactory,
                     rootTaskDisplayAreaOrganizer,
                     genericLinksParser,
                     assistContentRequester,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 7a4a834..0b86d1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -23,12 +23,12 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
-import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
 
@@ -127,7 +127,9 @@
             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             DisplayController displayController,
-            DesktopModeEventLogger desktopModeEventLogger) {
+            DesktopModeEventLogger desktopModeEventLogger,
+            InputChannel inputChannel,
+            InputChannel sinkInputChannel) {
         mContext = context;
         mWindowSession = windowSession;
         mBgExecutor = bgExecutor;
@@ -136,7 +138,11 @@
         mHandler = handler;
         mChoreographer = choreographer;
         mDisplayId = displayId;
-        mDecorationSurface = decorationSurface;
+        // Creates a new SurfaceControl pointing the same underlying surface with decorationSurface
+        // to ensure that mDecorationSurface will not be released while it's used on the background
+        // thread. Note that the empty name will be overridden by the next copyFrom call.
+        mDecorationSurface = surfaceControlBuilderSupplier.get().setName("").build();
+        mDecorationSurface.copyFrom(decorationSurface, "DragResizeInputListener");
         mDragPositioningCallback = callback;
         mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
@@ -154,9 +160,13 @@
             final InputSetUpResult result = setUpInputChannels(mDisplayId, mWindowSession,
                     mDecorationSurface, mClientToken, mSinkClientToken,
                     mSurfaceControlBuilderSupplier,
-                    mSurfaceControlTransactionSupplier);
+                    mSurfaceControlTransactionSupplier, inputChannel, sinkInputChannel);
             mainExecutor.execute(() -> {
                 if (mClosed) {
+                    result.mInputChannel.dispose();
+                    result.mSinkInputChannel.dispose();
+                    mSurfaceControlTransactionSupplier.get().remove(
+                            result.mInputSinkSurface).apply();
                     return;
                 }
                 mInputSinkSurface = result.mInputSinkSurface;
@@ -208,7 +218,7 @@
                 new DefaultTaskResizeInputEventReceiverFactory(), taskInfo,
                 handler, choreographer, displayId, decorationSurface, callback,
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
-                displayController, desktopModeEventLogger);
+                displayController, desktopModeEventLogger, new InputChannel(), new InputChannel());
     }
 
     DragResizeInputListener(
@@ -251,11 +261,11 @@
             @NonNull IBinder clientToken,
             @NonNull IBinder sinkClientToken,
             @NonNull Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
-            @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+            @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+            @NonNull InputChannel inputChannel,
+            @NonNull InputChannel sinkInputChannel) {
         Trace.beginSection("DragResizeInputListener#setUpInputChannels");
         final InputTransferToken inputTransferToken = new InputTransferToken();
-        final InputChannel inputChannel = new InputChannel();
-        final InputChannel sinkInputChannel = new InputChannel();
         try {
             windowSession.grantInputChannel(
                     displayId,
@@ -421,6 +431,9 @@
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
+            // Removing this surface on the background thread to ensure that mInitInputChannels has
+            // already been finished.
+            mSurfaceControlTransactionSupplier.get().remove(mDecorationSurface).apply();
         });
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 2d6f745..732f042 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -173,6 +173,9 @@
         return new Rect(mRepositionTaskBounds);
     }
 
+    @Override
+    public void close() {}
+
     private boolean isResizing() {
         return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index c544468..9cc64ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -108,19 +108,19 @@
     private val isViewAboveStatusBar: Boolean
         get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform)
 
-    private val pillElevation: Int = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_pill_elevation)
     private val pillTopMargin: Int = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
-    private val menuWidth = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_width) + pillElevation
+        R.dimen.desktop_mode_handle_menu_pill_spacing_margin
+    )
+    private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_width)
     private val menuHeight = getHandleMenuHeight()
     private val marginMenuTop = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_margin_top)
     private val marginMenuStart = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_margin_start)
+        R.dimen.desktop_mode_handle_menu_margin_start
+    )
 
     @VisibleForTesting
     var handleMenuViewContainer: AdditionalViewContainer? = null
+
     @VisibleForTesting
     var handleMenuView: HandleMenuView? = null
 
@@ -139,7 +139,7 @@
 
     private val shouldShowMoreActionsPill: Boolean
         get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
-            shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+                shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
 
     private var loadAppInfoJob: Job? = null
 
@@ -243,7 +243,8 @@
         val y = handleMenuPosition.y.toInt()
         handleMenuViewContainer =
             if ((!taskInfo.isFreeform && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue())
-                    || forceShowSystemBars) {
+                || forceShowSystemBars
+            ) {
                 AdditionalSystemViewContainer(
                     windowManagerWrapper = windowManagerWrapper,
                     taskId = taskInfo.taskId,
@@ -254,7 +255,11 @@
                     flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                             WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                     view = handleMenuView.rootView,
-                    forciblyShownTypes = if (forceShowSystemBars) { systemBars() } else { 0 },
+                    forciblyShownTypes = if (forceShowSystemBars) {
+                        systemBars()
+                    } else {
+                        0
+                    },
                     ignoreCutouts = Flags.showAppHandleLargeScreens()
                             || BubbleAnythingFlagHelper.enableBubbleToFullscreen()
                 )
@@ -372,7 +377,8 @@
                 inputPoint.y - globalMenuPosition.y
             )
             if (splitScreenController.getSplitPosition(taskInfo.taskId)
-                == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+                == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+            ) {
                 val leftStageBounds = Rect()
                 splitScreenController.getStageBounds(leftStageBounds, Rect())
                 inputRelativeToMenu.x += leftStageBounds.width().toFloat()
@@ -398,11 +404,11 @@
      * Determines handle menu height based the max size and the visibility of pills.
      */
     private fun getHandleMenuHeight(): Int {
-        var menuHeight = loadDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_height) + pillElevation
+        var menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_height)
         if (!shouldShowWindowingPill) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_windowing_pill_height)
+                R.dimen.desktop_mode_handle_menu_windowing_pill_height
+            )
             menuHeight -= pillTopMargin
         }
         if (!SHOULD_SHOW_SCREENSHOT_BUTTON) {
@@ -422,14 +428,16 @@
         }
         if (!shouldShowChangeAspectRatioButton) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height)
+                R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
+            )
         }
         if (!shouldShowMoreActionsPill) {
             menuHeight -= pillTopMargin
         }
         if (!shouldShowBrowserPill) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height)
+                R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height
+            )
             menuHeight -= pillTopMargin
         }
         return menuHeight
@@ -472,48 +480,66 @@
 
         // Insets for ripple effect of App Info Pill. and Windowing Pill. buttons
         val iconButtondrawableShiftInset = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift)
+            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift
+        )
         val iconButtondrawableBaseInset = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base)
+            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base
+        )
         private val iconButtonRippleRadius = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius)
-        private val iconButtonDrawableInsetsBase = DrawableInsets(t = iconButtondrawableBaseInset,
+            R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius
+        )
+        private val iconButtonDrawableInsetsBase = DrawableInsets(
+            t = iconButtondrawableBaseInset,
             b = iconButtondrawableBaseInset, l = iconButtondrawableBaseInset,
-            r = iconButtondrawableBaseInset)
-        private val iconButtonDrawableInsetsLeft = DrawableInsets(t = iconButtondrawableBaseInset,
-            b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0)
-        private val iconButtonDrawableInsetsRight = DrawableInsets(t = iconButtondrawableBaseInset,
-            b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset)
+            r = iconButtondrawableBaseInset
+        )
+        private val iconButtonDrawableInsetsLeft = DrawableInsets(
+            t = iconButtondrawableBaseInset,
+            b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0
+        )
+        private val iconButtonDrawableInsetsRight = DrawableInsets(
+            t = iconButtondrawableBaseInset,
+            b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset
+        )
 
         // App Info Pill.
         private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
         private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
-            R.id.collapse_menu_button)
+            R.id.collapse_menu_button
+        )
+
         @VisibleForTesting
         val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+
         @VisibleForTesting
         val appNameView = appInfoPill.requireViewById<MarqueedTextView>(R.id.application_name)
 
         // Windowing Pill.
         private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
         private val fullscreenBtn = windowingPill.requireViewById<ImageButton>(
-            R.id.fullscreen_button)
+            R.id.fullscreen_button
+        )
         private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
-            R.id.split_screen_button)
+            R.id.split_screen_button
+        )
         private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
         private val floatingBtnSpace = windowingPill.requireViewById<Space>(
-            R.id.floating_button_space)
+            R.id.floating_button_space
+        )
 
         private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
         private val desktopBtnSpace = windowingPill.requireViewById<Space>(
-            R.id.desktop_button_space)
+            R.id.desktop_button_space
+        )
 
         // More Actions Pill.
         private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
         private val screenshotBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
-            R.id.screenshot_button)
+            R.id.screenshot_button
+        )
         private val newWindowBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
-            R.id.new_window_button)
+            R.id.new_window_button
+        )
         private val manageWindowBtn = moreActionsPill
             .requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
         private val changeAspectRatioBtn = moreActionsPill
@@ -521,11 +547,14 @@
 
         // Open in Browser/App Pill.
         private val openInAppOrBrowserPill = rootView.requireViewById<View>(
-            R.id.open_in_app_or_browser_pill)
+            R.id.open_in_app_or_browser_pill
+        )
         private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<View>(
-            R.id.open_in_app_or_browser_button)
+            R.id.open_in_app_or_browser_button
+        )
         private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>(
-            R.id.open_by_default_button)
+            R.id.open_by_default_button
+        )
         private val decorThemeUtil = DecorThemeUtil(context)
         private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat())
 
@@ -734,9 +763,9 @@
             desktopBtn.imageTintList = style.windowingButtonColor
 
             val startInsets = if (context.isRtl) iconButtonDrawableInsetsRight
-                else iconButtonDrawableInsetsLeft
+            else iconButtonDrawableInsetsLeft
             val endInsets = if (context.isRtl) iconButtonDrawableInsetsLeft
-                else iconButtonDrawableInsetsRight
+            else iconButtonDrawableInsetsRight
 
             fullscreenBtn.apply {
                 background = createBackgroundDrawable(
@@ -808,9 +837,11 @@
                 getString(R.string.open_in_browser_text)
             }
 
+            val buttonRoot = openInAppOrBrowserBtn.requireViewById<LinearLayout>(R.id.action_button)
             val label = openInAppOrBrowserBtn.requireViewById<MarqueedTextView>(R.id.label)
             val image = openInAppOrBrowserBtn.requireViewById<ImageView>(R.id.image)
             openInAppOrBrowserBtn.contentDescription = btnText
+            buttonRoot.contentDescription = btnText
             label.apply {
                 text = btnText
                 setTextColor(style.textColor)
@@ -841,7 +872,7 @@
          */
         fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
             taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
-                taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+                    taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
     }
 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
index 4b2e473..a723a7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -23,9 +23,7 @@
 import android.view.LayoutInflater
 import android.widget.ImageView
 import android.widget.LinearLayout
-import android.widget.TextView
 import androidx.core.content.withStyledAttributes
-import androidx.core.view.isGone
 import com.android.wm.shell.R
 
 /**
@@ -54,6 +52,7 @@
 
         context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
             textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
+            rootElement.contentDescription = getString(R.styleable.HandleMenuActionButton_android_text)
             textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
             iconView.setImageResource(getResourceId(
                 R.styleable.HandleMenuActionButton_android_src, 0))
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index 1b0e0f70..eb324f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -294,6 +294,10 @@
         return Rect(repositionTaskBounds)
     }
 
+    override fun close() {
+        displayController.removeDisplayWindowListener(this)
+    }
+
     private fun resetVeilIfVisible() {
         if (isResizingOrAnimatingResize) {
             desktopWindowDecoration.hideResizeVeil()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index 63b288d..06e5380 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -41,4 +41,10 @@
      */
     void removeDragEventListener(
             DragPositioningCallbackUtility.DragEventListener dragEventListener);
+
+    /**
+     * Releases any resources associated with this TaskDragResizer. This should be called when the
+     * associated window is closed.
+     */
+    void close();
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index d2c79d7..7e941ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -205,6 +205,9 @@
         return new Rect(mRepositionTaskBounds);
     }
 
+    @Override
+    public void close() {}
+
     private boolean isResizing() {
         return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 1563259..5e4a0a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.view.SurfaceControl;
+import android.window.TransitionInfo;
 
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -83,12 +84,14 @@
      * @param taskSurface the surface of the task
      * @param startT      the start transaction to be applied before the transition
      * @param finishT     the finish transaction to restore states after the transition
+     * @param changeMode  the type of change to the task
      */
     void onTaskChanging(
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT);
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode);
 
     /**
      * Notifies that the given task is about to close to give the window decoration a chance to
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 bde46a1..91a899c 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
@@ -699,6 +699,9 @@
     public void close() {
         Trace.beginSection("WindowDecoration#close");
         mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
+        if (mTaskDragResizer != null) {
+            mTaskDragResizer.close();
+        }
         final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
         releaseViews(wct);
         mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 9c55f0e..7c5f34f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -152,6 +152,8 @@
                     endBounds = destinationBounds,
                     callback,
                 )
+            } else {
+                callback.invoke()
             }
         }
         return isTiled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index d9df899f..0985587 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -49,7 +49,7 @@
  * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
  * It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
  */
-internal class AppHandleViewHolder(
+class AppHandleViewHolder(
     rootView: View,
     onCaptionTouchListener: View.OnTouchListener,
     onCaptionButtonClickListener: OnClickListener,
@@ -66,7 +66,7 @@
         val position: Point,
         val width: Int,
         val height: Int,
-        val isCaptionVisible: Boolean
+        val showInputLayer: Boolean
     ) : Data()
 
     private lateinit var taskInfo: RunningTaskInfo
@@ -101,7 +101,7 @@
     }
 
     override fun bindData(data: HandleData) {
-        bindData(data.taskInfo, data.position, data.width, data.height, data.isCaptionVisible)
+        bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
     }
 
     private fun bindData(
@@ -109,14 +109,13 @@
         position: Point,
         width: Int,
         height: Int,
-        isCaptionVisible: Boolean
+        showInputLayer: Boolean
     ) {
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
         this.taskInfo = taskInfo
         // If handle is not in status bar region(i.e., bottom stage in vertical split),
         // do not create an input layer
-        if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
-        if (!isCaptionVisible) {
+        if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) {
             disposeStatusBarInputLayer()
             return
         }
@@ -276,4 +275,25 @@
     }
 
     override fun close() {}
+
+    /** Factory class for creating [AppHandleViewHolder] objects. */
+    class Factory {
+        /**
+         * Create a [AppHandleViewHolder] object to handle caption view and status bar
+         * input layer logic.
+         */
+        fun create(
+            rootView: View,
+            onCaptionTouchListener: View.OnTouchListener,
+            onCaptionButtonClickListener: OnClickListener,
+            windowManagerWrapper: WindowManagerWrapper,
+            handler: Handler,
+        ): AppHandleViewHolder = AppHandleViewHolder(
+            rootView,
+            onCaptionTouchListener,
+            onCaptionButtonClickListener,
+            windowManagerWrapper,
+            handler,
+        )
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
new file mode 100644
index 0000000..68f7ef0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A TestRule to manage multiple simulated connected overlay displays.
+ */
+class SimulatedConnectedDisplayTestRule : TestRule {
+
+    private val context = InstrumentationRegistry.getInstrumentation().targetContext
+    private val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+    private val displayManager = context.getSystemService(DisplayManager::class.java)
+    private val addedDisplays = mutableListOf<Int>()
+
+    override fun apply(base: Statement, description: Description): Statement =
+        object : Statement() {
+            override fun evaluate() {
+                try {
+                    base.evaluate()
+                } finally {
+                    teardown()
+                }
+            }
+        }
+
+    private fun teardown() {
+        cleanupTestDisplays()
+    }
+
+    /**
+     * Adds multiple overlay displays with specified dimensions. Any existing overlay displays
+     * will be removed before adding the new ones.
+     *
+     * @param displays A list of [Point] objects, where each [Point] represents the
+     *                 width and height of a simulated display.
+     * @return List of displayIds of added displays.
+     */
+    fun setupTestDisplays(displays: List<Point>): List<Int> = runBlocking {
+        // Cleanup any existing overlay displays.
+        cleanupTestDisplays()
+
+        if (displays.isEmpty()) {
+            Log.w(TAG, "setupTestDisplays called with an empty list. No displays created.")
+            return@runBlocking emptyList()
+        }
+
+        val displayAddedFlow: Flow<Int> = callbackFlow {
+            val listener = object : DisplayListener {
+                override fun onDisplayAdded(displayId: Int) {
+                    trySend(displayId)
+                }
+
+                override fun onDisplayRemoved(displayId: Int) {}
+                override fun onDisplayChanged(displayId: Int) {}
+            }
+
+            val handler = Handler(Looper.getMainLooper())
+            displayManager.registerDisplayListener(listener, handler)
+
+            awaitClose {
+                displayManager.unregisterDisplayListener(listener)
+            }
+        }
+
+        val displaySettings = displays.joinToString(separator = ";") { size ->
+            "${size.x}x${size.y}/$DEFAULT_DENSITY"
+        }
+
+        // Add the overlay displays
+        Settings.Global.putString(
+            InstrumentationRegistry.getInstrumentation().context.contentResolver,
+            Settings.Global.OVERLAY_DISPLAY_DEVICES, displaySettings
+        )
+        withTimeoutOrNull(TIMEOUT) {
+            displayAddedFlow.take(displays.size).collect { displayId ->
+                addedDisplays.add(displayId)
+            }
+        } ?: error("Timed out waiting for displays to be added.")
+        addedDisplays
+    }
+
+    /**
+     * Adds multiple overlay displays with default dimensions. Any existing overlay displays
+     * will be removed before adding the new ones.
+     *
+     * @param count number of displays to add.
+     * @return List of displayIds of added displays.
+     */
+    fun setupTestDisplays(count: Int): List<Int> {
+        val displays = List(count) { Point(DEFAULT_WIDTH, DEFAULT_HEIGHT) }
+        return setupTestDisplays(displays)
+    }
+
+    private fun cleanupTestDisplays() = runBlocking {
+        if (addedDisplays.isEmpty()) {
+            return@runBlocking
+        }
+
+        val displayRemovedFlow: Flow<Int> = callbackFlow {
+            val listener = object : DisplayListener {
+                override fun onDisplayAdded(displayId: Int) {}
+                override fun onDisplayRemoved(displayId: Int) {
+                    trySend(displayId)
+                }
+
+                override fun onDisplayChanged(displayId: Int) {}
+            }
+            val handler = Handler(Looper.getMainLooper())
+            displayManager.registerDisplayListener(listener, handler)
+
+            awaitClose {
+                displayManager.unregisterDisplayListener(listener)
+            }
+        }
+
+        // Remove overlay displays
+        Settings.Global.putString(
+            InstrumentationRegistry.getInstrumentation().context.contentResolver,
+            Settings.Global.OVERLAY_DISPLAY_DEVICES, null)
+
+        withTimeoutOrNull(TIMEOUT) {
+            displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
+                addedDisplays.remove(displayId)
+            }
+        } ?: error("Timed out waiting for displays to be removed.")
+    }
+
+    private companion object {
+        const val DEFAULT_WIDTH = 1280
+        const val DEFAULT_HEIGHT = 720
+        const val DEFAULT_DENSITY = 160
+        const val TAG = "SimulatedConnectedDisplayTestRule"
+        val TIMEOUT = 10.seconds
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 05750a5..b139c00 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -889,8 +889,8 @@
      */
     private void doStartEvents(int startX, int moveX) {
         doMotionEvent(MotionEvent.ACTION_DOWN, startX);
-        mController.onThresholdCrossed();
         doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+        mController.onThresholdCrossed();
     }
 
     private void simulateRemoteAnimationStart() throws RemoteException {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 2ef6c55..43bcc3b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -58,8 +58,7 @@
                 /* frameTime = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ false,
-                /* swipeEdge = */ BackEvent.EDGE_LEFT,
-                /* departingAnimationTarget = */ null);
+                /* swipeEdge = */ BackEvent.EDGE_LEFT);
     }
 
     @Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 2cc52c5..9d4cc49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -224,8 +224,7 @@
             /* frameTime = */ 0,
             /* progress = */ progress,
             /* triggerBack = */ false,
-            /* swipeEdge = */ BackEvent.EDGE_LEFT,
-            /* departingAnimationTarget = */ null
+            /* swipeEdge = */ BackEvent.EDGE_LEFT
         )
 
     private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 7a7d88b..b3d2db6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
@@ -804,7 +803,7 @@
         mBubbleData.setListener(mListener);
 
         changeExpandedStateAtTime(true, 2000L);
-        verifyZeroInteractions(mListener);
+        verifyNoMoreInteractions(mListener);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 25f17fe..b136bed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -311,4 +311,39 @@
         verify(startT).apply();
         assertFalse(mTaskViewTransitions.hasPending());
     }
+
+    @Test
+    public void convertFloatingBubbleToFullscreen() {
+        final BubbleExpandedView bev = mock(BubbleExpandedView.class);
+        final ViewRootImpl vri = mock(ViewRootImpl.class);
+        when(bev.getViewRootImpl()).thenReturn(vri);
+        when(mBubble.getBubbleBarExpandedView()).thenReturn(null);
+        when(mBubble.getExpandedView()).thenReturn(bev);
+
+        ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+        final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertFromBubble(
+                mBubble, taskInfo);
+        final BubbleTransitions.ConvertFromBubble cfb = (BubbleTransitions.ConvertFromBubble) bt;
+        verify(mTransitions).startTransition(anyInt(), any(), eq(cfb));
+        verify(mBubble).setPreparingTransition(eq(bt));
+        assertTrue(mTaskViewTransitions.hasPending());
+
+        final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+                mock(SurfaceControl.class));
+        chg.setMode(TRANSIT_CHANGE);
+        chg.setTaskInfo(taskInfo);
+        info.addChange(chg);
+        info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+        SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        Transitions.TransitionFinishCallback finishCb = wct -> {};
+        cfb.startAnimation(cfb.mTransition, info, startT, finishT, finishCb);
+
+        // Can really only verify that it interfaces with the taskViewTransitions queue.
+        // The actual functioning of this is tightly-coupled with SurfaceFlinger and renderthread
+        // in order to properly synchronize surface manipulation with drawing and thus can't be
+        // directly tested.
+        assertFalse(mTaskViewTransitions.hasPending());
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index c4b9c9b..b4b9679 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.ArgumentMatchers.floatThat;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
@@ -84,7 +83,7 @@
         verify(mMotionEventListener).onMove(0, -600);
         // Check that velocity up is about 5000
         verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000));
-        verifyZeroInteractions(mMotionEventListener);
+        verifyNoMoreInteractions(mMotionEventListener);
         verify(mInterceptTouchRunnable).run();
     }
 
@@ -94,8 +93,8 @@
         mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100));
         mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100));
 
-        verifyZeroInteractions(mMotionEventListener);
-        verifyZeroInteractions(mInterceptTouchRunnable);
+        verifyNoMoreInteractions(mMotionEventListener);
+        verifyNoMoreInteractions(mInterceptTouchRunnable);
     }
 
     @Test
@@ -107,7 +106,7 @@
         verify(mMotionEventListener).onDown(0, 990);
         verify(mMotionEventListener).onMove(100, 0);
         verify(mMotionEventListener).onUp(0, 0);
-        verifyZeroInteractions(mMotionEventListener);
+        verifyNoMoreInteractions(mMotionEventListener);
         verify(mInterceptTouchRunnable).run();
     }
 
@@ -119,7 +118,7 @@
 
         verify(mMotionEventListener).onDown(0, 990);
         verifyNoMoreInteractions(mMotionEventListener);
-        verifyZeroInteractions(mInterceptTouchRunnable);
+        verifyNoMoreInteractions(mInterceptTouchRunnable);
     }
 
     @Test
@@ -129,7 +128,7 @@
         verify(mMotionEventListener).onDown(0, 990);
         verify(mMotionEventListener).onCancel();
         verifyNoMoreInteractions(mMotionEventListener);
-        verifyZeroInteractions(mInterceptTouchRunnable);
+        verifyNoMoreInteractions(mInterceptTouchRunnable);
     }
 
     private MotionEvent newEvent(int actionDown, float x, float y) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index 3323740..1472464 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.content.Context;
 
@@ -105,6 +105,6 @@
         int sameDevicePosture = mDevicePostureCaptor.getValue();
         mDevicePostureController.onDevicePostureChanged(sameDevicePosture);
 
-        verifyZeroInteractions(mOnDevicePostureChangedListener);
+        verifyNoMoreInteractions(mOnDevicePostureChangedListener);
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ee9d177..a53277a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -32,7 +32,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -108,26 +108,26 @@
     public void insetsControlChanged_schedulesNoWorkOnExecutor() {
         Looper.prepare();
         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     @Test
     public void insetsChanged_schedulesNoWorkOnExecutor() {
         Looper.prepare();
         mPerDisplay.insetsChanged(insetsStateWithIme(false));
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     @Test
     public void showInsets_schedulesNoWorkOnExecutor() {
         mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     @Test
     public void hideInsets_schedulesNoWorkOnExecutor() {
         mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     // With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore
@@ -135,7 +135,7 @@
     @Test
     @RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void reappliesVisibilityToChangedLeash() {
-        verifyZeroInteractions(mT);
+        verifyNoMoreInteractions(mT);
         mPerDisplay.mImeShowing = false;
 
         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
index 96d202c..7d18669 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -145,7 +145,7 @@
         mConfiguration.windowConfiguration.setDisplayRotation(Surface.ROTATION_0);
         mPipTabletopController.onDisplayConfigurationChanged(DEFAULT_DISPLAY, mConfiguration);
 
-        verifyZeroInteractions(mOnTabletopModeChangedListener);
+        verifyNoMoreInteractions(mOnTabletopModeChangedListener);
     }
 
     // Test cases starting from folded state (DEVICE_POSTURE_CLOSED)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
index e92e243..a2066db 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -157,7 +157,7 @@
         opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
                 packageName);
 
-        verifyZeroInteractions(mMockExecutor);
+        verifyNoMoreInteractions(mMockExecutor);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 450989d..da6a67c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,28 +26,36 @@
 import android.os.Handler
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.UsesFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
 import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.IWindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.DisplayAreaInfo
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.server.display.feature.flags.Flags as DisplayFlags
 import com.android.window.flags.Flags
 import com.android.wm.shell.MockToken
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.transition.Transitions
 import com.google.common.truth.Truth.assertThat
 import com.google.testing.junit.testparameterinjector.TestParameter
 import com.google.testing.junit.testparameterinjector.TestParameterInjector
 import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,6 +68,7 @@
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
 
 /**
  * Test class for [DesktopDisplayModeController]
@@ -68,6 +77,7 @@
  */
 @SmallTest
 @RunWith(TestParameterInjector::class)
+@UsesFlags(com.android.server.display.feature.flags.Flags::class)
 class DesktopDisplayModeControllerTest(
     @TestParameter(valuesProvider = FlagsParameterizationProvider::class)
     flags: FlagsParameterization
@@ -79,6 +89,7 @@
     private val desktopWallpaperActivityTokenProvider =
         mock<DesktopWallpaperActivityTokenProvider>()
     private val inputManager = mock<InputManager>()
+    private val displayController = mock<DisplayController>()
     private val mainHandler = mock<Handler>()
 
     private lateinit var controller: DesktopDisplayModeController
@@ -90,6 +101,13 @@
         TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
     private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
     private val wallpaperToken = MockToken().token()
+    private val defaultDisplay = mock<Display>()
+    private val externalDisplay = mock<Display>()
+
+    private lateinit var extendedDisplaySettingsRestoreSession:
+        ExtendedDisplaySettingsRestoreSession
+
+    private lateinit var mockitoSession: StaticMockitoSession
 
     init {
         mSetFlagsRule.setFlagsParameterization(flags)
@@ -97,6 +115,13 @@
 
     @Before
     fun setUp() {
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .mockStatic(DesktopModeStatus::class.java)
+                .startMocking()
+        extendedDisplaySettingsRestoreSession =
+            ExtendedDisplaySettingsRestoreSession(context.contentResolver)
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
         whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
             .thenReturn(defaultTDA)
@@ -109,42 +134,57 @@
                 shellTaskOrganizer,
                 desktopWallpaperActivityTokenProvider,
                 inputManager,
+                displayController,
                 mainHandler,
             )
         runningTasks.add(freeformTask)
         runningTasks.add(fullscreenTask)
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+        whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
+        whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
         setTabletModeStatus(SwitchState.UNKNOWN)
+        whenever(
+            DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                context,
+                defaultDisplay
+            )
+        ).thenReturn(true)
+    }
+
+    @After
+    fun tearDown() {
+        extendedDisplaySettingsRestoreSession.restore()
+        mockitoSession.finishMocking()
     }
 
     private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
         defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
         whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
-        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
-            connectExternalDisplay()
-            if (expectToSwitch) {
-                // Assumes [connectExternalDisplay] properly triggered the switching transition.
-                // Will verify the transition later along with [disconnectExternalDisplay].
-                defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-            }
-            disconnectExternalDisplay()
+        setExtendedMode(true)
 
-            if (expectToSwitch) {
-                val arg = argumentCaptor<WindowContainerTransaction>()
-                verify(transitions, times(2))
-                    .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-                assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FREEFORM)
-                assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-                assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-                assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-            } else {
-                verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
-            }
+        connectExternalDisplay()
+        if (expectToSwitch) {
+            // Assumes [connectExternalDisplay] properly triggered the switching transition.
+            // Will verify the transition later along with [disconnectExternalDisplay].
+            defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+        }
+        disconnectExternalDisplay()
+
+        if (expectToSwitch) {
+            val arg = argumentCaptor<WindowContainerTransaction>()
+            verify(transitions, times(2))
+                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+            assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
+            assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+        } else {
+            verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
         }
     }
 
@@ -176,15 +216,16 @@
             disconnectExternalDisplay()
         }
         setTabletModeStatus(tabletModeStatus)
-
-        ExtendedDisplaySettingsSession(
-                context.contentResolver,
-                if (param.extendedDisplayEnabled) 1 else 0,
+        setExtendedMode(param.extendedDisplayEnabled)
+        whenever(
+            DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                context,
+                defaultDisplay
             )
-            .use {
-                assertThat(controller.getTargetWindowingModeForDefaultDisplay())
-                    .isEqualTo(param.expectedWindowingMode)
-            }
+        ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+            .isEqualTo(param.expectedWindowingMode)
     }
 
     @Test
@@ -199,15 +240,16 @@
             disconnectExternalDisplay()
         }
         setTabletModeStatus(param.tabletModeStatus)
-
-        ExtendedDisplaySettingsSession(
-                context.contentResolver,
-                if (param.extendedDisplayEnabled) 1 else 0,
+        setExtendedMode(param.extendedDisplayEnabled)
+        whenever(
+            DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                context,
+                defaultDisplay
             )
-            .use {
-                assertThat(controller.getTargetWindowingModeForDefaultDisplay())
-                    .isEqualTo(param.expectedWindowingMode)
-            }
+        ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+            .isEqualTo(param.expectedWindowingMode)
     }
 
     @Test
@@ -215,18 +257,16 @@
     fun displayWindowingModeSwitch_existingTasksOnConnected() {
         defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
         whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+        setExtendedMode(true)
 
-        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
-            connectExternalDisplay()
+        connectExternalDisplay()
 
-            val arg = argumentCaptor<WindowContainerTransaction>()
-            verify(transitions, times(1))
-                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_UNDEFINED)
-            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-        }
+        val arg = argumentCaptor<WindowContainerTransaction>()
+        verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
     }
 
     @Test
@@ -236,18 +276,16 @@
         whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
             WINDOWING_MODE_FULLSCREEN
         }
+        setExtendedMode(true)
 
-        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
-            disconnectExternalDisplay()
+        disconnectExternalDisplay()
 
-            val arg = argumentCaptor<WindowContainerTransaction>()
-            verify(transitions, times(1))
-                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FREEFORM)
-            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_UNDEFINED)
-        }
+        val arg = argumentCaptor<WindowContainerTransaction>()
+        verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
     }
 
     private fun connectExternalDisplay() {
@@ -266,18 +304,30 @@
         whenever(inputManager.isInTabletMode()).thenReturn(status.value)
     }
 
-    private class ExtendedDisplaySettingsSession(
-        private val contentResolver: ContentResolver,
-        private val overrideValue: Int,
-    ) : AutoCloseable {
+    private fun setExtendedMode(enabled: Boolean) {
+        if (DisplayFlags.enableDisplayContentModeManagement()) {
+            whenever(
+                DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                    context,
+                    externalDisplay
+                )
+            ).thenReturn(enabled)
+        } else {
+            Settings.Global.putInt(
+                context.contentResolver,
+                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+                if (enabled) 1 else 0,
+            )
+        }
+    }
+
+    private class ExtendedDisplaySettingsRestoreSession(
+        private val contentResolver: ContentResolver
+    ) {
         private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
         private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
 
-        init {
-            Settings.Global.putInt(contentResolver, settingName, overrideValue)
-        }
-
-        override fun close() {
+        fun restore() {
             Settings.Global.putInt(contentResolver, settingName, initialValue)
         }
     }
@@ -287,7 +337,8 @@
             context: TestParameterValuesProvider.Context
         ): List<FlagsParameterization> {
             return FlagsParameterization.allCombinationsOf(
-                Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH
+                Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
+                DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
             )
         }
     }
@@ -305,54 +356,119 @@
             val defaultWindowingMode: Int,
             val hasExternalDisplay: Boolean,
             val extendedDisplayEnabled: Boolean,
+            val isDefaultDisplayDesktopEligible: Boolean,
             val expectedWindowingMode: Int,
         ) {
-            FREEFORM_EXTERNAL_EXTENDED(
+            FREEFORM_EXTERNAL_EXTENDED_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FREEFORM,
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            FULLSCREEN_EXTERNAL_EXTENDED(
+            FULLSCREEN_EXTERNAL_EXTENDED_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            FREEFORM_NO_EXTERNAL_EXTENDED(
+            FREEFORM_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FREEFORM,
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            FULLSCREEN_NO_EXTERNAL_EXTENDED(
+            FULLSCREEN_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            FREEFORM_EXTERNAL_MIRROR(
+            FREEFORM_EXTERNAL_MIRROR_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FREEFORM,
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            FULLSCREEN_EXTERNAL_MIRROR(
+            FULLSCREEN_EXTERNAL_MIRROR_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            FREEFORM_NO_EXTERNAL_MIRROR(
+            FREEFORM_NO_EXTERNAL_MIRROR_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FREEFORM,
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            FULLSCREEN_NO_EXTERNAL_MIRROR(
+            FULLSCREEN_NO_EXTERNAL_MIRROR_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_NO_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_NO_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_NO_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_NO_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
         }
@@ -361,78 +477,175 @@
             val hasExternalDisplay: Boolean,
             val extendedDisplayEnabled: Boolean,
             val tabletModeStatus: SwitchState,
+            val isDefaultDisplayDesktopEligible: Boolean,
             val expectedWindowingMode: Int,
         ) {
-            EXTERNAL_EXTENDED_TABLET(
+            EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = true,
                 tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            NO_EXTERNAL_EXTENDED_TABLET(
+            NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = true,
                 tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            EXTERNAL_MIRROR_TABLET(
+            EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = false,
                 tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            NO_EXTERNAL_MIRROR_TABLET(
+            NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = false,
                 tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            EXTERNAL_EXTENDED_CLAMSHELL(
+            EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = true,
                 tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            NO_EXTERNAL_EXTENDED_CLAMSHELL(
+            NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = true,
                 tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            EXTERNAL_MIRROR_CLAMSHELL(
+            EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = false,
                 tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            NO_EXTERNAL_MIRROR_CLAMSHELL(
+            NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = false,
                 tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            EXTERNAL_EXTENDED_UNKNOWN(
+            EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = true,
                 tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            NO_EXTERNAL_EXTENDED_UNKNOWN(
+            NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = true,
                 tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            EXTERNAL_MIRROR_UNKNOWN(
+            EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
                 hasExternalDisplay = true,
                 extendedDisplayEnabled = false,
                 tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
-            NO_EXTERNAL_MIRROR_UNKNOWN(
+            NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
                 hasExternalDisplay = false,
                 extendedDisplayEnabled = false,
                 tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_TABLET_PROJECTED(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_TABLET_PROJECTED(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_TABLET_PROJECTED(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_TABLET_PROJECTED(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
                 expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 8a5acfa..695cf60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -23,7 +23,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
-import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.window.flags.Flags
@@ -102,7 +102,7 @@
                 eq(sessionId),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -127,7 +127,7 @@
                 eq(sessionId),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -135,7 +135,7 @@
         desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -157,7 +157,7 @@
                 eq(sessionId),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
         assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
     }
 
@@ -166,7 +166,7 @@
         desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -205,7 +205,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -213,7 +213,7 @@
         desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -252,7 +252,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -260,7 +260,7 @@
         desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -302,7 +302,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -346,7 +346,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -390,7 +390,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -434,7 +434,7 @@
                 eq(FocusReason.UNKNOWN.reason),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -446,7 +446,7 @@
         )
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -485,7 +485,7 @@
         )
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index b7d25b5..bd37610 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -83,7 +83,7 @@
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 /**
@@ -596,7 +596,7 @@
             .logTaskRemoved(
                 eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -668,7 +668,7 @@
             .logTaskInfoChanged(
                 eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -701,7 +701,7 @@
                     )
                 )
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -729,7 +729,7 @@
                     )
                 )
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -753,7 +753,7 @@
             .logTaskInfoChanged(
                 eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
 
         // task 2 resize
         val newTaskInfo2 =
@@ -781,7 +781,7 @@
                     )
                 )
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -892,14 +892,14 @@
                 eq(taskUpdate.visibleTaskCount.toString()),
             )
         }
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     private fun verifyTaskRemovedAndExitLogging(exitReason: ExitReason, taskUpdate: TaskUpdate) {
         assertFalse(transitionObserver.isSessionActive)
         verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
         verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 3813f75..a10aeca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -275,6 +275,18 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun removeActiveTask_excludingDesk_leavesTaskInDesk() {
+        repo.addDesk(displayId = 2, deskId = 11)
+        repo.addDesk(displayId = 3, deskId = 12)
+        repo.addTaskToDesk(displayId = 3, deskId = 12, taskId = 100, isVisible = true)
+
+        repo.removeActiveTask(taskId = 100, excludedDeskId = 12)
+
+        assertThat(repo.getActiveTaskIdsInDesk(12)).contains(100)
+    }
+
+    @Test
     fun isActiveTask_nonExistingTask_returnsFalse() {
         assertThat(repo.isActiveTask(99)).isFalse()
     }
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 14af573..7efcd4f 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
@@ -57,6 +57,7 @@
 import android.testing.TestableContext
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display.INVALID_DISPLAY
 import android.view.DragEvent
 import android.view.Gravity
 import android.view.MotionEvent
@@ -458,7 +459,7 @@
             overviewToDesktopTransitionObserver,
             desksOrganizer,
             desksTransitionsObserver,
-            desktopPipTransitionObserver,
+            Optional.of(desktopPipTransitionObserver),
             userProfileContexts,
             desktopModeCompatPolicy,
             dragToDisplayTransitionHandler,
@@ -3126,6 +3127,36 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_toDesktopInOtherDisplay_appliesTaskLimit() {
+        val transition = Binder()
+        val sourceDeskId = 0
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        taskRepository.setDeskInactive(deskId = targetDeskId)
+        val targetDeskTasks =
+            (1..MAX_TASK_LIMIT + 1).map { _ ->
+                setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+            }
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+            .thenReturn(transition)
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+        controller.moveToNextDisplay(task.taskId)
+
+        val wct = getLatestTransition()
+        assertNotNull(wct)
+        verify(desksOrganizer).minimizeTask(wct, targetDeskId, targetDeskTasks[0])
+    }
+
+    @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
@@ -3194,10 +3225,11 @@
         verify(desksOrganizer).activateDesk(any(), eq(targetDeskId))
         verify(desksTransitionsObserver)
             .addPendingTransition(
-                DeskTransition.ActivateDesk(
+                DeskTransition.ActiveDeskWithTask(
                     token = transition,
                     displayId = SECOND_DISPLAY,
                     deskId = targetDeskId,
+                    enterTaskId = task.taskId,
                 )
             )
     }
@@ -5072,6 +5104,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun handleRequest_closeTransition_singleTaskNoToken_secondaryDisplay_launchesHome() {
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -7409,6 +7442,14 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun testCreateDesk_invalidDisplay_dropsRequest() {
+        controller.createDesk(INVALID_DISPLAY)
+
+        verify(desksOrganizer, never()).createDesk(any(), any())
+    }
+
+    @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index ec64c2f..5ef1ace 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -50,6 +50,7 @@
 import com.android.wm.shell.transition.Transitions
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import java.util.Optional
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -110,7 +111,7 @@
                 transitions,
                 shellTaskOrganizer,
                 mixedHandler,
-                pipTransitionObserver,
+                Optional.of(pipTransitionObserver),
                 backAnimationController,
                 desktopWallpaperActivityTokenProvider,
                 shellInit,
@@ -333,6 +334,50 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun nonTopTransparentTaskOpened_clearTopTransparentTaskIdFromRepository() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val topTransparentTask = createTaskInfo(1)
+        val nonTopTransparentTask = createTaskInfo(2)
+        whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+            .thenReturn(topTransparentTask.taskId)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createOpenChangeTransition(nonTopTransparentTask),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun nonTopTransparentTaskSentToFront_clearTopTransparentTaskIdFromRepository() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val topTransparentTask = createTaskInfo(1)
+        val nonTopTransparentTask = createTaskInfo(2)
+        whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+            .thenReturn(topTransparentTask.taskId)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createToFrontTransition(nonTopTransparentTask),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+    }
+
+    @Test
     fun transitCloseWallpaper_wallpaperActivityVisibilitySaved() {
         val wallpaperTask = createWallpaperTaskInfo()
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 6e7adf3..4e2994c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -54,7 +54,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.MockitoSession
@@ -64,7 +66,7 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
 
@@ -85,8 +87,17 @@
     @Mock private lateinit var desktopUserRepositories: DesktopUserRepositories
     @Mock private lateinit var bubbleController: BubbleController
     @Mock private lateinit var visualIndicator: DesktopModeVisualIndicator
+    @Mock private lateinit var dragCancelCallback: Runnable
+    @Mock
+    private lateinit var dragToDesktopStateListener:
+        DragToDesktopTransitionHandler.DragToDesktopStateListener
 
-    private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+    private val transactionSupplier = Supplier {
+        val transaction = mock<SurfaceControl.Transaction>()
+        whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+        whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+        transaction
+    }
 
     private lateinit var defaultHandler: DragToDesktopTransitionHandler
     private lateinit var springHandler: SpringDragToDesktopTransitionHandler
@@ -104,7 +115,11 @@
                     Optional.of(bubbleController),
                     transactionSupplier,
                 )
-                .apply { setSplitScreenController(splitScreenController) }
+                .apply {
+                    setSplitScreenController(splitScreenController)
+                    dragToDesktopStateListener =
+                        this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+                }
         springHandler =
             SpringDragToDesktopTransitionHandler(
                     context,
@@ -115,7 +130,11 @@
                     Optional.of(bubbleController),
                     transactionSupplier,
                 )
-                .apply { setSplitScreenController(splitScreenController) }
+                .apply {
+                    setSplitScreenController(splitScreenController)
+                    dragToDesktopStateListener =
+                        this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+                }
         mockitoSession =
             ExtendedMockito.mockitoSession()
                 .strictness(Strictness.LENIENT)
@@ -438,7 +457,7 @@
         )
 
         // No need to animate the cancel since the start animation couldn't even start.
-        verifyZeroInteractions(dragAnimator)
+        verifyNoMoreInteractions(dragAnimator)
     }
 
     @Test
@@ -489,7 +508,7 @@
         )
 
         // Should NOT have any transaction changes
-        verifyZeroInteractions(mergedStartTransaction)
+        verifyNoMoreInteractions(mergedStartTransaction)
         // Should NOT merge animation
         verify(finishCallback, never()).onTransitionFinished(any())
     }
@@ -706,8 +725,8 @@
     }
 
     @Test
-    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
-    fun mergeOtherTransition_cancelAndEndNotYetRequested_doesntInterruptsStartDrag() {
+    @DisableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_flagDisabled_cancelAndEndNotYetRequested_doesNotInterruptStartDrag() {
         val finishCallback = mock<Transitions.TransitionFinishCallback>()
         val task = createTask()
         defaultHandler.onTaskResizeAnimationListener = mock()
@@ -721,6 +740,39 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_cancelAndEndNotYetRequested_interruptsStartDrag() {
+        val finishCallback = mock<Transitions.TransitionFinishCallback>()
+        val task = createTask()
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+        mergeInterruptingTransition(mergeTarget = startTransition)
+
+        verify(dragAnimator).cancelAnimator()
+        verify(dragCancelCallback).run()
+        verify(dragToDesktopStateListener).onTransitionInterrupted()
+        assertThat(defaultHandler.inProgress).isTrue()
+        // Doesn't finish start transition yet
+        verify(finishCallback, never()).onTransitionFinished(/* wct= */ anyOrNull())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_cancelAndEndNotYetRequested_finishesStartAfterAnimation() {
+        val finishCallback = mock<Transitions.TransitionFinishCallback>()
+        val task = createTask()
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+        mergeInterruptingTransition(mergeTarget = startTransition)
+        mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+
+        verify(finishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+        assertThat(defaultHandler.inProgress).isFalse()
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
     fun mergeOtherTransition_endDragAlreadyMerged_doesNotInterruptStartDrag() {
         val startDragFinishCallback = mock<Transitions.TransitionFinishCallback>()
         val task = createTask()
@@ -795,6 +847,35 @@
         verify(dragAnimator, times(2)).startAnimation()
     }
 
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun startCancelAnimation_otherTransitionInterruptingAfterCancelRequest_finishImmediately() {
+        val task1 = createTask()
+        val startTransition = startDrag(defaultHandler, task1)
+        val cancelTransition =
+            cancelDragToDesktopTransition(defaultHandler, CancelState.STANDARD_CANCEL)
+        mergeInterruptingTransition(mergeTarget = startTransition)
+        val cancelFinishCallback = mock<Transitions.TransitionFinishCallback>()
+        val startTransaction = mock<SurfaceControl.Transaction>()
+
+        val didAnimate =
+            defaultHandler.startAnimation(
+                transition = requireNotNull(cancelTransition),
+                info =
+                    createTransitionInfo(
+                        type = TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP,
+                        draggedTask = task1,
+                    ),
+                startTransaction = startTransaction,
+                finishTransaction = mock(),
+                finishCallback = cancelFinishCallback,
+            )
+
+        assertThat(didAnimate).isTrue()
+        verify(startTransaction).apply()
+        verify(cancelFinishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+    }
+
     private fun mergeInterruptingTransition(mergeTarget: IBinder) {
         defaultHandler.mergeAnimation(
             transition = mock<IBinder>(),
@@ -942,7 +1023,12 @@
                 )
             )
             .thenReturn(token)
-        handler.startDragToDesktopTransition(task, dragAnimator, visualIndicator)
+        handler.startDragToDesktopTransition(
+            task,
+            dragAnimator,
+            visualIndicator,
+            dragCancelCallback,
+        )
         return token
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index c7518d5..3983bfb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -54,7 +54,7 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 /**
@@ -111,7 +111,7 @@
                 eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
             )
         // Assert fadeIn, fadeOut, and animateIndicatorType were not called.
-        verifyZeroInteractions(spyViewContainer)
+        verifyNoMoreInteractions(spyViewContainer)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 7560945..dc973d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -90,7 +90,7 @@
         whenever(packageManager.getHomeActivities(ArrayList())).thenReturn(componentName)
         desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext)
         transitionHandler = createTransitionHandler()
-        allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+        allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
     }
 
     private fun createTransitionHandler() =
@@ -200,10 +200,16 @@
             .isTrue()
     }
 
-    fun allowOverlayPermission(permissions: Array<String>) {
+    fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
         val packageInfo = mock<PackageInfo>()
         packageInfo.requestedPermissions = permissions
-        whenever(packageManager.getPackageInfo(anyString(), eq(PackageManager.GET_PERMISSIONS)))
+        whenever(
+                packageManager.getPackageInfoAsUser(
+                    anyString(),
+                    eq(PackageManager.GET_PERMISSIONS),
+                    anyInt(),
+                )
+            )
             .thenReturn(packageInfo)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 14f9ffc..2bd9afc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -107,7 +107,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index 72c4666..fa7ab952 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -117,7 +117,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
 
         // Check corner and shadow radii were set
         verify(mMockAnimateTransaction, atLeastOnce())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index b816f0e..97133be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -143,7 +143,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 23fbad0..c99ca6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.kotlin.MatchersKt.eq;
 
@@ -118,7 +118,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 5029371..b6894fd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -30,7 +30,7 @@
 import static org.mockito.kotlin.VerificationKt.clearInvocations;
 import static org.mockito.kotlin.VerificationKt.times;
 import static org.mockito.kotlin.VerificationKt.verify;
-import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+import static org.mockito.kotlin.VerificationKt.verifyNoMoreInteractions;
 
 import android.app.ActivityManager;
 import android.app.PendingIntent;
@@ -176,7 +176,7 @@
         mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
                 aspectRatio, action1));
 
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
     }
 
     @Test
@@ -193,7 +193,7 @@
         clearInvocations(mMockPipParamsChangedCallback);
         mPipTaskListener.onTaskInfoChanged(new ActivityManager.RunningTaskInfo());
 
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
         verify(mMockPipTransitionState, times(0))
                 .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
     }
@@ -245,7 +245,7 @@
         mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
 
         verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
     }
 
     @Test
@@ -262,7 +262,7 @@
         clearInvocations(mMockPipParamsChangedCallback);
         mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
 
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
         verify(mMockPipTransitionState, times(0))
                 .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
     }
@@ -319,7 +319,7 @@
                 PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
                 extras);
 
-        verifyZeroInteractions(mMockPipScheduler);
+        verifyNoMoreInteractions(mMockPipScheduler);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
index 82cdfd5..51de50d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.Flags;
@@ -82,7 +82,7 @@
         mPipUiStateChangeController.onPipTransitionStateChanged(
                 PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
 
-        verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+        verifyNoMoreInteractions(mPictureInPictureUiStateConsumer);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index 5ac6800..12785c0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -42,6 +42,7 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.kotlin.any
 import org.mockito.kotlin.eq
@@ -87,7 +88,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
-        allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+        allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
         assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
                 .apply {
@@ -101,7 +102,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
-        allowOverlayPermission(arrayOf())
+        allowOverlayPermissionForAllUsers(arrayOf())
         assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
                 .apply {
@@ -115,7 +116,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun testIsTopActivityExemptCachedPermissionCheckIsUsed() {
-        allowOverlayPermission(arrayOf())
+        allowOverlayPermissionForAllUsers(arrayOf())
         assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
                 .apply {
@@ -123,6 +124,7 @@
                     isTopActivityNoDisplay = false
                     numActivities = 1
                     baseActivity = baseActivityTest
+                    userId = 10
                 }))
         assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
@@ -131,10 +133,26 @@
                     isTopActivityNoDisplay = false
                     numActivities = 1
                     baseActivity = baseActivityTest
+                    userId = 10
                 }))
-        verify(packageManager, times(1)).getPackageInfo(
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    isActivityStackTransparent = true
+                    isTopActivityNoDisplay = false
+                    numActivities = 1
+                    baseActivity = baseActivityTest
+                    userId = 0
+                }))
+        verify(packageManager, times(1)).getPackageInfoAsUser(
             eq("com.test.dummypackage"),
-            eq(PackageManager.GET_PERMISSIONS)
+            eq(PackageManager.GET_PERMISSIONS),
+            eq(10)
+        )
+        verify(packageManager, times(1)).getPackageInfoAsUser(
+            eq("com.test.dummypackage"),
+            eq(PackageManager.GET_PERMISSIONS),
+            eq(0)
         )
     }
 
@@ -284,13 +302,14 @@
             }
         }
 
-    fun allowOverlayPermission(permissions: Array<String>) {
+    fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
         val packageInfo = mock<PackageInfo>()
         packageInfo.requestedPermissions = permissions
         whenever(
-            packageManager.getPackageInfo(
+            packageManager.getPackageInfoAsUser(
                 anyString(),
-                eq(PackageManager.GET_PERMISSIONS)
+                eq(PackageManager.GET_PERMISSIONS),
+                anyInt(),
             )
         ).thenReturn(packageInfo)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 3099b0f..a122c38 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -27,6 +27,7 @@
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
 
@@ -44,6 +45,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -196,6 +198,73 @@
     }
 
     @Test
+    @DisableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+    public void startDragToDesktopFinished_flagDisabled_doesNotTriggerCallback()
+            throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+        IBinder transition = mock(IBinder.class);
+        mHomeTransitionObserver.onTransitionReady(
+                transition,
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+        verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+    }
+
+    @Test
+    @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+    public void startDragToDesktopAborted_doesNotTriggerCallback() throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+        IBinder transition = mock(IBinder.class);
+        mHomeTransitionObserver.onTransitionReady(
+                transition,
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ true);
+
+        verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+    }
+
+    @Test
+    @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+    public void startDragToDesktopFinished_triggersCallback() throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+        IBinder transition = mock(IBinder.class);
+        mHomeTransitionObserver.onTransitionReady(
+                transition,
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+        verify(mListener).onHomeVisibilityChanged(/* isVisible= */ true);
+    }
+
+    @Test
     @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
     public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
         ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index 067dcec..b1f9241 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -28,6 +28,7 @@
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -109,7 +110,7 @@
         onTaskOpening(task, taskSurface)
         assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
         task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
 
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
         verify(decoration).close()
@@ -165,7 +166,7 @@
 
         setLargeScreen(false)
         setUpMockDecorationForTask(task)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index d69509f..ad3426e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -51,6 +51,7 @@
 import android.view.View
 import android.view.ViewRootImpl
 import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.HierarchyOp
 import androidx.test.filters.SmallTest
@@ -134,7 +135,7 @@
 
         task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
         task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
 
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
         verify(decoration).close()
@@ -149,12 +150,12 @@
         val taskSurface = SurfaceControl()
         setUpMockDecorationForTask(task)
 
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
 
         task.setWindowingMode(WINDOWING_MODE_FREEFORM)
         task.setActivityType(ACTIVITY_TYPE_STANDARD)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
         assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
@@ -758,20 +759,6 @@
     }
 
     @Test
-    fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
-        val toSplitScreenListenerCaptor = forClass(Function0::class.java)
-                as ArgumentCaptor<Function0<Unit>>
-        val decor = createOpenTaskDecoration(
-            windowingMode = WINDOWING_MODE_MULTI_WINDOW,
-            onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
-        )
-
-        toSplitScreenListenerCaptor.value.invoke()
-
-        verify(decor).disposeStatusBarInputLayer()
-    }
-
-    @Test
     fun testDecor_onClickToOpenBrowser_closeMenus() {
         val openInBrowserListenerCaptor = forClass(Consumer::class.java)
                 as ArgumentCaptor<Consumer<Intent>>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index a1f40fd..4c9c2f1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -84,6 +84,7 @@
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
 import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
 import org.junit.After
 import org.mockito.Mockito
@@ -125,6 +126,7 @@
     protected val mockShellController = mock<ShellController>()
     protected val testShellExecutor = TestShellExecutor()
     protected val mockAppHeaderViewHolderFactory = mock<AppHeaderViewHolder.Factory>()
+    protected val mockAppHandleViewHolderFactory = mock<AppHandleViewHolder.Factory>()
     protected val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
     protected val mockShellCommandHandler = mock<ShellCommandHandler>()
     protected val mockWindowManager = mock<IWindowManager>()
@@ -222,6 +224,7 @@
             mockInputMonitorFactory,
             transactionFactory,
             mockAppHeaderViewHolderFactory,
+            mockAppHandleViewHolderFactory,
             mockRootTaskDisplayAreaOrganizer,
             windowDecorByTaskIdSpy,
             mockInteractionJankMonitor,
@@ -331,7 +334,7 @@
             mockDesktopModeWindowDecorFactory.create(
                 any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
                 any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
-                any(), any(), any())
+                any(), any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.user).thenReturn(mockUserHandle)
@@ -353,12 +356,17 @@
         )
     }
 
-    protected fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+    protected fun onTaskChanging(
+        task: RunningTaskInfo,
+        leash: SurfaceControl = SurfaceControl(),
+        changeMode: Int
+    ) {
         desktopModeWindowDecorViewModel.onTaskChanging(
             task,
             leash,
             StubTransaction(),
-            StubTransaction()
+            StubTransaction(),
+            changeMode
         )
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 8783249..f37f2fb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -115,6 +115,7 @@
 import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 
 import kotlin.Unit;
@@ -171,6 +172,9 @@
     private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
     private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
     private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
+    private static final boolean DEFAULT_IS_RECENTS_TRANSITION_RUNNING = false;
+    private static final boolean DEFAULT_IS_MOVING_TO_BACK = false;
+
 
     @Mock
     private DisplayController mMockDisplayController;
@@ -191,8 +195,12 @@
     @Mock
     private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
     @Mock
+    private AppHandleViewHolder.Factory mMockAppHandleViewHolderFactory;
+    @Mock
     private AppHeaderViewHolder mMockAppHeaderViewHolder;
     @Mock
+    private AppHandleViewHolder mMockAppHandleViewHolder;
+    @Mock
     private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
     @Mock
     private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
@@ -301,6 +309,9 @@
         when(mMockAppHeaderViewHolderFactory
                 .create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
                 .thenReturn(mMockAppHeaderViewHolder);
+        when(mMockAppHandleViewHolderFactory
+                .create(any(), any(), any(), any(), any()))
+                .thenReturn(mMockAppHandleViewHolder);
         when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
         when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
         when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
@@ -421,7 +432,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 /* shouldIgnoreCornerRadius= */ true,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
     }
@@ -623,7 +636,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                /* shouldExcludeCaptionFromAppBounds */ true);
+                /* shouldExcludeCaptionFromAppBounds */ true,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         // Force consuming flags are disabled.
         assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
@@ -658,7 +673,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
         assertThat(
@@ -737,7 +754,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         // Takes status bar inset as padding, ignores caption bar inset.
         assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -765,7 +784,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsInsetSource).isFalse();
     }
@@ -792,7 +813,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         // Header is always shown because it's assumed the status bar is always visible.
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -819,7 +842,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
     }
@@ -845,7 +870,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -871,7 +898,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -898,7 +927,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
 
@@ -917,7 +948,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -944,7 +977,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
     }
@@ -971,7 +1006,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -1002,6 +1039,65 @@
         assertThat(relayoutParams.mAsyncViewHost).isFalse();
     }
 
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+    public void updateRelayoutParams_handle_movingToBack_captionNotVisible() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                mMockSplitScreenController,
+                DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+                DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+                DEFAULT_IS_STATUSBAR_VISIBLE,
+                DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+                DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
+                new InsetsState(),
+                DEFAULT_HAS_GLOBAL_FOCUS,
+                mExclusionRegion,
+                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                /* isMovingToBack= */ true);
+
+        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+    public void updateRelayoutParams_handle_inRecentsTransition_captionNotVisible() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                mMockSplitScreenController,
+                DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+                DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+                DEFAULT_IS_STATUSBAR_VISIBLE,
+                DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+                DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
+                new InsetsState(),
+                DEFAULT_HAS_GLOBAL_FOCUS,
+                mExclusionRegion,
+                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                /* isRecentsTransitionRunning= */ true,
+                DEFAULT_IS_MOVING_TO_BACK);
+
+        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+    }
+
     @Test
     public void relayout_fullscreenTask_appliesTransactionImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1633,7 +1729,9 @@
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
     }
 
     private DesktopModeWindowDecoration createWindowDecoration(
@@ -1676,9 +1774,9 @@
                 taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
                 mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
                 mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
-                mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
-                mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
-                WindowContainerTransaction::new, SurfaceControl::new,
+                mMockAppHandleViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
+                mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+                mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
                 new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
                 mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
                 mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
index 7341e09..3600997 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
@@ -40,11 +40,13 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.function.Consumer
 import java.util.function.Supplier
+import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argThat
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
@@ -63,6 +65,15 @@
     private val testBgExecutor = TestShellExecutor()
     private val mockWindowSession = mock<IWindowSession>()
     private val mockInputEventReceiver = mock<TaskResizeInputEventReceiver>()
+    private val inputChannel = mock<InputChannel>()
+    private val sinkInputChannel = mock<InputChannel>()
+    private val decorationSurface = SurfaceControl.Builder().setName("decoration surface").build()
+    private val createdSurfaces = ArrayList<SurfaceControl>()
+
+    @After
+    fun tearDown() {
+        decorationSurface.release()
+    }
 
     @Test
     fun testGrantInputChannelOffMainThread() {
@@ -73,6 +84,35 @@
     }
 
     @Test
+    fun testGrantInputChannelAfterDecorSurfaceReleased() {
+        // Keep tracking the underlying surface that the decorationSurface points to.
+        val forVerification = SurfaceControl(decorationSurface, "forVerification")
+        try {
+            create()
+            decorationSurface.release()
+            testBgExecutor.flushAll()
+
+            verify(mockWindowSession)
+                .grantInputChannel(
+                    anyInt(),
+                    argThat<SurfaceControl> { isValid && isSameSurface(forVerification) },
+                    any(),
+                    anyOrNull(),
+                    anyInt(),
+                    anyInt(),
+                    anyInt(),
+                    anyInt(),
+                    anyOrNull(),
+                    any(),
+                    any(),
+                    any(),
+                )
+        } finally {
+            forVerification.release()
+        }
+    }
+
+    @Test
     fun testInitializationCallback_waitsForBgSetup() {
         val inputListener = create()
 
@@ -143,6 +183,40 @@
         verify(mockWindowSession).remove(inputListener.mSinkClientToken)
     }
 
+    @Test
+    fun testClose_afterBgSetup_disposesOfInputChannels() {
+        val inputListener = create()
+        testBgExecutor.flushAll()
+        inputListener.close()
+        testMainExecutor.flushAll()
+        verify(inputChannel).dispose()
+        verify(sinkInputChannel).dispose()
+    }
+
+    @Test
+    fun testClose_beforeBgSetup_releaseSurfaces() {
+        val inputListener = create()
+        inputListener.close()
+        testBgExecutor.flushAll()
+        testMainExecutor.flushAll()
+
+        assertThat(createdSurfaces).hasSize(1)
+        assertThat(createdSurfaces[0].isValid).isFalse()
+    }
+
+    @Test
+    fun testClose_afterBgSetup_releaseSurfaces() {
+        val inputListener = create()
+        testBgExecutor.flushAll()
+        inputListener.close()
+        testMainExecutor.flushAll()
+        testBgExecutor.flushAll()
+
+        assertThat(createdSurfaces).hasSize(2)
+        assertThat(createdSurfaces[0].isValid).isFalse()
+        assertThat(createdSurfaces[1].isValid).isFalse()
+    }
+
     private fun verifyNoInputChannelGrantRequests() {
         verify(mockWindowSession, never())
             .grantInputChannel(
@@ -172,12 +246,26 @@
             TestHandler(Looper.getMainLooper()),
             mock<Choreographer>(),
             Display.DEFAULT_DISPLAY,
-            mock<SurfaceControl>(),
+            decorationSurface,
             mock<DragPositioningCallback>(),
-            { SurfaceControl.Builder() },
-            { StubTransaction() },
+            {
+                object : SurfaceControl.Builder() {
+                    override fun build(): SurfaceControl {
+                        return super.build().also { createdSurfaces.add(it) }
+                    }
+                }
+            },
+            {
+                object : StubTransaction() {
+                    override fun remove(sc: SurfaceControl): SurfaceControl.Transaction {
+                        return super.remove(sc).also { sc.release() }
+                    }
+                }
+            },
             mock<DisplayController>(),
             mock<DesktopModeEventLogger>(),
+            inputChannel,
+            sinkInputChannel,
         )
 
     private class TestInitializationCallback : Runnable {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index f984f6d..2e46f63 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -98,8 +98,6 @@
 
     private lateinit var handleMenu: HandleMenu
 
-    private val menuWidthWithElevation = MENU_WIDTH + MENU_PILL_ELEVATION
-
     @Before
     fun setUp() {
         val mockAdditionalViewHostViewContainer = AdditionalViewHostViewContainer(
@@ -126,7 +124,6 @@
             addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
             addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
             addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
-            addOverride(R.dimen.desktop_mode_handle_menu_pill_elevation, MENU_PILL_ELEVATION)
             addOverride(
                 R.dimen.desktop_mode_handle_menu_pill_spacing_margin, MENU_PILL_SPACING_MARGIN)
         }
@@ -141,7 +138,7 @@
         assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of display.
-        val expected = Point(DISPLAY_BOUNDS.centerX() - menuWidthWithElevation / 2, MENU_TOP_MARGIN)
+        val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
         assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
     }
 
@@ -165,7 +162,7 @@
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of split left task.
         val expected = Point(
-            SPLIT_LEFT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+            SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2,
             MENU_TOP_MARGIN
         )
         assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -180,7 +177,7 @@
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of split right task.
         val expected = Point(
-            SPLIT_RIGHT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+            SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2,
             MENU_TOP_MARGIN
         )
         assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -323,7 +320,6 @@
         private const val MENU_HEIGHT = 400
         private const val MENU_TOP_MARGIN = 10
         private const val MENU_START_MARGIN = 20
-        private const val MENU_PILL_ELEVATION = 2
         private const val MENU_PILL_SPACING_MARGIN = 4
         private const val HANDLE_WIDTH = 80
         private const val APP_NAME = "Test App"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index a6b0770..0798613 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -559,6 +559,17 @@
     }
 
     @Test
+    fun testClose() = runOnUiThread {
+        verify(mockDisplayController, times(1))
+            .addDisplayWindowListener(eq(taskPositioner))
+
+        taskPositioner.close()
+
+        verify(mockDisplayController, times(1))
+            .removeDisplayWindowListener(eq(taskPositioner))
+    }
+
+    @Test
     fun testIsResizingOrAnimatingResizeSet() = runOnUiThread {
         Assert.assertFalse(taskPositioner.isResizingOrAnimating)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index fa3d3e4..011c8f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -52,7 +52,7 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 
@@ -216,7 +216,7 @@
 
         veil.hideVeil()
 
-        verifyZeroInteractions(mockTransaction)
+        verifyNoMoreInteractions(mockTransaction)
     }
 
     @Test
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 9a2e2fa..2e95a97 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
@@ -826,6 +826,18 @@
     }
 
     @Test
+    public void testClose_withTaskDragResizerSet_callResizerClose() {
+        final TestWindowDecoration windowDecor = createWindowDecoration(
+                new TestRunningTaskInfoBuilder().build());
+        final TaskDragResizer taskDragResizer = mock(TaskDragResizer.class);
+        windowDecor.setTaskDragResizer(taskDragResizer);
+
+        windowDecor.close();
+
+        verify(taskDragResizer).close();
+    }
+
+    @Test
     public void testRelayout_captionFrameChanged_insetsReapplied() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
index c8ccac3..714d062 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -54,7 +54,7 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 /**
@@ -125,7 +125,7 @@
 
         loader.getName(task)
 
-        verifyZeroInteractions(
+        verifyNoMoreInteractions(
             mockPackageManager,
             mockIconProvider,
             mockHeaderIconFactory,
@@ -165,7 +165,7 @@
 
         loader.getHeaderIcon(task)
 
-        verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+        verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
     }
 
     @Test
@@ -187,7 +187,7 @@
 
         loader.getVeilIcon(task)
 
-        verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+        verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
     }
 
     @Test
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index f8eb418..496ba50 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -186,3 +186,11 @@
     bug: "398254728"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "gnss_assistance_interface_jni"
+    namespace: "location"
+    description: "Flag for GNSS assistance interface JNI"
+    bug: "209078566"
+}
+
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b57476f..6e0821f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -183,7 +183,17 @@
             appContext.registerReceiver(new VolumeChangeReceiver(),
                     new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
 
-            mDisplayService.registerDisplayListener(this, mHandler);
+            if (com.android.server.display.feature.flags.Flags
+                    .displayListenerPerformanceImprovements()
+                    && com.android.server.display.feature.flags.Flags
+                    .delayImplicitRrRegistrationUntilRrAccessed()) {
+                mDisplayService.registerDisplayListener(this, mHandler,
+                        DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+                                | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+                                | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
+            } else {
+                mDisplayService.registerDisplayListener(this, mHandler);
+            }
 
             AudioRoutesInfo newAudioRoutes = null;
             try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
index 6089f42..f65c7ef 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
@@ -28,7 +28,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.companion.virtual.VirtualDeviceManager;
@@ -56,7 +56,7 @@
         audioManager.playSoundEffect(FX_KEY_CLICK);
 
         // We expect no interactions with VDM when running on default device.
-        verifyZeroInteractions(mockVdm);
+        verifyNoMoreInteractions(mockVdm);
     }
 
     @Test
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index b2c1e60..964268e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -65,6 +65,7 @@
 import android.net.MacAddress;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.text.Spanned;
@@ -621,8 +622,10 @@
             Slog.w(TAG, "Already selected.");
             return;
         }
-        // Notify the adapter to highlight the selected item.
-        mDeviceAdapter.setSelectedPosition(position);
+        // Delay highlighting the selected item by posting to the main thread.
+        // This helps avoid flicker in the user consent dialog after device selection.
+        new Handler(
+                Looper.getMainLooper()).post(() -> mDeviceAdapter.setSelectedPosition(position));
 
         mSelectedDevice = requireNonNull(selectedDevice);
 
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 5033101..716845c 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -23,6 +23,28 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "options":[
         {
@@ -120,6 +142,28 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "options":[
         {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 472ffa9..6dec2f9 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.datastore
 
 import android.content.SharedPreferences
+import android.util.Log
 
 /** Interface of key-value store. */
 interface KeyValueStore : KeyedObservable<String> {
@@ -80,6 +81,27 @@
     fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
 }
 
+/** Delegation of [KeyValueStore]. */
+interface KeyValueStoreDelegate : KeyValueStore, KeyedObservableDelegate<String> {
+
+    /** [KeyValueStore] to delegate. */
+    val keyValueStoreDelegate: KeyValueStore
+
+    override val keyedObservableDelegate
+        get() = keyValueStoreDelegate
+
+    override fun contains(key: String) = keyValueStoreDelegate.contains(key)
+
+    override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
+        keyValueStoreDelegate.getDefaultValue(key, valueType)
+
+    override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+        keyValueStoreDelegate.getValue(key, valueType) ?: getDefaultValue(key, valueType)
+
+    override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+        keyValueStoreDelegate.setValue(key, valueType, value)
+}
+
 /** [SharedPreferences] based [KeyValueStore]. */
 interface SharedPreferencesKeyValueStore : KeyValueStore {
 
@@ -103,11 +125,11 @@
 
     @Suppress("UNCHECKED_CAST")
     override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+        val edit = sharedPreferences.edit()
         if (value == null) {
-            sharedPreferences.edit().remove(key).apply()
+            edit.remove(key).apply()
             return
         }
-        val edit = sharedPreferences.edit()
         when (valueType) {
             Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
             Float::class.javaObjectType -> edit.putFloat(key, value as Float)
@@ -115,7 +137,7 @@
             Long::class.javaObjectType -> edit.putLong(key, value as Long)
             String::class.javaObjectType -> edit.putString(key, value as String)
             Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
-            else -> {}
+            else -> Log.e(LOG_TAG, "Unsupported $valueType for $key: $value")
         }
         edit.apply()
     }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 07b1c9e..ff58bf7 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -116,8 +116,28 @@
 }
 
 /** Delegation of [KeyedObservable]. */
-open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
-    KeyedObservable<K> by delegate
+interface KeyedObservableDelegate<K> : KeyedObservable<K> {
+
+    /** [KeyedObservable] to delegate. */
+    val keyedObservableDelegate: KeyedObservable<K>
+
+    override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean =
+        keyedObservableDelegate.addObserver(observer, executor)
+
+    override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean =
+        keyedObservableDelegate.addObserver(key, observer, executor)
+
+    override fun removeObserver(observer: KeyedObserver<K?>): Boolean =
+        keyedObservableDelegate.removeObserver(observer)
+
+    override fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean =
+        keyedObservableDelegate.removeObserver(key, observer)
+
+    override fun notifyChange(reason: Int): Unit = keyedObservableDelegate.notifyChange(reason)
+
+    override fun notifyChange(key: K, reason: Int): Unit =
+        keyedObservableDelegate.notifyChange(key, reason)
+}
 
 /** A thread safe implementation of [KeyedObservable]. */
 open class KeyedDataObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
new file mode 100644
index 0000000..fdde3d3
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settingslib.devicestate
+
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+
+/**
+ * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
+ *
+ * It provides methods to register/unregister listeners for setting changes, update the setting for
+ * specific device states, retrieve the setting value, and check if rotation is locked for specific
+ * or all device states.
+ */
+interface DeviceStateAutoRotateSettingManager {
+    // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
+
+    /** Listener for changes in device-state based auto rotate setting. */
+    interface DeviceStateAutoRotateSettingListener {
+        /** Called whenever the setting has changed. */
+        fun onSettingsChanged()
+    }
+
+    /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+    fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+    /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+    fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+    /**
+     * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK] setting.
+     */
+    fun updateSetting(deviceState: Int, autoRotate: Boolean)
+
+    /** Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. */
+    fun getRotationLockSetting(deviceState: Int): Int
+
+    /** Returns true if auto-rotate setting is OFF for [deviceState]. */
+    fun isRotationLocked(deviceState: Int): Boolean
+
+    /** Returns true if the auto-rotate setting value for all device states is OFF. */
+    fun isRotationLockedForAllStates(): Boolean
+
+    /** Returns a list of device states and their respective auto rotate setting availability. */
+    fun getSettableDeviceStates(): List<SettableDeviceState>
+}
+
+/** Represents a device state and whether it has an auto-rotation setting. */
+data class SettableDeviceState(
+    /** Returns the device state associated with this object. */
+    val deviceState: Int,
+    /** Returns whether there is an auto-rotation setting for this device state. */
+    val isSettable: Boolean
+)
+
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
new file mode 100644
index 0000000..0b6c6e2
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settingslib.devicestate
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
+ * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
+ * is enabled.
+ */
+class DeviceStateAutoRotateSettingManagerImpl(
+    context: Context,
+    backgroundExecutor: Executor,
+    private val secureSettings: SecureSettings,
+    private val mainHandler: Handler,
+    private val posturesHelper: PosturesHelper,
+) : DeviceStateAutoRotateSettingManager {
+    // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
+
+    private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
+        mutableListOf()
+    private val fallbackPostureMap = SparseIntArray()
+    private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
+
+    private val autoRotateSettingValue: String
+        get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
+
+    init {
+        loadAutoRotateDeviceStates(context)
+        val contentObserver =
+            object : ContentObserver(mainHandler) {
+                override fun onChange(selfChange: Boolean) = notifyListeners()
+            }
+        backgroundExecutor.execute {
+            secureSettings.registerContentObserver(
+                DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
+            )
+        }
+    }
+
+    override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
+        settingListeners.add(settingListener)
+    }
+
+    override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
+        if (!settingListeners.remove(settingListener)) {
+            Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
+        }
+    }
+
+    override fun getRotationLockSetting(deviceState: Int): Int {
+        val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
+        val serializedSetting = autoRotateSettingValue
+        val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
+
+        // If the setting is ignored for this posture, check the fallback posture.
+        if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            val fallbackPosture =
+                fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
+            return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
+        }
+
+        return autoRotateSetting
+    }
+
+    override fun isRotationLocked(deviceState: Int) =
+        getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
+
+    override fun isRotationLockedForAllStates(): Boolean =
+        convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
+            value == DEVICE_STATE_ROTATION_LOCK_LOCKED
+        }
+
+    override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
+
+    override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
+        // TODO: b/350946537 - Create IPC to update the setting, and call it here.
+        throw UnsupportedOperationException("API updateSetting is not implemented yet")
+    }
+
+    private fun notifyListeners() =
+        settingListeners.forEach { listener -> listener.onSettingsChanged() }
+
+    private fun loadAutoRotateDeviceStates(context: Context) {
+        val perDeviceStateAutoRotateDefaults =
+            context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+        for (entry in perDeviceStateAutoRotateDefaults) {
+            entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
+                if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
+                    fallbackPostureMap.put(posture, fallbackPosture)
+                }
+                settableDeviceState.add(
+                    SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
+                )
+            }
+        }
+    }
+
+    private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
+        if (serializedSetting.isEmpty()) return emptyMap()
+        return try {
+            serializedSetting
+                .split(SEPARATOR_REGEX)
+                .hasEvenSize()
+                .chunked(2)
+                .mapNotNull(::parsePostureSettingPair)
+                .toMap()
+        } catch (e: Exception) {
+            Log.w(
+                TAG,
+                "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
+            )
+            return emptyMap()
+        }
+    }
+
+    private fun List<String>.hasEvenSize(): List<String> {
+        if (this.size % 2 != 0) {
+            throw IllegalStateException("Odd number of elements in the list")
+        }
+        return this
+    }
+
+    private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
+        return settingPair.let { (keyStr, valueStr) ->
+            val key = keyStr.toIntOrNull()
+            val value = valueStr.toIntOrNull()
+            if (key != null && value != null && value in 0..2) {
+                key to value
+            } else {
+                Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
+                null // Invalid pair, skip it
+            }
+        }
+    }
+
+    private fun extractSettingForDevicePosture(
+        devicePosture: Int,
+        serializedSetting: String
+    ): Int =
+        convertSerializedSettingToMap(serializedSetting)[devicePosture]
+            ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
+
+    private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
+        val values = split(SEPARATOR_REGEX)
+        if (values.size !in 2..3) { // It should contain 2 or 3 values.
+            Log.w(TAG, "Invalid number of values in entry: '$this'")
+            return null
+        }
+        return try {
+            val posture = values[0].toInt()
+            val rotationLockSetting = values[1].toInt()
+            val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
+            Triple(posture, rotationLockSetting, fallbackPosture)
+        } catch (e: NumberFormatException) {
+            Log.w(TAG, "Invalid number format in '$this': ${e.message}")
+            null
+        }
+    }
+
+    companion object {
+        private const val TAG = "DeviceStateAutoRotate"
+        private const val SEPARATOR_REGEX = ":"
+    }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
new file mode 100644
index 0000000..4d1d292
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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:JvmName("DeviceStateAutoRotateSettingUtils")
+
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import com.android.internal.R
+
+/** Returns true if device-state based rotation lock settings are enabled. */
+object DeviceStateAutoRotateSettingUtils {
+    @JvmStatic
+    fun isDeviceStateRotationLockEnabled(context: Context) =
+        context.resources
+            .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+            .isNotEmpty()
+}
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 635f690..deeba57 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,6 +20,8 @@
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
 
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
+
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -43,7 +45,6 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -58,7 +59,7 @@
     private static DeviceStateRotationLockSettingsManager sSingleton;
 
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
-    private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+    private final Set<DeviceStateAutoRotateSettingListener> mListeners = new HashSet<>();
     private final SecureSettings mSecureSettings;
     private final PosturesHelper mPosturesHelper;
     private String[] mPostureRotationLockDefaults;
@@ -127,20 +128,20 @@
     }
 
     /**
-     * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+     * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
      * change. Can be called multiple times with different listeners.
      */
-    public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+    public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
         mListeners.add(runnable);
     }
 
     /**
-     * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+     * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
      * was never registered.
      */
     public void unregisterListener(
-            DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
-        if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+            DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+        if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
             Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
         }
     }
@@ -379,56 +380,8 @@
     }
 
     private void notifyListeners() {
-        for (DeviceStateRotationLockSettingsListener r : mListeners) {
+        for (DeviceStateAutoRotateSettingListener r : mListeners) {
             r.onSettingsChanged();
         }
     }
-
-    /** Listener for changes in device-state based rotation lock settings */
-    public interface DeviceStateRotationLockSettingsListener {
-        /** Called whenever the settings have changed. */
-        void onSettingsChanged();
-    }
-
-    /** Represents a device state and whether it has an auto-rotation setting. */
-    public static class SettableDeviceState {
-        private final int mDeviceState;
-        private final boolean mIsSettable;
-
-        SettableDeviceState(int deviceState, boolean isSettable) {
-            mDeviceState = deviceState;
-            mIsSettable = isSettable;
-        }
-
-        /** Returns the device state associated with this object. */
-        public int getDeviceState() {
-            return mDeviceState;
-        }
-
-        /** Returns whether there is an auto-rotation setting for this device state. */
-        public boolean isSettable() {
-            return mIsSettable;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof SettableDeviceState)) return false;
-            SettableDeviceState that = (SettableDeviceState) o;
-            return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mDeviceState, mIsSettable);
-        }
-
-        @Override
-        public String toString() {
-            return "SettableDeviceState{"
-                    + "mDeviceState=" + mDeviceState
-                    + ", mIsSettable=" + mIsSettable
-                    + '}';
-        }
-    }
 }
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 1052873..ea40e14 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
@@ -19,7 +19,7 @@
 import android.database.ContentObserver;
 
 /** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
-interface SecureSettings {
+public interface SecureSettings {
 
     void putStringForUser(String name, String value, int userHandle);
 
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 7bd4b3f..9d4c5c2 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -113,8 +113,6 @@
             mSwitch.setOnCheckedChangeListener(this);
         }
 
-        setChecked(mSwitch.isChecked());
-
         if (attrs != null) {
             final TypedArray a = context.obtainStyledAttributes(attrs,
                     androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
@@ -130,8 +128,6 @@
             }
             a.recycle();
         }
-
-        setBackground(mSwitch.isChecked());
     }
 
     @Override
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
index 686c148..112a69b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
@@ -262,4 +262,30 @@
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
     </style>
+
+    <style name="SettingsLibEntityHeaderContent">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_centerHorizontal">true</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:gravity">center_horizontal</item>
+    </style>
+
+    <style name="SettingsLibEntityHeaderIcon">
+        <item name="android:layout_width">@dimen/settingslib_expressive_space_large3</item>
+        <item name="android:layout_height">@dimen/settingslib_expressive_space_large3</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:antialias">true</item>
+    </style>
+
+    <style name="SettingsLibEntityHeaderTitle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:singleLine">false</item>
+        <item name="android:gravity">center</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:textDirection">locale</item>
+        <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index fe8e8b6..6d02c5d 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.widget;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -46,6 +48,9 @@
  */
 public class SliderPreference extends Preference {
     private static final String TAG = "SliderPreference";
+    public static final int HAPTIC_FEEDBACK_MODE_NONE = 0;
+    public static final int HAPTIC_FEEDBACK_MODE_ON_TICKS = 1;
+    public static final int HAPTIC_FEEDBACK_MODE_ON_ENDS = 2;
 
     private final int mTextStartId;
     private final int mTextEndId;
@@ -71,6 +76,8 @@
     private int mMin;
     private int mMax;
     private int mSliderIncrement;
+    private int mHapticFeedbackMode = HAPTIC_FEEDBACK_MODE_NONE;
+    private boolean mTickVisible = false;
     private boolean mAdjustable;
     private boolean mTrackingTouch;
     private CharSequence mSliderContentDescription;
@@ -265,6 +272,7 @@
         }
         if (mSliderIncrement != 0) {
             mSlider.setStepSize(mSliderIncrement);
+            mSlider.setTickVisible(mTickVisible);
         } else {
             mSliderIncrement = (int) (mSlider.getStepSize());
         }
@@ -442,6 +450,29 @@
     }
 
     /**
+     * Sets the haptic feedback mode. HAPTIC_FEEDBACK_MODE_ON_TICKS means to perform haptic feedback
+     * as the {@link Slider} value is updated; HAPTIC_FEEDBACK_MODE_ON_ENDS means to perform haptic
+     * feedback as the {@link Slider} value is equal to the min/max value.
+     *
+     * @param hapticFeedbackMode The haptic feedback mode.
+     */
+    public void setHapticFeedbackMode(int hapticFeedbackMode) {
+        mHapticFeedbackMode = hapticFeedbackMode;
+    }
+
+    /**
+     * Sets whether the tick marks are visible. Only used when the slider is in discrete mode.
+     *
+     * @param tickVisible The visibility of tick marks.
+     */
+    public void setTickVisible(boolean tickVisible) {
+        if (tickVisible != mTickVisible) {
+            mTickVisible = tickVisible;
+            notifyChanged();
+        }
+    }
+
+    /**
      * Gets whether the current {@link Slider} value is displayed to the user.
      *
      * @return Whether the current {@link Slider} value is displayed to the user
@@ -519,7 +550,16 @@
         if (sliderValue != mSliderValue) {
             if (callChangeListener(sliderValue)) {
                 setValueInternal(sliderValue, false);
-                // TODO: mHapticFeedbackMode
+                switch (mHapticFeedbackMode) {
+                    case HAPTIC_FEEDBACK_MODE_ON_TICKS:
+                        slider.performHapticFeedback(CLOCK_TICK);
+                        break;
+                    case HAPTIC_FEEDBACK_MODE_ON_ENDS:
+                        if (mSliderValue == mMax || mSliderValue == mMin) {
+                            slider.performHapticFeedback(CLOCK_TICK);
+                        }
+                        break;
+                }
             } else {
                 slider.setValue(mSliderValue);
             }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 03cb1ff..1297aa3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1510,6 +1510,9 @@
     <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
     <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
 
+    <!-- Warning message when the bluetooth key is missing. [CHAR_LIMIT=NONE] -->
+    <string name="bluetooth_key_missing_subtext">Can’t connect</string>
+
     <!-- Name of the 3.5mm audio device. [CHAR LIMIT=40] -->
     <string name="media_transfer_wired_device_name">Wired audio device</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 9d97901..bf6006b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -151,6 +151,18 @@
                         UserHandle.myUserId());
     }
 
+    /**
+     * Configures the user restriction that this preference will track. This is equivalent to
+     * specifying {@link R.styleable#RestrictedPreference_userRestriction} in XML and allows
+     * configuring user restriction at runtime.
+     */
+    public void setUserRestriction(@Nullable String userRestriction) {
+        mAttrUserRestriction = userRestriction == null ||
+            RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, userRestriction,
+                UserHandle.myUserId()) ? null : userRestriction;
+        setDisabledByAdmin(checkRestrictionEnforced());
+    }
+
     public void useAdminDisabledSummary(boolean useSummary) {
         mDisabledSummary = useSummary;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 2fdf2ac..ae9ad95 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -703,18 +703,18 @@
             // However, app layer need to gate the feature based on whether the device has audio
             // sharing capability regardless of the BT state.
             // So here we check the BluetoothProperties when BT off.
-            String mode = BluetoothProperties.le_audio_dynamic_switcher_mode().orElse("none");
-            Set<String> disabledModes = ImmutableSet.of("disabled", "unicast");
+            //
+            // TODO: Also check SystemProperties "persist.bluetooth.leaudio_dynamic_switcher.mode"
+            // and return true if it is in broadcast mode.
+            // Now SystemUI don't have access to read the value.
             int sourceSupportedCode = adapter.isLeAudioBroadcastSourceSupported();
             int assistantSupportedCode = adapter.isLeAudioBroadcastAssistantSupported();
             return (sourceSupportedCode == BluetoothStatusCodes.FEATURE_SUPPORTED
                     || (sourceSupportedCode == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
-                    && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)
-                    && !disabledModes.contains(mode)))
+                    && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)))
                     && (assistantSupportedCode == BluetoothStatusCodes.FEATURE_SUPPORTED
                     || (assistantSupportedCode == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
-                    && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false)
-                    && !disabledModes.contains(mode)));
+                    && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false)));
         } catch (IllegalStateException e) {
             Log.d(TAG, "Fail to check isAudioSharingSupported, e = ", e);
             return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 5f88bcd..011b2fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1495,6 +1495,11 @@
         int leftBattery = -1;
         int rightBattery = -1;
 
+        Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mDevice);
+        if (keyMissingCount != null && keyMissingCount > 0) {
+            return mContext.getString(R.string.bluetooth_key_missing_subtext);
+        }
+
         if (isProfileConnectedFail() && isConnected()) {
             return mContext.getString(R.string.profile_connect_timeout_subtext);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index f22bdaf..01d86942 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -724,7 +724,10 @@
             Log.d(TAG, "The BluetoothLeBroadcast is null");
             return null;
         }
-        if (mBluetoothLeBroadcastMetadata == null) {
+        if (mBluetoothLeBroadcastMetadata == null
+                // mBroadcastId is updated when onBroadcastStarted, which is always before
+                // onBroadcastMetadataChanged, so mBroadcastId is always the latest broadcast info
+                || mBluetoothLeBroadcastMetadata.getBroadcastId() != mBroadcastId) {
             final List<BluetoothLeBroadcastMetadata> metadataList =
                     mServiceBroadcast.getAllBroadcastMetadata();
             mBluetoothLeBroadcastMetadata =
@@ -732,6 +735,7 @@
                             .filter(i -> i.getBroadcastId() == mBroadcastId)
                             .findFirst()
                             .orElse(null);
+            Log.d(TAG, "getLatestBluetoothLeBroadcastMetadata for broadcast id " + mBroadcastId);
         }
         return mBluetoothLeBroadcastMetadata;
     }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
new file mode 100644
index 0000000..78dba57
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settingslib.devicestate
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerImplTest {
+    @get:Rule
+    val rule = MockitoJUnit.rule()
+
+    private val fakeSecureSettings = FakeSecureSettings()
+    private val executor: Executor = Executor { it.run() }
+    private val configPerDeviceStateRotationLockDefaults = arrayOf(
+        "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:" +
+                "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+                "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+        "$DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY:" +
+                "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+                "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+        "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+        "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+    )
+
+    @Mock
+    private lateinit var mockContext: Context
+
+    @Mock
+    private lateinit var mockContentResolver: ContentResolver
+
+    @Mock
+    private lateinit var mockPosturesHelper: PosturesHelper
+
+    @Mock
+    private lateinit var mockHandler: Handler
+
+    @Mock
+    private lateinit var mockDeviceStateManager: DeviceStateManager
+
+    @Mock
+    private lateinit var mockResources: Resources
+    private lateinit var settingManager: DeviceStateAutoRotateSettingManagerImpl
+
+    @Before
+    fun setUp() {
+        whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+        whenever(mockContext.resources).thenReturn(mockResources)
+        whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+            .thenReturn(configPerDeviceStateRotationLockDefaults)
+        whenever(mockHandler.post(any(Runnable::class.java))).thenAnswer { invocation ->
+            val runnable = invocation.arguments[0] as Runnable
+            runnable.run()
+            null
+        }
+        whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+            .thenReturn(mockDeviceStateManager)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_UNFOLDED))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_FOLDED))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_INVALID))
+            .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+
+        settingManager =
+            DeviceStateAutoRotateSettingManagerImpl(
+                mockContext,
+                executor,
+                fakeSecureSettings,
+                mockHandler,
+                mockPosturesHelper,
+            )
+    }
+
+    @Test
+    fun registerListener_onSettingsChanged_listenerNotified() {
+        val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+        settingManager.registerListener(listener)
+
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        verify(listener).onSettingsChanged()
+    }
+
+    @Test
+    fun registerMultipleListeners_onSettingsChanged_allListenersNotified() {
+        val listener1 = mock(DeviceStateAutoRotateSettingListener::class.java)
+        val listener2 = mock(DeviceStateAutoRotateSettingListener::class.java)
+        settingManager.registerListener(listener1)
+        settingManager.registerListener(listener2)
+
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        verify(listener1).onSettingsChanged()
+        verify(listener2).onSettingsChanged()
+    }
+
+    @Test
+    fun unregisterListener_onSettingsChanged_listenerNotNotified() {
+        val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+        settingManager.registerListener(listener)
+        settingManager.unregisterListener(listener)
+
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        verify(listener, never()).onSettingsChanged()
+    }
+
+    @Test
+    fun getAutoRotateSetting_offForUnfolded_returnsOff() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_onForFolded_returnsOn() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_forInvalidPostureWithNoFallback_returnsIgnored() {
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_INVALID)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+        persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_invalidFormat_returnsIgnored() {
+        persistSettings("invalid_format")
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_invalidNumberFormat_returnsIgnored() {
+        persistSettings("$DEVICE_STATE_ROTATION_KEY_FOLDED:4")
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_multipleSettings_returnsCorrectSetting() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+        )
+
+        val foldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+        val unfoldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+        assertThat(foldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+        assertThat(unfoldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+    }
+
+    @Test
+    fun isAutoRotateOff_offForUnfolded_returnsTrue() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        val isAutoRotateOff = settingManager.isRotationLocked(DEVICE_STATE_UNFOLDED)
+
+        assertThat(isAutoRotateOff).isTrue()
+    }
+
+    @Test
+    fun isRotationLockedForAllStates_allStatesLocked_returnsTrue() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+        )
+
+        val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+        assertThat(isRotationLockedForAllStates).isTrue()
+    }
+
+    @Test
+    fun isRotationLockedForAllStates_someStatesLocked_returnsFalse() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+        )
+
+        val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+        assertThat(isRotationLockedForAllStates).isFalse()
+    }
+
+    @Test
+    fun isRotationLockedForAllStates_noStatesLocked_returnsFalse() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+        )
+
+        val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+        assertThat(isRotationLockedForAllStates).isFalse()
+    }
+
+    @Test
+    fun getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+        val settableDeviceStates = settingManager.getSettableDeviceStates()
+
+        assertThat(settableDeviceStates)
+            .containsExactly(
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED, isSettable = true),
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
+                SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
+            )
+    }
+
+    private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
+        persistSettings("$devicePosture:$autoRotateSetting")
+    }
+
+    private fun persistSettings(value: String) {
+        fakeSecureSettings.putStringForUser(
+            Settings.Secure.DEVICE_STATE_ROTATION_LOCK, value, UserHandle.USER_CURRENT
+        )
+    }
+
+    private companion object {
+        const val DEVICE_STATE_FOLDED = 0
+        const val DEVICE_STATE_HALF_FOLDED = 1
+        const val DEVICE_STATE_UNFOLDED = 2
+        const val DEVICE_STATE_REAR_DISPLAY = 3
+        const val DEVICE_STATE_INVALID = 4
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 9f9aaf5..baebaf7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -40,7 +40,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
 
 import com.google.common.truth.Expect;
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index fc61b1e..d3291b4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -118,6 +118,8 @@
         Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
         Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
         Settings.Global.Wearable.AUTO_BEDTIME_MODE,
+        Settings.Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE,
+        Settings.Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE,
         Settings.Global.FORCE_ENABLE_PSS_PROFILING,
         Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
         Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 0121d31..829d4cb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -300,5 +300,9 @@
         Settings.Secure.DUAL_SHADE,
         Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED,
         Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED,
+        Settings.Secure.SPELL_CHECKER_ENABLED,
+        Settings.Secure.SELECTED_SPELL_CHECKER,
+        // SELECTED_SPELL_CHECKER_SUBTYPE needs to be restored after SELECTED_SPELL_CHECKER
+        Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index cf0447f..98f5fac 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -124,7 +124,8 @@
                 Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
                 Settings.System.NOTIFICATION_COOLDOWN_ALL,
                 Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-                Settings.System.PREFERRED_REGION
+                Settings.System.PREFERRED_REGION,
+                Settings.System.CV_ENABLED
         ));
         if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
             settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4c6a1ba..cd6521f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -474,5 +474,7 @@
                                 String.valueOf(
                                         Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
                         }));
+        VALIDATORS.put(Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 5eb6af6..d0f8462 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -472,5 +472,8 @@
         VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.BROWSER_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SEARCH_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SPELL_CHECKER_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER, NULLABLE_COMPONENT_NAME_VALIDATOR);
+        VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER_SUBTYPE, ANY_INTEGER_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4f649ed..3a58440 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -271,5 +271,7 @@
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(System.CV_ENABLED,
+                new InclusiveIntegerRangeValidator(0, 1));
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
index b0086c1..78c87b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -1,2 +1,2 @@
-per-file WritableNamespacePrefixes.java = mpgroover@google.com,tedbauer@google.com
-per-file WritableNamespaces.java = mpgroover@google.com,tedbauer@google.com
+per-file WritableNamespacePrefixes.java = mpgroover@google.com
+per-file WritableNamespaces.java = mpgroover@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index fc402d4..41ec621 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -37,10 +37,12 @@
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupRestoreEventLogger;
 import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
 import android.database.Cursor;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
@@ -941,6 +943,7 @@
         Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
 
         int restoredSettingsCount = 0;
+        boolean selectedSpellCheckerRestored = false;
         for (String key : allowlist.mSettingsAllowlist) {
             boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
             if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri,  key)) {
@@ -1068,6 +1071,25 @@
                     }
                     continue;
                 }
+            } else if (Settings.Secure.SELECTED_SPELL_CHECKER.equals(key)) {
+                ServiceInfo si = getServiceInfoOrNull(value);
+                if (si == null || si.applicationInfo == null) {
+                    Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+                            + "as it is not installed");
+                    continue;
+                } else if (!si.applicationInfo.isSystemApp()
+                        && !si.applicationInfo.isUpdatedSystemApp()) {
+                    Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+                            + "as it is not a system app");
+                    continue;
+                }
+                selectedSpellCheckerRestored = true;
+            } else if (Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE.equals(key)) {
+                if (!selectedSpellCheckerRestored) {
+                    Log.i(TAG, "Skipping restore for setting selected_spell_checker_subtype "
+                            + "as selected_spell_checker was not restored");
+                    continue;
+                }
             }
 
             if (Settings.System.FONT_SCALE.equals(key)) {
@@ -1868,6 +1890,18 @@
         return result;
     }
 
+    @Nullable
+    private ServiceInfo getServiceInfoOrNull(@Nullable String flattenedServiceName) {
+        if (flattenedServiceName == null) return null;
+        ComponentName componentName = ComponentName.unflattenFromString(flattenedServiceName);
+        if (componentName == null) return null;
+        try {
+            return getBaseContext().getPackageManager().getServiceInfo(componentName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
     /**
      * Store the allowlist of settings to be backed up and validators for them.
      */
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e7527dc..584b21a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -3157,6 +3157,12 @@
                 SystemSettingsProto.Volume.MASTER_BALANCE);
         p.end(volumeToken);
 
+        final long systemDisplayToken = p.start(SystemSettingsProto.DISPLAY);
+        dumpSetting(s, p,
+                Settings.System.CV_ENABLED,
+                SystemSettingsProto.Display.CV_ENABLED);
+        p.end(systemDisplayToken);
+
         dumpSetting(s, p,
                 Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
                 SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 70c042c..3148f22 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -749,15 +749,12 @@
                  Settings.Secure.SECURE_FRP_MODE,
                  Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
                  Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
-                 Settings.Secure.SELECTED_SPELL_CHECKER,  // Intentionally removed in Q
-                 Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,  // Intentionally removed in Q
                  Settings.Secure.SETTINGS_CLASSNAME,
                  Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
                  Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
                  Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
                  Settings.Secure.SLEEP_TIMEOUT,
                  Settings.Secure.SMS_DEFAULT_APPLICATION,
-                 Settings.Secure.SPELL_CHECKER_ENABLED,  // Intentionally removed in Q
                  Settings.Secure.TRUST_AGENTS_INITIALIZED,
                  Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED,
                  Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index fb0678f..5bba99f 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -255,11 +255,11 @@
     /** Always keep remote bugreport files created in the last day. */
     private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
 
-    /** Minimum delay for sending last update notification */
-    private static final int DELAY_NOTIFICATION_MS = 250;
-
     private final Object mLock = new Object();
 
+/** Minimum delay between percentage points before sending an update notification */
+    private static final int MIN_NOTIFICATION_GAP = 10;
+
     /** Managed bugreport info (keyed by id) */
     @GuardedBy("mLock")
     private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
@@ -1460,17 +1460,6 @@
      * Sends a notification indicating the bugreport has finished so use can share it.
      */
     private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) {
-
-        final long lastUpdate = System.currentTimeMillis() - info.lastUpdate.longValue();
-        if (lastUpdate < DELAY_NOTIFICATION_MS) {
-            Log.d(TAG, "Delaying final notification for "
-                    + (DELAY_NOTIFICATION_MS - lastUpdate) + " ms ");
-            mMainThreadHandler.postDelayed(() -> {
-                sendBugreportNotification(info, takingScreenshot);
-            }, DELAY_NOTIFICATION_MS - lastUpdate);
-            return;
-        }
-
         // Since adding the details can take a while, do it before notifying user.
         addDetailsToZipFile(info);
 
@@ -1523,7 +1512,7 @@
             builder.setSubText(info.getName());
         }
 
-        Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+        Log.d(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
         NotificationManager.from(mContext).notify(info.id, builder.build());
     }
 
@@ -2753,6 +2742,11 @@
         if (progress > CAPPED_PROGRESS) {
             progress = CAPPED_PROGRESS;
         }
+
+        if ((progress - info.lastProgress.intValue()) < MIN_NOTIFICATION_GAP) {
+            return;
+        }
+
         if (DEBUG) {
             if (progress != info.progress.intValue()) {
                 Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5b48566..129949f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -534,6 +534,7 @@
         "androidx.compose.animation_animation-graphics",
         "androidx.lifecycle_lifecycle-viewmodel-compose",
         "kairos",
+        "displaylib",
         "aconfig_settings_flags_lib",
     ],
     libs: [
@@ -728,6 +729,7 @@
         "Traceur-res",
         "aconfig_settings_flags_lib",
         "kairos",
+        "displaylib",
     ],
 }
 
@@ -770,6 +772,7 @@
         "androidx.compose.runtime_runtime",
         "kairos",
         "kosmos",
+        "displaylib",
         "testables",
         "androidx.test.rules",
         "platform-compat-test-rules",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 7172619..1543dbe 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -46,7 +46,7 @@
 import android.media.AudioManager;
 import android.os.PowerManager;
 import android.os.UserManager;
-import android.platform.uiautomator_helpers.WaitUtils;
+import android.platform.uiautomatorhelpers.WaitUtils;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 3cb3025..c6bc1c7 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -155,3 +155,13 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "hearing_devices_input_routing_ui_improvement"
+    namespace: "accessibility"
+    description: "UI improvement for hearing device input routing feature"
+    bug: "397314200"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ab18612..4693377 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -213,18 +213,6 @@
 }
 
 flag {
-    name: "notification_undo_guts_on_config_changed"
-    namespace: "systemui"
-    description: "Fixes a bug where a theme or font change while notification guts were open"
-        " (e.g. the snooze options or notification info) would show an empty notification by"
-        " closing the guts and undoing changes."
-    bug: "379267630"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
    name: "pss_app_selector_recents_split_screen"
    namespace: "systemui"
    description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 694fc8e..ca94482 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -22,6 +22,7 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
@@ -261,7 +262,8 @@
             SurfaceControl.Transaction startTransaction
     ) {
         checkArgument(isOpeningMode(launcherChange.getMode()));
-        if (!isClosingType(info.getType())) {
+        if (!isClosingType(info.getType())
+                && !ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX.isTrue()) {
             return;
         }
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index a352b1e..82e5f5b 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -21,7 +21,6 @@
 import android.view.ViewGroup
 import android.view.ViewGroupOverlay
 import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -62,6 +61,7 @@
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.graphics.isSpecified
 import androidx.compose.ui.graphics.layer.GraphicsLayer
 import androidx.compose.ui.graphics.layer.drawLayer
 import androidx.compose.ui.graphics.rememberGraphicsLayer
@@ -82,6 +82,7 @@
 import androidx.lifecycle.setViewTreeViewModelStoreOwner
 import androidx.savedstate.findViewTreeSavedStateRegistryOwner
 import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.compose.modifiers.animatedBackground
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
 import com.android.systemui.animation.ComposableControllerFactory
@@ -291,7 +292,7 @@
                     .updateExpandableSize()
                     .then(minInteractiveSizeModifier)
                     .then(clickModifier(controller, onClick, interactionSource))
-                    .background(color, shape)
+                    .animatedBackground(color, shape = shape)
                     .border(controller)
                     .onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
             ) {
@@ -307,19 +308,27 @@
     contentColor: Color,
     content: @Composable (Expandable) -> Unit,
 ) {
-    CompositionLocalProvider(LocalContentColor provides contentColor) {
-        // We make sure that the content itself (wrapped by the background) is at least 40.dp, which
-        // is the same as the M3 buttons. This applies even if onClick is null, to make it easier to
-        // write expandables that are sometimes clickable and sometimes not. There shouldn't be any
-        // Expandable smaller than 40dp because if the expandable is not clickable directly, then
-        // something in its content should be (and with a size >= 40dp).
-        val minSize = 40.dp
-        Box(
-            Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
-            contentAlignment = Alignment.Center,
-        ) {
-            content(expandable)
+    val minSizeContent =
+        @Composable {
+            // We make sure that the content itself (wrapped by the background) is at least 40.dp,
+            // which is the same as the M3 buttons. This applies even if onClick is null, to make it
+            // easier to write expandables that are sometimes clickable and sometimes not. There
+            // shouldn't be any Expandable smaller than 40dp because if the expandable is not
+            // clickable directly, then something in its content should be (and with a size >=
+            // 40dp).
+            val minSize = 40.dp
+            Box(
+                Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+                contentAlignment = Alignment.Center,
+            ) {
+                content(expandable)
+            }
         }
+
+    if (contentColor.isSpecified) {
+        CompositionLocalProvider(LocalContentColor provides contentColor, content = minSizeContent)
+    } else {
+        minSizeContent()
     }
 }
 
@@ -345,7 +354,7 @@
         .thenIf(drawContent) {
             Modifier.border(controller)
                 .then(clickModifier(controller, onClick, interactionSource))
-                .background(controller.color, controller.shape)
+                .animatedBackground(controller.color, shape = controller.shape)
         }
         .onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
         .drawWithContent {
@@ -422,7 +431,7 @@
             // Background.
             this@draw.drawBackground(
                 state,
-                controller.color,
+                controller.color(),
                 controller.borderStroke,
                 size = Size(state.width.toFloat(), state.height.toFloat()),
             )
@@ -469,7 +478,7 @@
 /** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
 @Composable
 private fun AnimatedContentInOverlay(
-    color: Color,
+    color: () -> Color,
     sizeInOriginalLayout: Size,
     overlay: ViewGroupOverlay,
     controller: ExpandableControllerImpl,
@@ -523,7 +532,7 @@
                                     return@drawWithContent
                                 }
 
-                                drawBackground(animatorState, color, controller.borderStroke)
+                                drawBackground(animatorState, color(), controller.borderStroke)
                                 drawContent()
                             },
                             // We center the content in the expanding container.
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 72da175e..d7d5a48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -26,7 +26,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -80,6 +79,24 @@
     borderStroke: BorderStroke? = null,
     transitionControllerFactory: ComposableControllerFactory? = null,
 ): ExpandableController {
+    return rememberExpandableController(
+        color = { color },
+        shape = shape,
+        contentColor = contentColor,
+        borderStroke = borderStroke,
+        transitionControllerFactory = transitionControllerFactory,
+    )
+}
+
+/** Create an [ExpandableController] to control an [Expandable]. */
+@Composable
+fun rememberExpandableController(
+    color: () -> Color,
+    shape: Shape,
+    contentColor: Color = Color.Unspecified,
+    borderStroke: BorderStroke? = null,
+    transitionControllerFactory: ComposableControllerFactory? = null,
+): ExpandableController {
     val composeViewRoot = LocalView.current
     val density = LocalDensity.current
     val layoutDirection = LocalLayoutDirection.current
@@ -125,7 +142,7 @@
 }
 
 internal class ExpandableControllerImpl(
-    internal val color: Color,
+    internal val color: () -> Color,
     internal val contentColor: Color,
     internal val shape: Shape,
     internal val borderStroke: BorderStroke?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 959f28f..2ea9c48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -95,13 +95,31 @@
      * nested scrollable.
      *
      * This is called whenever a nested scrollable does not consume some scroll amount. If this
-     * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+     * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
      * consume all future events during preScroll until the nested scroll is finished.
      */
-    fun shouldConsumeNestedScroll(sign: Float): Boolean
+    fun shouldConsumeNestedPostScroll(sign: Float): Boolean = true
+
+    /**
+     * Whether this draggable should consume any scroll amount with the given [sign] *before* it can
+     * be consumed by a nested scrollable.
+     *
+     * This is called before a nested scrollable is able to consume that scroll amount. If this
+     * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
+     * consume all future scroll events during preScroll until the nested scroll is finished.
+     */
+    fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false
 
     interface Controller {
         /**
+         * Whether drags that were started from nested scrolls should be automatically
+         * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
+         * [onDrag].
+         */
+        val autoStopNestedDrags: Boolean
+            get() = false
+
+        /**
          * Drag by [delta] pixels.
          *
          * @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
@@ -540,6 +558,14 @@
     }
 
     override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        val sign = available.toFloat().sign
+        maybeCreateNewController(
+            sign = sign,
+            condition = {
+                source == NestedScrollSource.UserInput &&
+                    draggable.shouldConsumeNestedPreScroll(sign)
+            },
+        )
         val controller = nestedScrollController ?: return Offset.Zero
         return scrollWithOverscroll(controller, available)
     }
@@ -560,33 +586,46 @@
         }
 
         val sign = offset.sign
-        if (
-            nestedDragsEnabled &&
-                nestedScrollController == null &&
-                // TODO(b/388231324): Remove this.
-                !lastEventWasScrollWheel &&
-                draggable.shouldConsumeNestedScroll(sign) &&
-                lastFirstDown != null
-        ) {
-            val startedPosition = checkNotNull(lastFirstDown)
-
-            // TODO(b/382665591): Ensure that there is at least one pointer down.
-            val pointersDownCount = pointersDown.size.coerceAtLeast(1)
-            val pointerType = pointersDown.entries.firstOrNull()?.value
-            nestedScrollController =
-                NestedScrollController(
-                    overscrollEffect,
-                    draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
-                )
-        }
-
+        maybeCreateNewController(
+            sign,
+            condition = { draggable.shouldConsumeNestedPostScroll(sign) },
+        )
         val controller = nestedScrollController ?: return Offset.Zero
         return scrollWithOverscroll(controller, available)
     }
 
+    private fun maybeCreateNewController(sign: Float, condition: () -> Boolean) {
+        if (
+            !nestedDragsEnabled ||
+                nestedScrollController != null ||
+                lastEventWasScrollWheel ||
+                lastFirstDown == null ||
+                !condition()
+        ) {
+            return
+        }
+
+        // TODO(b/382665591): Ensure that there is at least one pointer down.
+        val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+        val pointerType = pointersDown.entries.firstOrNull()?.value
+        val startedPosition = checkNotNull(lastFirstDown)
+        nestedScrollController =
+            NestedScrollController(
+                overscrollEffect,
+                draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
+            )
+    }
+
     private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
-        return scrollWithOverscroll(offset) {
-            controller.controller.onDrag(it.toFloat()).toOffset()
+        return scrollWithOverscroll(offset) { delta ->
+            val available = delta.toFloat()
+            val consumed = controller.controller.onDrag(available)
+            if (controller.controller.autoStopNestedDrags && consumed != available) {
+                controller.ensureOnDragStoppedIsCalled()
+                this.nestedScrollController = null
+            }
+
+            consumed.toOffset()
         }
     }
 
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
new file mode 100644
index 0000000..5b1f0a7
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
@@ -0,0 +1,159 @@
+/*
+ * 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.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
+import androidx.compose.ui.node.invalidateDraw
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.LayoutDirection
+
+/**
+ * Draws a background in a given [shape] and with a [color] or [alpha] that can be animated.
+ *
+ * @param color color to paint background with
+ * @param alpha alpha of the background
+ * @param shape desired shape of the background
+ */
+fun Modifier.animatedBackground(
+    color: () -> Color,
+    alpha: () -> Float = DefaultAlpha,
+    shape: Shape = RectangleShape,
+) =
+    this.then(
+        BackgroundElement(
+            color = color,
+            alpha = alpha,
+            shape = shape,
+            inspectorInfo =
+                debugInspectorInfo {
+                    name = "background"
+                    value = color
+                    properties["color"] = color
+                    properties["alpha"] = alpha
+                    properties["shape"] = shape
+                },
+        )
+    )
+
+private val DefaultAlpha = { 1f }
+
+private class BackgroundElement(
+    private val color: () -> Color,
+    private val alpha: () -> Float,
+    private val shape: Shape,
+    private val inspectorInfo: InspectorInfo.() -> Unit,
+) : ModifierNodeElement<BackgroundNode>() {
+    override fun create(): BackgroundNode {
+        return BackgroundNode(color, alpha, shape)
+    }
+
+    override fun update(node: BackgroundNode) {
+        node.color = color
+        node.alpha = alpha
+        node.shape = shape
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        inspectorInfo()
+    }
+
+    override fun hashCode(): Int {
+        var result = color.hashCode()
+        result = 31 * result + alpha.hashCode()
+        result = 31 * result + shape.hashCode()
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        val otherModifier = other as? BackgroundElement ?: return false
+        return color == otherModifier.color &&
+            alpha == otherModifier.alpha &&
+            shape == otherModifier.shape
+    }
+}
+
+private class BackgroundNode(var color: () -> Color, var alpha: () -> Float, var shape: Shape) :
+    DrawModifierNode, Modifier.Node(), ObserverModifierNode {
+
+    // Naively cache outline calculation if input parameters are the same, we manually observe
+    // reads inside shape#createOutline separately
+    private var lastSize: Size = Size.Unspecified
+    private var lastLayoutDirection: LayoutDirection? = null
+    private var lastOutline: Outline? = null
+    private var lastShape: Shape? = null
+    private var tmpOutline: Outline? = null
+
+    override fun ContentDrawScope.draw() {
+        if (shape === RectangleShape) {
+            // shortcut to avoid Outline calculation and allocation
+            drawRect()
+        } else {
+            drawOutline()
+        }
+        drawContent()
+    }
+
+    override fun onObservedReadsChanged() {
+        // Reset cached properties
+        lastSize = Size.Unspecified
+        lastLayoutDirection = null
+        lastOutline = null
+        lastShape = null
+        // Invalidate draw so we build the cache again - this is needed because observeReads within
+        // the draw scope obscures the state reads from the draw scope's observer
+        invalidateDraw()
+    }
+
+    private fun ContentDrawScope.drawRect() {
+        drawRect(color = color(), alpha = alpha())
+    }
+
+    private fun ContentDrawScope.drawOutline() {
+        val outline = getOutline()
+        drawOutline(outline, color = color(), alpha = alpha())
+    }
+
+    private fun ContentDrawScope.getOutline(): Outline {
+        val outline: Outline?
+        if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
+            outline = lastOutline!!
+        } else {
+            // Manually observe reads so we can directly invalidate the outline when it changes
+            // Use tmpOutline to avoid creating an object reference to local var outline
+            observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) }
+            outline = tmpOutline
+            tmpOutline = null
+        }
+        lastOutline = outline
+        lastSize = size
+        lastLayoutDirection = layoutDirection
+        lastShape = shape
+        return outline!!
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
deleted file mode 100644
index 794b7a4..0000000
--- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
+++ /dev/null
@@ -1,113 +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.compose.modifiers
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.DrawModifier
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawOutline
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.LayoutDirection
-
-/**
- * Draws a fading [shape] with a solid [color] and [alpha] behind the content.
- *
- * @param color color to paint background with
- * @param alpha alpha of the background
- * @param shape desired shape of the background
- */
-fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) =
-    this.then(
-        FadingBackground(
-            brush = SolidColor(color),
-            alpha = alpha,
-            shape = shape,
-            inspectorInfo =
-                debugInspectorInfo {
-                    name = "background"
-                    value = color
-                    properties["color"] = color
-                    properties["alpha"] = alpha
-                    properties["shape"] = shape
-                },
-        )
-    )
-
-private class FadingBackground
-constructor(
-    private val brush: Brush,
-    private val shape: Shape,
-    private val alpha: () -> Float,
-    inspectorInfo: InspectorInfo.() -> Unit,
-) : DrawModifier, InspectorValueInfo(inspectorInfo) {
-    // naive cache outline calculation if size is the same
-    private var lastSize: Size? = null
-    private var lastLayoutDirection: LayoutDirection? = null
-    private var lastOutline: Outline? = null
-
-    override fun ContentDrawScope.draw() {
-        if (shape === RectangleShape) {
-            // shortcut to avoid Outline calculation and allocation
-            drawRect()
-        } else {
-            drawOutline()
-        }
-        drawContent()
-    }
-
-    private fun ContentDrawScope.drawRect() {
-        drawRect(brush, alpha = alpha())
-    }
-
-    private fun ContentDrawScope.drawOutline() {
-        val outline =
-            if (size == lastSize && layoutDirection == lastLayoutDirection) {
-                lastOutline!!
-            } else {
-                shape.createOutline(size, layoutDirection, this)
-            }
-        drawOutline(outline, brush = brush, alpha = alpha())
-        lastOutline = outline
-        lastSize = size
-        lastLayoutDirection = layoutDirection
-    }
-
-    override fun hashCode(): Int {
-        var result = brush.hashCode()
-        result = 31 * result + alpha.hashCode()
-        result = 31 * result + shape.hashCode()
-        return result
-    }
-
-    override fun equals(other: Any?): Boolean {
-        val otherModifier = other as? FadingBackground ?: return false
-        return brush == otherModifier.brush &&
-            alpha == otherModifier.alpha &&
-            shape == otherModifier.shape
-    }
-
-    override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
-}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index d8e46ad..b247993 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -971,6 +971,68 @@
         assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
     }
 
+    @Test
+    fun consumeNestedPreScroll() {
+        var consumeNestedPreScroll by mutableStateOf(false)
+        val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
+
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation)
+                        // Always consume everything so that the only way to start the drag is to
+                        // intercept preScroll events.
+                        .scrollable(rememberScrollableState { it }, orientation)
+                )
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy((touchSlop + 1f).toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isFalse()
+
+        consumeNestedPreScroll = true
+        rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+    }
+
+    @Test
+    fun autoStopNestedDrags() {
+        var consumeScrolls by mutableStateOf(true)
+        val draggable =
+            TestDraggable(autoStopNestedDrags = true, onDrag = { if (consumeScrolls) it else 0f })
+
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation)
+                        .scrollable(rememberScrollableState { 0f }, orientation)
+                )
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy((touchSlop + 1f).toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+        assertThat(draggable.onDragStoppedCalled).isFalse()
+
+        rule.onRoot().performTouchInput { moveBy(50f.toOffset()) }
+
+        assertThat(draggable.onDragStoppedCalled).isFalse()
+
+        consumeScrolls = false
+        rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+        assertThat(draggable.onDragStoppedCalled).isTrue()
+    }
+
     private fun ComposeContentTestRule.setContentWithTouchSlop(
         content: @Composable () -> Unit
     ): Float {
@@ -996,7 +1058,9 @@
             { velocity, _ ->
                 velocity
             },
-        private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+        private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
+        private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+        private val autoStopNestedDrags: Boolean = false,
     ) : NestedDraggable {
         var shouldStartDrag = true
         var onDragStartedCalled = false
@@ -1026,6 +1090,8 @@
 
             onDragStarted.invoke(position, sign)
             return object : NestedDraggable.Controller {
+                override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
+
                 override fun onDrag(delta: Float): Float {
                     onDragCalled = true
                     onDragDelta += delta
@@ -1042,8 +1108,12 @@
             }
         }
 
-        override fun shouldConsumeNestedScroll(sign: Float): Boolean {
-            return shouldConsumeNestedScroll.invoke(sign)
+        override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
+            return shouldConsumeNestedPostScroll.invoke(sign)
+        }
+
+        override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
+            return shouldConsumeNestedPreScroll.invoke(sign)
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 216f0a7..7782705 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,7 +73,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.scene.ContentScope
-import com.android.compose.modifiers.fadingBackground
+import com.android.compose.modifiers.animatedBackground
 import com.android.compose.theme.colorAttr
 import com.android.systemui.Flags.notificationShadeBlur
 import com.android.systemui.animation.Expandable
@@ -172,8 +172,8 @@
     val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
     val backgroundModifier =
         remember(backgroundColor, backgroundAlphaValue, backgroundTopRadius) {
-            Modifier.fadingBackground(
-                backgroundColor,
+            Modifier.animatedBackground(
+                { backgroundColor },
                 backgroundAlphaValue,
                 RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius),
             )
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 7015f79..0daef46 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
@@ -101,7 +101,7 @@
         rememberActivated(traceName = "sceneJankMonitor") { sceneJankMonitorFactory.create() }
 
     val hapticFeedback = LocalHapticFeedback.current
-    val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion()
+    val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion(isFullWidthShade())
     val sceneTransitions =
         remember(hapticFeedback, shadeExpansionMotion) {
             transitionsBuilder.build(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 9b45ef6..2f5a030 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -6,7 +6,7 @@
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.transitions
 import com.android.internal.jank.Cuj
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
@@ -50,7 +50,7 @@
  */
 class SceneContainerTransitions : SceneContainerTransitionsBuilder {
     override fun build(
-        shadeExpansionMotion: EdgeContainerExpansionSpec,
+        shadeExpansionMotion: VerticalExpandContainerSpec,
         revealHaptics: ContainerRevealHaptics,
     ): SceneTransitions {
         return transitions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
index eb5548d..4c9c23a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
@@ -19,7 +19,7 @@
 import com.android.compose.animation.scene.SceneTransitions
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 
 /**
  * Builder of the comprehensive definition of all transitions between scenes and overlays in the
@@ -29,7 +29,7 @@
 
     /** Build the [SceneContainer] transitions spec. */
     fun build(
-        shadeExpansionMotion: EdgeContainerExpansionSpec,
+        shadeExpansionMotion: VerticalExpandContainerSpec,
         revealHaptics: ContainerRevealHaptics,
     ): SceneTransitions
 }
@@ -42,7 +42,7 @@
     private val transitions: SceneTransitions = transitions { /* No transitions */ }
 ) : SceneContainerTransitionsBuilder {
     override fun build(
-        shadeExpansionMotion: EdgeContainerExpansionSpec,
+        shadeExpansionMotion: VerticalExpandContainerSpec,
         revealHaptics: ContainerRevealHaptics,
     ): SceneTransitions = transitions
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 9b4b91e..85aad9b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -20,7 +20,7 @@
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
 import com.android.systemui.notifications.ui.composable.NotificationsShade
 import com.android.systemui.scene.shared.model.Overlays
@@ -29,7 +29,7 @@
 
 fun TransitionBuilder.toNotificationsShadeTransition(
     durationScale: Double = 1.0,
-    shadeExpansionMotion: EdgeContainerExpansionSpec,
+    shadeExpansionMotion: VerticalExpandContainerSpec,
     revealHaptics: ContainerRevealHaptics,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index 47dd85f..8f0447d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -20,14 +20,14 @@
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import com.android.systemui.qs.ui.composable.QuickSettingsShade
 import com.android.systemui.shade.ui.composable.OverlayShade
 import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.toQuickSettingsShadeTransition(
     durationScale: Double = 1.0,
-    shadeExpansionMotion: EdgeContainerExpansionSpec,
+    shadeExpansionMotion: VerticalExpandContainerSpec,
     revealHaptics: ContainerRevealHaptics,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 3446081..068218a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -53,8 +53,8 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
 import com.android.systemui.res.R
 import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
 
@@ -114,9 +114,9 @@
         modifier =
             modifier
                 .disableSwipesWhenScrolling()
-                .edgeContainerExpansionBackground(
-                    OverlayShade.Colors.PanelBackground,
-                    rememberShadeExpansionMotion(),
+                .verticalExpandContainerBackground(
+                    backgroundColor = OverlayShade.Colors.PanelBackground,
+                    spec = rememberShadeExpansionMotion(isFullWidthShade()),
                 )
     ) {
         Column {
@@ -202,8 +202,10 @@
     }
 
     @Composable
-    fun rememberShadeExpansionMotion(): EdgeContainerExpansionSpec {
+    fun rememberShadeExpansionMotion(isFullWidth: Boolean): VerticalExpandContainerSpec {
         val radius = Dimensions.PanelCornerRadius
-        return remember(radius) { EdgeContainerExpansionSpec(radius = radius) }
+        return remember(radius, isFullWidth) {
+            VerticalExpandContainerSpec(isFloating = !isFullWidth, radius = radius)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 1360611..024ca22 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -68,7 +68,7 @@
         return layoutImpl.swipeDetector.detectSwipe(change)
     }
 
-    override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+    override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
         return this.enabled()
     }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
index 72f9bd5..734de34 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -29,7 +29,7 @@
 import com.android.compose.animation.scene.transformation.PropertyTransformation
 import com.android.compose.animation.scene.transformation.PropertyTransformationScope
 import com.android.mechanics.MotionValue
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import kotlinx.coroutines.CoroutineScope
 
 interface ContainerRevealHaptics {
@@ -53,7 +53,7 @@
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 fun TransitionBuilder.verticalContainerReveal(
     container: ElementKey,
-    motionSpec: EdgeContainerExpansionSpec,
+    motionSpec: VerticalExpandContainerSpec,
     haptics: ContainerRevealHaptics,
 ) {
     // Make the swipe distance be exactly the target height of the container.
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 2ab27af..d63450b 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -42,6 +42,7 @@
         "PlatformMotionTestingCompose",
         "androidx.test.runner",
         "androidx.test.ext.junit",
+        "platform-parametric-runner-lib",
 
         "androidx.compose.runtime_runtime",
         "androidx.compose.ui_ui-test-junit4",
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 0000000..5dbb013
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,624 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.6
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 8.4,
+          "y": 5.2
+        },
+        {
+          "x": 11.2,
+          "y": 5.2
+        },
+        {
+          "x": 13.6,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 16.8,
+          "y": 5.2
+        },
+        {
+          "x": 17.6,
+          "y": 5.2
+        },
+        {
+          "x": 18.4,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 293.2
+        },
+        {
+          "width": 150,
+          "height": 293.2
+        },
+        {
+          "width": 150,
+          "height": 286
+        },
+        {
+          "width": 150,
+          "height": 279.6
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 266.8
+        },
+        {
+          "width": 150,
+          "height": 260.4
+        },
+        {
+          "width": 150,
+          "height": 254
+        },
+        {
+          "width": 150,
+          "height": 247.6
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 228
+        },
+        {
+          "width": 150,
+          "height": 221.6
+        },
+        {
+          "width": 150,
+          "height": 215.2
+        },
+        {
+          "width": 150,
+          "height": 208.8
+        },
+        {
+          "width": 150,
+          "height": 202
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 182.8
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 170
+        },
+        {
+          "width": 150,
+          "height": 163.6
+        },
+        {
+          "width": 150,
+          "height": 157.2
+        },
+        {
+          "width": 150,
+          "height": 150.8
+        },
+        {
+          "width": 150,
+          "height": 144.4
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 131.2
+        },
+        {
+          "width": 150,
+          "height": 124.8
+        },
+        {
+          "width": 150,
+          "height": 118.4
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 99.2
+        },
+        {
+          "width": 150,
+          "height": 81.2
+        },
+        {
+          "width": 144,
+          "height": 62.8
+        },
+        {
+          "width": 138,
+          "height": 46.4
+        },
+        {
+          "width": 133.2,
+          "height": 32
+        },
+        {
+          "width": 129.6,
+          "height": 20.4
+        },
+        {
+          "width": 127.2,
+          "height": 12
+        },
+        {
+          "width": 125.2,
+          "height": 6.4
+        },
+        {
+          "width": 124,
+          "height": 2.8
+        },
+        {
+          "width": 123.2,
+          "height": 0.4
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99781144,
+        0.87040234,
+        0.6695792,
+        0.48078007,
+        0.33033127,
+        0.22004372,
+        0.1432175,
+        0.09153092,
+        0.057634592,
+        0.035840213,
+        0.022048414,
+        0.013435662,
+        0.008117795,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 0000000..1543d18
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,644 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944,
+    960,
+    976
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 6.8
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 13.2,
+          "y": 5.2
+        },
+        {
+          "x": 15.2,
+          "y": 5.2
+        },
+        {
+          "x": 16.8,
+          "y": 5.2
+        },
+        {
+          "x": 17.6,
+          "y": 5.2
+        },
+        {
+          "x": 18.4,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 294
+        },
+        {
+          "width": 150,
+          "height": 287.6
+        },
+        {
+          "width": 150,
+          "height": 282.8
+        },
+        {
+          "width": 150,
+          "height": 278
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 268.4
+        },
+        {
+          "width": 150,
+          "height": 263.6
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 253.6
+        },
+        {
+          "width": 150,
+          "height": 248.8
+        },
+        {
+          "width": 150,
+          "height": 244
+        },
+        {
+          "width": 150,
+          "height": 239.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 229.6
+        },
+        {
+          "width": 150,
+          "height": 224.8
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 214.8
+        },
+        {
+          "width": 150,
+          "height": 210
+        },
+        {
+          "width": 150,
+          "height": 205.2
+        },
+        {
+          "width": 150,
+          "height": 200.4
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 190.8
+        },
+        {
+          "width": 150,
+          "height": 186
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 171.6
+        },
+        {
+          "width": 150,
+          "height": 166.8
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 147.2
+        },
+        {
+          "width": 150,
+          "height": 122
+        },
+        {
+          "width": 150,
+          "height": 95.2
+        },
+        {
+          "width": 146.8,
+          "height": 70.8
+        },
+        {
+          "width": 139.6,
+          "height": 50.8
+        },
+        {
+          "width": 134,
+          "height": 34
+        },
+        {
+          "width": 130,
+          "height": 19.2
+        },
+        {
+          "width": 127.2,
+          "height": 9.2
+        },
+        {
+          "width": 125.2,
+          "height": 2.8
+        },
+        {
+          "width": 123.6,
+          "height": 0
+        },
+        {
+          "width": 122.8,
+          "height": 0
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99979615,
+        0.8860379,
+        0.6869267,
+        0.4955439,
+        0.34154767,
+        0.22803628,
+        0.14868057,
+        0.09515619,
+        0.059987247,
+        0.037340224,
+        0.02299112,
+        0.01402092,
+        0.008477271,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 0000000..13f75d2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 16.8,
+          "y": 5.2
+        },
+        {
+          "x": 16,
+          "y": 5.2
+        },
+        {
+          "x": 14.8,
+          "y": 5.2
+        },
+        {
+          "x": 13.6,
+          "y": 5.2
+        },
+        {
+          "x": 12.4,
+          "y": 5.2
+        },
+        {
+          "x": 11.2,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 9.2,
+          "y": 5.2
+        },
+        {
+          "x": 9.2,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 124.4,
+          "height": 1.6
+        },
+        {
+          "width": 124.4,
+          "height": 1.6
+        },
+        {
+          "width": 126.8,
+          "height": 3.2
+        },
+        {
+          "width": 128.8,
+          "height": 4.8
+        },
+        {
+          "width": 131.2,
+          "height": 6.4
+        },
+        {
+          "width": 133.6,
+          "height": 8
+        },
+        {
+          "width": 135.6,
+          "height": 9.6
+        },
+        {
+          "width": 138,
+          "height": 11.2
+        },
+        {
+          "width": 140,
+          "height": 12.8
+        },
+        {
+          "width": 142.4,
+          "height": 14.4
+        },
+        {
+          "width": 142.4,
+          "height": 14.4
+        },
+        {
+          "width": 144.8,
+          "height": 16.4
+        },
+        {
+          "width": 147.2,
+          "height": 18
+        },
+        {
+          "width": 149.2,
+          "height": 19.6
+        },
+        {
+          "width": 150,
+          "height": 25.6
+        },
+        {
+          "width": 150,
+          "height": 36.4
+        },
+        {
+          "width": 150,
+          "height": 45.6
+        },
+        {
+          "width": 150,
+          "height": 59.2
+        },
+        {
+          "width": 150,
+          "height": 72.8
+        },
+        {
+          "width": 150,
+          "height": 79.6
+        },
+        {
+          "width": 150,
+          "height": 92.8
+        },
+        {
+          "width": 150,
+          "height": 104.4
+        },
+        {
+          "width": 150,
+          "height": 115.2
+        },
+        {
+          "width": 150,
+          "height": 125.2
+        },
+        {
+          "width": 150,
+          "height": 134.8
+        },
+        {
+          "width": 150,
+          "height": 143.2
+        },
+        {
+          "width": 150,
+          "height": 151.2
+        },
+        {
+          "width": 150,
+          "height": 158.8
+        },
+        {
+          "width": 150,
+          "height": 160
+        },
+        {
+          "width": 150,
+          "height": 167.2
+        },
+        {
+          "width": 150,
+          "height": 174.4
+        },
+        {
+          "width": 150,
+          "height": 180.8
+        },
+        {
+          "width": 150,
+          "height": 187.6
+        },
+        {
+          "width": 150,
+          "height": 188
+        },
+        {
+          "width": 150,
+          "height": 200.4
+        },
+        {
+          "width": 150,
+          "height": 218.4
+        },
+        {
+          "width": 150,
+          "height": 236.8
+        },
+        {
+          "width": 150,
+          "height": 253.2
+        },
+        {
+          "width": 150,
+          "height": 266.8
+        },
+        {
+          "width": 150,
+          "height": 277.2
+        },
+        {
+          "width": 150,
+          "height": 284.8
+        },
+        {
+          "width": 150,
+          "height": 290
+        },
+        {
+          "width": 150,
+          "height": 294
+        },
+        {
+          "width": 150,
+          "height": 296.4
+        },
+        {
+          "width": 150,
+          "height": 298
+        },
+        {
+          "width": 150,
+          "height": 298.8
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 300
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0,
+        0,
+        0.0067873597,
+        0.06125766,
+        0.19080031,
+        0.39327443,
+        0.5711931,
+        0.7085583,
+        0.8074065,
+        0.8754226,
+        0.9207788,
+        0.95032376,
+        0.9692185,
+        0.98112255,
+        0.9885286,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
new file mode 100644
index 0000000..115483cf
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,434 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 6.4
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 6.4,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 13.6,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 17.2,
+          "y": 5.2
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 290
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 266
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 223.6
+        },
+        {
+          "width": 150,
+          "height": 182.8
+        },
+        {
+          "width": 150,
+          "height": 141.2
+        },
+        {
+          "width": 150,
+          "height": 104
+        },
+        {
+          "width": 147.6,
+          "height": 74
+        },
+        {
+          "width": 139.6,
+          "height": 50.8
+        },
+        {
+          "width": 133.6,
+          "height": 32
+        },
+        {
+          "width": 129.2,
+          "height": 15.6
+        },
+        {
+          "width": 126.4,
+          "height": 5.2
+        },
+        {
+          "width": 124.4,
+          "height": 0
+        },
+        {
+          "width": 123.2,
+          "height": 0
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99479187,
+        0.8575029,
+        0.65572864,
+        0.4691311,
+        0.3215357,
+        0.21380007,
+        0.13896108,
+        0.0887118,
+        0.05580789,
+        0.03467691,
+        0.021318138,
+        0.0129826665,
+        0.007839739,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
similarity index 63%
rename from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
rename to packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
index 6dc5a0e..f202fcd 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
@@ -64,104 +64,104 @@
           "type": "not_found"
         },
         {
-          "x": 62.4,
-          "y": 50
+          "x": 17.6,
+          "y": 5.2
         },
         {
-          "x": 61.2,
-          "y": 50
+          "x": 16.4,
+          "y": 5.2
         },
         {
-          "x": 59.2,
-          "y": 50
+          "x": 14.4,
+          "y": 5.2
         },
         {
-          "x": 57.2,
-          "y": 50
+          "x": 12.4,
+          "y": 5.2
         },
         {
-          "x": 54.8,
-          "y": 50
+          "x": 10,
+          "y": 5.2
         },
         {
-          "x": 52.4,
-          "y": 50
+          "x": 7.6,
+          "y": 5.2
         },
         {
-          "x": 52.4,
-          "y": 50
+          "x": 7.6,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         }
       ]
     },
@@ -194,104 +194,104 @@
           "type": "not_found"
         },
         {
-          "width": 163.2,
+          "width": 125.2,
           "height": 2
         },
         {
-          "width": 166,
+          "width": 128,
           "height": 4.4
         },
         {
-          "width": 170,
+          "width": 132,
           "height": 6.8
         },
         {
-          "width": 174,
+          "width": 136,
           "height": 10
         },
         {
-          "width": 178.4,
+          "width": 140.4,
           "height": 13.2
         },
         {
-          "width": 183.6,
+          "width": 145.6,
           "height": 16.8
         },
         {
-          "width": 183.6,
+          "width": 145.6,
           "height": 16.8
         },
         {
-          "width": 188,
-          "height": 44.4
+          "width": 150,
+          "height": 36.8
         },
         {
-          "width": 188,
-          "height": 103.6
+          "width": 150,
+          "height": 81.2
         },
         {
-          "width": 188,
-          "height": 166
+          "width": 150,
+          "height": 126.8
         },
         {
-          "width": 188,
-          "height": 222.4
+          "width": 150,
+          "height": 168
         },
         {
-          "width": 188,
-          "height": 270
+          "width": 150,
+          "height": 202.8
         },
         {
-          "width": 188,
-          "height": 307.2
+          "width": 150,
+          "height": 230
         },
         {
-          "width": 188,
-          "height": 335.6
+          "width": 150,
+          "height": 250.8
         },
         {
-          "width": 188,
-          "height": 356.4
+          "width": 150,
+          "height": 266.4
         },
         {
-          "width": 188,
-          "height": 371.2
+          "width": 150,
+          "height": 277.6
         },
         {
-          "width": 188,
-          "height": 381.6
+          "width": 150,
+          "height": 285.2
         },
         {
-          "width": 188,
-          "height": 388.8
+          "width": 150,
+          "height": 290.4
         },
         {
-          "width": 188,
-          "height": 393.2
+          "width": 150,
+          "height": 294
         },
         {
-          "width": 188,
-          "height": 396
+          "width": 150,
+          "height": 296.4
         },
         {
-          "width": 188,
-          "height": 398
+          "width": 150,
+          "height": 298
         },
         {
-          "width": 188,
-          "height": 398.8
+          "width": 150,
+          "height": 298.8
         },
         {
-          "width": 188,
-          "height": 399.2
+          "width": 150,
+          "height": 299.2
         },
         {
-          "width": 188,
-          "height": 399.6
+          "width": 150,
+          "height": 299.6
         },
         {
-          "width": 188,
-          "height": 399.6
+          "width": 150,
+          "height": 299.6
         }
       ]
     },
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 0000000..4c57bda
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,724 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944,
+    960,
+    976,
+    992,
+    1008,
+    1024,
+    1040,
+    1056,
+    1072,
+    1088,
+    1104
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 17.2,
+          "y": 5.2
+        },
+        {
+          "x": 16.4,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 14.8,
+          "y": 5.2
+        },
+        {
+          "x": 14,
+          "y": 5.2
+        },
+        {
+          "x": 13.2,
+          "y": 5.2
+        },
+        {
+          "x": 12.4,
+          "y": 5.2
+        },
+        {
+          "x": 11.6,
+          "y": 5.2
+        },
+        {
+          "x": 10.8,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 9.6,
+          "y": 5.2
+        },
+        {
+          "x": 8.8,
+          "y": 5.2
+        },
+        {
+          "x": 8.4,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 7.2,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 6.4,
+          "y": 5.2
+        },
+        {
+          "x": 6,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 5.2
+        },
+        {
+          "x": 6,
+          "y": 5.2
+        },
+        {
+          "x": 6.4,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 7.2,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 8.4,
+          "y": 5.2
+        },
+        {
+          "x": 8.8,
+          "y": 5.2
+        },
+        {
+          "x": 9.6,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 10.8,
+          "y": 5.2
+        },
+        {
+          "x": 11.6,
+          "y": 5.2
+        },
+        {
+          "x": 12.4,
+          "y": 5.2
+        },
+        {
+          "x": 13.2,
+          "y": 5.2
+        },
+        {
+          "x": 14,
+          "y": 5.2
+        },
+        {
+          "x": 14.8,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 16.4,
+          "y": 5.2
+        },
+        {
+          "x": 17.2,
+          "y": 5.2
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 124.4,
+          "height": 1.6
+        },
+        {
+          "width": 126,
+          "height": 2.8
+        },
+        {
+          "width": 128,
+          "height": 4
+        },
+        {
+          "width": 129.6,
+          "height": 5.2
+        },
+        {
+          "width": 131.2,
+          "height": 6.4
+        },
+        {
+          "width": 132.8,
+          "height": 7.6
+        },
+        {
+          "width": 134.4,
+          "height": 8.8
+        },
+        {
+          "width": 136,
+          "height": 10
+        },
+        {
+          "width": 137.2,
+          "height": 10.8
+        },
+        {
+          "width": 138.8,
+          "height": 12
+        },
+        {
+          "width": 140,
+          "height": 12.8
+        },
+        {
+          "width": 141.2,
+          "height": 13.6
+        },
+        {
+          "width": 142.8,
+          "height": 14.8
+        },
+        {
+          "width": 144,
+          "height": 15.6
+        },
+        {
+          "width": 144.8,
+          "height": 16.4
+        },
+        {
+          "width": 146,
+          "height": 17.2
+        },
+        {
+          "width": 146.8,
+          "height": 17.6
+        },
+        {
+          "width": 148,
+          "height": 18.4
+        },
+        {
+          "width": 148.8,
+          "height": 19.2
+        },
+        {
+          "width": 149.6,
+          "height": 19.6
+        },
+        {
+          "width": 150,
+          "height": 21.2
+        },
+        {
+          "width": 150,
+          "height": 24.8
+        },
+        {
+          "width": 150,
+          "height": 30
+        },
+        {
+          "width": 150,
+          "height": 38
+        },
+        {
+          "width": 150,
+          "height": 46
+        },
+        {
+          "width": 150,
+          "height": 54
+        },
+        {
+          "width": 150,
+          "height": 61.2
+        },
+        {
+          "width": 150,
+          "height": 66.8
+        },
+        {
+          "width": 150,
+          "height": 71.6
+        },
+        {
+          "width": 150,
+          "height": 75.6
+        },
+        {
+          "width": 150,
+          "height": 78
+        },
+        {
+          "width": 150,
+          "height": 79.6
+        },
+        {
+          "width": 150,
+          "height": 80.8
+        },
+        {
+          "width": 150,
+          "height": 80.8
+        },
+        {
+          "width": 150,
+          "height": 80.4
+        },
+        {
+          "width": 150,
+          "height": 79.6
+        },
+        {
+          "width": 149.6,
+          "height": 78
+        },
+        {
+          "width": 148.8,
+          "height": 76.4
+        },
+        {
+          "width": 148,
+          "height": 74
+        },
+        {
+          "width": 146.8,
+          "height": 71.6
+        },
+        {
+          "width": 146,
+          "height": 69.2
+        },
+        {
+          "width": 144.8,
+          "height": 66
+        },
+        {
+          "width": 144,
+          "height": 62.8
+        },
+        {
+          "width": 142.8,
+          "height": 59.2
+        },
+        {
+          "width": 141.2,
+          "height": 55.6
+        },
+        {
+          "width": 140,
+          "height": 52
+        },
+        {
+          "width": 138.8,
+          "height": 48
+        },
+        {
+          "width": 137.2,
+          "height": 44
+        },
+        {
+          "width": 136,
+          "height": 40
+        },
+        {
+          "width": 134.4,
+          "height": 37.6
+        },
+        {
+          "width": 132.8,
+          "height": 38
+        },
+        {
+          "width": 131.2,
+          "height": 30.4
+        },
+        {
+          "width": 129.6,
+          "height": 25.2
+        },
+        {
+          "width": 128,
+          "height": 20.4
+        },
+        {
+          "width": 126,
+          "height": 16
+        },
+        {
+          "width": 124.4,
+          "height": 12.4
+        },
+        {
+          "width": 122.8,
+          "height": 9.2
+        },
+        {
+          "width": 122,
+          "height": 6.8
+        },
+        {
+          "width": 122,
+          "height": 5.2
+        },
+        {
+          "width": 122,
+          "height": 3.6
+        },
+        {
+          "width": 122,
+          "height": 2.4
+        },
+        {
+          "width": 122,
+          "height": 1.6
+        },
+        {
+          "width": 122,
+          "height": 0.8
+        },
+        {
+          "width": 122,
+          "height": 0.4
+        },
+        {
+          "width": 122,
+          "height": 0.4
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0,
+        0,
+        0.012518823,
+        0.0741024,
+        0.22542936,
+        0.42628878,
+        0.5976642,
+        0.7280313,
+        0.82100236,
+        0.8845844,
+        0.9267946,
+        0.95419544,
+        0.9716705,
+        0.98265487,
+        0.98947525,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.9944124,
+        0.9417388,
+        0.81841844,
+        0.61578125,
+        0.43616113,
+        0.29689062,
+        0.19641556,
+        0.12716138,
+        0.080922,
+        0.050773032,
+        0.031477194,
+        0.019312754,
+        0.011740657,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 0000000..26c80e3
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,314 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 7.2,
+          "y": 5.2
+        },
+        {
+          "x": 11.2,
+          "y": 5.2
+        },
+        {
+          "x": 14,
+          "y": 5.2
+        },
+        {
+          "x": 16,
+          "y": 5.2
+        },
+        {
+          "x": 17.6,
+          "y": 5.2
+        },
+        {
+          "x": 18.4,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 234.8
+        },
+        {
+          "width": 150,
+          "height": 185.2
+        },
+        {
+          "width": 150,
+          "height": 138.8
+        },
+        {
+          "width": 150,
+          "height": 100.4
+        },
+        {
+          "width": 146.4,
+          "height": 69.6
+        },
+        {
+          "width": 138.4,
+          "height": 46.8
+        },
+        {
+          "width": 132.4,
+          "height": 28
+        },
+        {
+          "width": 128.4,
+          "height": 13.2
+        },
+        {
+          "width": 125.6,
+          "height": 4
+        },
+        {
+          "width": 124,
+          "height": 0
+        },
+        {
+          "width": 122.8,
+          "height": 0
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.9762947,
+        0.8118515,
+        0.60931784,
+        0.43090785,
+        0.29299664,
+        0.19368339,
+        0.12531388,
+        0.079705715,
+        0.049988627,
+        0.030979574,
+        0.019001365,
+        0.011548042,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 0000000..7a02d36
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,224 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 129.2,
+          "height": 5.2
+        },
+        {
+          "width": 144.4,
+          "height": 16
+        },
+        {
+          "width": 150,
+          "height": 62
+        },
+        {
+          "width": 150,
+          "height": 118.4
+        },
+        {
+          "width": 150,
+          "height": 166
+        },
+        {
+          "width": 150,
+          "height": 204
+        },
+        {
+          "width": 150,
+          "height": 233.2
+        },
+        {
+          "width": 150,
+          "height": 254.4
+        },
+        {
+          "width": 150,
+          "height": 270
+        },
+        {
+          "width": 150,
+          "height": 280.8
+        },
+        {
+          "width": 150,
+          "height": 288
+        },
+        {
+          "width": 150,
+          "height": 292.8
+        },
+        {
+          "width": 150,
+          "height": 296
+        },
+        {
+          "width": 150,
+          "height": 298
+        },
+        {
+          "width": 150,
+          "height": 298.8
+        },
+        {
+          "width": 150,
+          "height": 299.2
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.0951103,
+        0.2911651,
+        0.48551244,
+        0.6439433,
+        0.76157355,
+        0.8441935,
+        0.9001033,
+        0.9369305,
+        0.96069145,
+        0.97577035,
+        0.98520935,
+        0.9910494,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 0000000..f44d4cd
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,584 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.6
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 293.2
+        },
+        {
+          "width": 150,
+          "height": 293.2
+        },
+        {
+          "width": 150,
+          "height": 286
+        },
+        {
+          "width": 150,
+          "height": 279.6
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 266.8
+        },
+        {
+          "width": 150,
+          "height": 260.4
+        },
+        {
+          "width": 150,
+          "height": 254
+        },
+        {
+          "width": 150,
+          "height": 247.6
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 228
+        },
+        {
+          "width": 150,
+          "height": 221.6
+        },
+        {
+          "width": 150,
+          "height": 215.2
+        },
+        {
+          "width": 150,
+          "height": 208.8
+        },
+        {
+          "width": 150,
+          "height": 202
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 182.8
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 170
+        },
+        {
+          "width": 150,
+          "height": 163.6
+        },
+        {
+          "width": 150,
+          "height": 157.2
+        },
+        {
+          "width": 150,
+          "height": 150.8
+        },
+        {
+          "width": 150,
+          "height": 144.4
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 131.2
+        },
+        {
+          "width": 150,
+          "height": 124.8
+        },
+        {
+          "width": 150,
+          "height": 118.4
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 99.2
+        },
+        {
+          "width": 150,
+          "height": 84.4
+        },
+        {
+          "width": 150,
+          "height": 70.8
+        },
+        {
+          "width": 150,
+          "height": 58
+        },
+        {
+          "width": 150,
+          "height": 46.4
+        },
+        {
+          "width": 150,
+          "height": 36.4
+        },
+        {
+          "width": 150,
+          "height": 28
+        },
+        {
+          "width": 150,
+          "height": 20.8
+        },
+        {
+          "width": 150,
+          "height": 15.6
+        },
+        {
+          "width": 150,
+          "height": 11.2
+        },
+        {
+          "width": 150,
+          "height": 8
+        },
+        {
+          "width": 150,
+          "height": 5.6
+        },
+        {
+          "width": 150,
+          "height": 3.6
+        },
+        {
+          "width": 150,
+          "height": 2.4
+        },
+        {
+          "width": 150,
+          "height": 1.2
+        },
+        {
+          "width": 150,
+          "height": 0.8
+        },
+        {
+          "width": 150,
+          "height": 0.4
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99781144,
+        0.87040234,
+        0.6695792,
+        0.48078007,
+        0.33033127,
+        0.22004372,
+        0.1432175,
+        0.09153092,
+        0.057634592,
+        0.035840213,
+        0.022048414,
+        0.013435662,
+        0.008117795,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 0000000..9b68c71
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,624 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 6.8
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 294
+        },
+        {
+          "width": 150,
+          "height": 287.6
+        },
+        {
+          "width": 150,
+          "height": 282.8
+        },
+        {
+          "width": 150,
+          "height": 278
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 268.4
+        },
+        {
+          "width": 150,
+          "height": 263.6
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 253.6
+        },
+        {
+          "width": 150,
+          "height": 248.8
+        },
+        {
+          "width": 150,
+          "height": 244
+        },
+        {
+          "width": 150,
+          "height": 239.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 229.6
+        },
+        {
+          "width": 150,
+          "height": 224.8
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 214.8
+        },
+        {
+          "width": 150,
+          "height": 210
+        },
+        {
+          "width": 150,
+          "height": 205.2
+        },
+        {
+          "width": 150,
+          "height": 200.4
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 190.8
+        },
+        {
+          "width": 150,
+          "height": 186
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 171.6
+        },
+        {
+          "width": 150,
+          "height": 166.8
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 147.2
+        },
+        {
+          "width": 150,
+          "height": 122
+        },
+        {
+          "width": 150,
+          "height": 95.2
+        },
+        {
+          "width": 150,
+          "height": 70.8
+        },
+        {
+          "width": 150,
+          "height": 51.6
+        },
+        {
+          "width": 150,
+          "height": 36.8
+        },
+        {
+          "width": 150,
+          "height": 25.6
+        },
+        {
+          "width": 150,
+          "height": 17.2
+        },
+        {
+          "width": 150,
+          "height": 11.2
+        },
+        {
+          "width": 150,
+          "height": 6.8
+        },
+        {
+          "width": 150,
+          "height": 4
+        },
+        {
+          "width": 150,
+          "height": 2
+        },
+        {
+          "width": 150,
+          "height": 0.8
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99979615,
+        0.8860379,
+        0.6869267,
+        0.4955439,
+        0.34154767,
+        0.22803628,
+        0.14868057,
+        0.09515619,
+        0.059987247,
+        0.037340224,
+        0.02299112,
+        0.01402092,
+        0.008477271,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 0000000..1bf6a62
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 1.6
+        },
+        {
+          "width": 150,
+          "height": 1.6
+        },
+        {
+          "width": 150,
+          "height": 3.2
+        },
+        {
+          "width": 150,
+          "height": 4.8
+        },
+        {
+          "width": 150,
+          "height": 6.4
+        },
+        {
+          "width": 150,
+          "height": 8
+        },
+        {
+          "width": 150,
+          "height": 9.6
+        },
+        {
+          "width": 150,
+          "height": 11.2
+        },
+        {
+          "width": 150,
+          "height": 12.8
+        },
+        {
+          "width": 150,
+          "height": 14.4
+        },
+        {
+          "width": 150,
+          "height": 14.4
+        },
+        {
+          "width": 150,
+          "height": 16.4
+        },
+        {
+          "width": 150,
+          "height": 18
+        },
+        {
+          "width": 150,
+          "height": 19.6
+        },
+        {
+          "width": 150,
+          "height": 20.8
+        },
+        {
+          "width": 150,
+          "height": 22.8
+        },
+        {
+          "width": 150,
+          "height": 24.4
+        },
+        {
+          "width": 150,
+          "height": 26
+        },
+        {
+          "width": 150,
+          "height": 27.6
+        },
+        {
+          "width": 150,
+          "height": 27.6
+        },
+        {
+          "width": 150,
+          "height": 29.2
+        },
+        {
+          "width": 150,
+          "height": 30.8
+        },
+        {
+          "width": 150,
+          "height": 32.4
+        },
+        {
+          "width": 150,
+          "height": 34
+        },
+        {
+          "width": 150,
+          "height": 40.4
+        },
+        {
+          "width": 150,
+          "height": 52.4
+        },
+        {
+          "width": 150,
+          "height": 64.8
+        },
+        {
+          "width": 150,
+          "height": 83.2
+        },
+        {
+          "width": 150,
+          "height": 96
+        },
+        {
+          "width": 150,
+          "height": 114.8
+        },
+        {
+          "width": 150,
+          "height": 132
+        },
+        {
+          "width": 150,
+          "height": 148
+        },
+        {
+          "width": 150,
+          "height": 162
+        },
+        {
+          "width": 150,
+          "height": 168.4
+        },
+        {
+          "width": 150,
+          "height": 186
+        },
+        {
+          "width": 150,
+          "height": 208
+        },
+        {
+          "width": 150,
+          "height": 229.6
+        },
+        {
+          "width": 150,
+          "height": 248.4
+        },
+        {
+          "width": 150,
+          "height": 263.2
+        },
+        {
+          "width": 150,
+          "height": 274.8
+        },
+        {
+          "width": 150,
+          "height": 283.2
+        },
+        {
+          "width": 150,
+          "height": 289.2
+        },
+        {
+          "width": 150,
+          "height": 293.6
+        },
+        {
+          "width": 150,
+          "height": 296
+        },
+        {
+          "width": 150,
+          "height": 298
+        },
+        {
+          "width": 150,
+          "height": 298.8
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 300
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0,
+        0,
+        0.0067873597,
+        0.06125766,
+        0.19080031,
+        0.39327443,
+        0.5711931,
+        0.7085583,
+        0.8074065,
+        0.8754226,
+        0.9207788,
+        0.95032376,
+        0.9692185,
+        0.98112255,
+        0.9885286,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
new file mode 100644
index 0000000..86805bd
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,424 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 6.4
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 290
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 266
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 223.6
+        },
+        {
+          "width": 150,
+          "height": 182.8
+        },
+        {
+          "width": 150,
+          "height": 141.2
+        },
+        {
+          "width": 150,
+          "height": 104
+        },
+        {
+          "width": 150,
+          "height": 72
+        },
+        {
+          "width": 150,
+          "height": 46
+        },
+        {
+          "width": 150,
+          "height": 28
+        },
+        {
+          "width": 150,
+          "height": 15.6
+        },
+        {
+          "width": 150,
+          "height": 7.2
+        },
+        {
+          "width": 150,
+          "height": 2
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99479187,
+        0.8575029,
+        0.65572864,
+        0.4691311,
+        0.3215357,
+        0.21380007,
+        0.13896108,
+        0.0887118,
+        0.05580789,
+        0.03467691,
+        0.021318138,
+        0.0129826665,
+        0.007839739,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
new file mode 100644
index 0000000..98519db
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
@@ -0,0 +1,374 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 2
+        },
+        {
+          "width": 150,
+          "height": 4.4
+        },
+        {
+          "width": 150,
+          "height": 6.8
+        },
+        {
+          "width": 150,
+          "height": 10
+        },
+        {
+          "width": 150,
+          "height": 13.2
+        },
+        {
+          "width": 150,
+          "height": 16.8
+        },
+        {
+          "width": 150,
+          "height": 16.8
+        },
+        {
+          "width": 150,
+          "height": 23.6
+        },
+        {
+          "width": 150,
+          "height": 32.8
+        },
+        {
+          "width": 150,
+          "height": 76.8
+        },
+        {
+          "width": 150,
+          "height": 123.6
+        },
+        {
+          "width": 150,
+          "height": 164.8
+        },
+        {
+          "width": 150,
+          "height": 198.4
+        },
+        {
+          "width": 150,
+          "height": 225.6
+        },
+        {
+          "width": 150,
+          "height": 246.4
+        },
+        {
+          "width": 150,
+          "height": 262
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 281.6
+        },
+        {
+          "width": 150,
+          "height": 287.6
+        },
+        {
+          "width": 150,
+          "height": 292
+        },
+        {
+          "width": 150,
+          "height": 294.8
+        },
+        {
+          "width": 150,
+          "height": 296.4
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 298.4
+        },
+        {
+          "width": 150,
+          "height": 299.2
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.008216977,
+        0.06259775,
+        0.19032806,
+        0.39281356,
+        0.57081985,
+        0.7082821,
+        0.80721295,
+        0.8752918,
+        0.9206928,
+        0.95026827,
+        0.9691833,
+        0.98110056,
+        0.988515,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 0000000..850cee9
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,744 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944,
+    960,
+    976,
+    992,
+    1008,
+    1024,
+    1040,
+    1056,
+    1072,
+    1088,
+    1104,
+    1120,
+    1136
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 2.8
+        },
+        {
+          "width": 150,
+          "height": 4.8
+        },
+        {
+          "width": 150,
+          "height": 6.8
+        },
+        {
+          "width": 150,
+          "height": 8.4
+        },
+        {
+          "width": 150,
+          "height": 10.4
+        },
+        {
+          "width": 150,
+          "height": 12.4
+        },
+        {
+          "width": 150,
+          "height": 14
+        },
+        {
+          "width": 150,
+          "height": 16
+        },
+        {
+          "width": 150,
+          "height": 17.6
+        },
+        {
+          "width": 150,
+          "height": 19.2
+        },
+        {
+          "width": 150,
+          "height": 20.8
+        },
+        {
+          "width": 150,
+          "height": 22.4
+        },
+        {
+          "width": 150,
+          "height": 24
+        },
+        {
+          "width": 150,
+          "height": 25.6
+        },
+        {
+          "width": 150,
+          "height": 26.8
+        },
+        {
+          "width": 150,
+          "height": 28
+        },
+        {
+          "width": 150,
+          "height": 29.2
+        },
+        {
+          "width": 150,
+          "height": 30.4
+        },
+        {
+          "width": 150,
+          "height": 31.6
+        },
+        {
+          "width": 150,
+          "height": 32.4
+        },
+        {
+          "width": 150,
+          "height": 33.2
+        },
+        {
+          "width": 150,
+          "height": 34
+        },
+        {
+          "width": 150,
+          "height": 36.8
+        },
+        {
+          "width": 150,
+          "height": 42.4
+        },
+        {
+          "width": 150,
+          "height": 50.8
+        },
+        {
+          "width": 150,
+          "height": 64
+        },
+        {
+          "width": 150,
+          "height": 78
+        },
+        {
+          "width": 150,
+          "height": 91.2
+        },
+        {
+          "width": 150,
+          "height": 102.4
+        },
+        {
+          "width": 150,
+          "height": 112.4
+        },
+        {
+          "width": 150,
+          "height": 120
+        },
+        {
+          "width": 150,
+          "height": 126
+        },
+        {
+          "width": 150,
+          "height": 130
+        },
+        {
+          "width": 150,
+          "height": 132.8
+        },
+        {
+          "width": 150,
+          "height": 134
+        },
+        {
+          "width": 150,
+          "height": 134
+        },
+        {
+          "width": 150,
+          "height": 133.2
+        },
+        {
+          "width": 150,
+          "height": 131.2
+        },
+        {
+          "width": 150,
+          "height": 128.8
+        },
+        {
+          "width": 150,
+          "height": 125.2
+        },
+        {
+          "width": 150,
+          "height": 121.6
+        },
+        {
+          "width": 150,
+          "height": 117.6
+        },
+        {
+          "width": 150,
+          "height": 112.8
+        },
+        {
+          "width": 150,
+          "height": 108
+        },
+        {
+          "width": 150,
+          "height": 102.4
+        },
+        {
+          "width": 150,
+          "height": 96.4
+        },
+        {
+          "width": 150,
+          "height": 91.2
+        },
+        {
+          "width": 150,
+          "height": 88
+        },
+        {
+          "width": 150,
+          "height": 81.6
+        },
+        {
+          "width": 150,
+          "height": 70.8
+        },
+        {
+          "width": 150,
+          "height": 59.2
+        },
+        {
+          "width": 150,
+          "height": 48
+        },
+        {
+          "width": 150,
+          "height": 38.4
+        },
+        {
+          "width": 150,
+          "height": 30
+        },
+        {
+          "width": 150,
+          "height": 22.8
+        },
+        {
+          "width": 150,
+          "height": 17.2
+        },
+        {
+          "width": 150,
+          "height": 12.4
+        },
+        {
+          "width": 150,
+          "height": 9.2
+        },
+        {
+          "width": 150,
+          "height": 6.4
+        },
+        {
+          "width": 150,
+          "height": 4.4
+        },
+        {
+          "width": 150,
+          "height": 2.8
+        },
+        {
+          "width": 150,
+          "height": 1.6
+        },
+        {
+          "width": 150,
+          "height": 1.2
+        },
+        {
+          "width": 150,
+          "height": 0.4
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.0066464543,
+        0.059778452,
+        0.1875459,
+        0.39009166,
+        0.5686131,
+        0.70664865,
+        0.8060679,
+        0.87451804,
+        0.92018366,
+        0.94994,
+        0.9689752,
+        0.9809703,
+        0.98843443,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.98828065,
+        0.9288363,
+        0.7806658,
+        0.57941735,
+        0.40687433,
+        0.27529213,
+        0.18131107,
+        0.11697123,
+        0.074225225,
+        0.046460062,
+        0.028744182,
+        0.017604083,
+        0.010684598
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 0000000..afa005a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,304 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 234.8
+        },
+        {
+          "width": 150,
+          "height": 185.2
+        },
+        {
+          "width": 150,
+          "height": 138.8
+        },
+        {
+          "width": 150,
+          "height": 100.4
+        },
+        {
+          "width": 150,
+          "height": 66.8
+        },
+        {
+          "width": 150,
+          "height": 41.6
+        },
+        {
+          "width": 150,
+          "height": 23.6
+        },
+        {
+          "width": 150,
+          "height": 12
+        },
+        {
+          "width": 150,
+          "height": 4.4
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.9762947,
+        0.8118515,
+        0.60931784,
+        0.43090785,
+        0.29299664,
+        0.19368339,
+        0.12531388,
+        0.079705715,
+        0.049988627,
+        0.030979574,
+        0.019001365,
+        0.011548042,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 0000000..317d480
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,254 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 5.2
+        },
+        {
+          "width": 150,
+          "height": 16
+        },
+        {
+          "width": 150,
+          "height": 28.4
+        },
+        {
+          "width": 150,
+          "height": 63.6
+        },
+        {
+          "width": 150,
+          "height": 116.4
+        },
+        {
+          "width": 150,
+          "height": 161.2
+        },
+        {
+          "width": 150,
+          "height": 197.2
+        },
+        {
+          "width": 150,
+          "height": 225.2
+        },
+        {
+          "width": 150,
+          "height": 246.8
+        },
+        {
+          "width": 150,
+          "height": 262.4
+        },
+        {
+          "width": 150,
+          "height": 274
+        },
+        {
+          "width": 150,
+          "height": 282.4
+        },
+        {
+          "width": 150,
+          "height": 288.4
+        },
+        {
+          "width": 150,
+          "height": 292.4
+        },
+        {
+          "width": 150,
+          "height": 294.8
+        },
+        {
+          "width": 150,
+          "height": 296.4
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 298.4
+        },
+        {
+          "width": 150,
+          "height": 299.2
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.0951103,
+        0.2911651,
+        0.48551244,
+        0.6439433,
+        0.76157355,
+        0.8441935,
+        0.9001033,
+        0.9369305,
+        0.96069145,
+        0.97577035,
+        0.98520935,
+        0.9910494,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
deleted file mode 100644
index 57f6766..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
+++ /dev/null
@@ -1,654 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816,
-    832,
-    848,
-    864,
-    880,
-    896,
-    912,
-    928,
-    944,
-    960,
-    976,
-    992
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50.4
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 52.4,
-          "y": 50
-        },
-        {
-          "x": 56,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 60.8,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 393.2
-        },
-        {
-          "width": 188,
-          "height": 393.2
-        },
-        {
-          "width": 188,
-          "height": 386
-        },
-        {
-          "width": 188,
-          "height": 379.6
-        },
-        {
-          "width": 188,
-          "height": 372.8
-        },
-        {
-          "width": 188,
-          "height": 366.8
-        },
-        {
-          "width": 188,
-          "height": 360.4
-        },
-        {
-          "width": 188,
-          "height": 354
-        },
-        {
-          "width": 188,
-          "height": 347.6
-        },
-        {
-          "width": 188,
-          "height": 341.2
-        },
-        {
-          "width": 188,
-          "height": 341.2
-        },
-        {
-          "width": 188,
-          "height": 334
-        },
-        {
-          "width": 188,
-          "height": 328
-        },
-        {
-          "width": 188,
-          "height": 321.6
-        },
-        {
-          "width": 188,
-          "height": 315.2
-        },
-        {
-          "width": 188,
-          "height": 308.8
-        },
-        {
-          "width": 188,
-          "height": 302.4
-        },
-        {
-          "width": 188,
-          "height": 296
-        },
-        {
-          "width": 188,
-          "height": 289.6
-        },
-        {
-          "width": 188,
-          "height": 289.6
-        },
-        {
-          "width": 188,
-          "height": 282.8
-        },
-        {
-          "width": 188,
-          "height": 276.4
-        },
-        {
-          "width": 188,
-          "height": 270
-        },
-        {
-          "width": 188,
-          "height": 263.6
-        },
-        {
-          "width": 188,
-          "height": 257.2
-        },
-        {
-          "width": 188,
-          "height": 250.8
-        },
-        {
-          "width": 188,
-          "height": 244.4
-        },
-        {
-          "width": 188,
-          "height": 238
-        },
-        {
-          "width": 188,
-          "height": 238
-        },
-        {
-          "width": 188,
-          "height": 231.2
-        },
-        {
-          "width": 188,
-          "height": 224.8
-        },
-        {
-          "width": 188,
-          "height": 218.4
-        },
-        {
-          "width": 188,
-          "height": 212
-        },
-        {
-          "width": 188,
-          "height": 212
-        },
-        {
-          "width": 188,
-          "height": 192.4
-        },
-        {
-          "width": 188,
-          "height": 159.6
-        },
-        {
-          "width": 188,
-          "height": 124.4
-        },
-        {
-          "width": 188,
-          "height": 92.8
-        },
-        {
-          "width": 183.2,
-          "height": 66.4
-        },
-        {
-          "width": 176,
-          "height": 46
-        },
-        {
-          "width": 170.4,
-          "height": 28.8
-        },
-        {
-          "width": 166.8,
-          "height": 15.2
-        },
-        {
-          "width": 164,
-          "height": 6.4
-        },
-        {
-          "width": 162.4,
-          "height": 0.8
-        },
-        {
-          "width": 161.2,
-          "height": 0
-        },
-        {
-          "width": 160.4,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9808927,
-        0.8211168,
-        0.61845565,
-        0.43834114,
-        0.29850912,
-        0.19755232,
-        0.12793064,
-        0.08142871,
-        0.051099956,
-        0.031684637,
-        0.019442618,
-        0.011821032,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
deleted file mode 100644
index 01bc852..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
+++ /dev/null
@@ -1,644 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816,
-    832,
-    848,
-    864,
-    880,
-    896,
-    912,
-    928,
-    944,
-    960,
-    976
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50.8,
-          "y": 52
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 52.4,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 58.4,
-          "y": 50
-        },
-        {
-          "x": 60.4,
-          "y": 50
-        },
-        {
-          "x": 61.6,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.2,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 390
-        },
-        {
-          "width": 188,
-          "height": 390
-        },
-        {
-          "width": 188,
-          "height": 379.2
-        },
-        {
-          "width": 188,
-          "height": 371.2
-        },
-        {
-          "width": 188,
-          "height": 363.2
-        },
-        {
-          "width": 188,
-          "height": 355.2
-        },
-        {
-          "width": 188,
-          "height": 347.2
-        },
-        {
-          "width": 188,
-          "height": 339.2
-        },
-        {
-          "width": 188,
-          "height": 331.2
-        },
-        {
-          "width": 188,
-          "height": 323.2
-        },
-        {
-          "width": 188,
-          "height": 323.2
-        },
-        {
-          "width": 188,
-          "height": 314.8
-        },
-        {
-          "width": 188,
-          "height": 306.8
-        },
-        {
-          "width": 188,
-          "height": 298.8
-        },
-        {
-          "width": 188,
-          "height": 290.8
-        },
-        {
-          "width": 188,
-          "height": 282.8
-        },
-        {
-          "width": 188,
-          "height": 274.8
-        },
-        {
-          "width": 188,
-          "height": 266.8
-        },
-        {
-          "width": 188,
-          "height": 258.8
-        },
-        {
-          "width": 188,
-          "height": 258.8
-        },
-        {
-          "width": 188,
-          "height": 250.4
-        },
-        {
-          "width": 188,
-          "height": 242.4
-        },
-        {
-          "width": 188,
-          "height": 234.4
-        },
-        {
-          "width": 188,
-          "height": 226.4
-        },
-        {
-          "width": 188,
-          "height": 218.4
-        },
-        {
-          "width": 188,
-          "height": 210.4
-        },
-        {
-          "width": 188,
-          "height": 202.4
-        },
-        {
-          "width": 188,
-          "height": 194.4
-        },
-        {
-          "width": 188,
-          "height": 194.4
-        },
-        {
-          "width": 188,
-          "height": 185.6
-        },
-        {
-          "width": 188,
-          "height": 178
-        },
-        {
-          "width": 188,
-          "height": 170
-        },
-        {
-          "width": 188,
-          "height": 161.6
-        },
-        {
-          "width": 188,
-          "height": 161.6
-        },
-        {
-          "width": 188,
-          "height": 144.8
-        },
-        {
-          "width": 188,
-          "height": 118.8
-        },
-        {
-          "width": 188,
-          "height": 92
-        },
-        {
-          "width": 183.6,
-          "height": 68
-        },
-        {
-          "width": 176.8,
-          "height": 48.4
-        },
-        {
-          "width": 171.6,
-          "height": 32
-        },
-        {
-          "width": 167.6,
-          "height": 18
-        },
-        {
-          "width": 164.8,
-          "height": 8.8
-        },
-        {
-          "width": 162.8,
-          "height": 2.8
-        },
-        {
-          "width": 161.6,
-          "height": 0
-        },
-        {
-          "width": 160.8,
-          "height": 0
-        },
-        {
-          "width": 160.4,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9967737,
-        0.86538374,
-        0.66414475,
-        0.47619528,
-        0.32686388,
-        0.21757984,
-        0.14153665,
-        0.09041709,
-        0.05691254,
-        0.035380244,
-        0.02175957,
-        0.01325649,
-        0.008007765,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
deleted file mode 100644
index b6e423a..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
+++ /dev/null
@@ -1,544 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 61.6,
-          "y": 50
-        },
-        {
-          "x": 60.8,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 58.4,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 56,
-          "y": 50
-        },
-        {
-          "x": 55.2,
-          "y": 50
-        },
-        {
-          "x": 54,
-          "y": 50
-        },
-        {
-          "x": 54,
-          "y": 50
-        },
-        {
-          "x": 52.8,
-          "y": 50
-        },
-        {
-          "x": 51.6,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "width": 162.4,
-          "height": 1.6
-        },
-        {
-          "width": 162.4,
-          "height": 1.6
-        },
-        {
-          "width": 164.8,
-          "height": 3.2
-        },
-        {
-          "width": 166.8,
-          "height": 4.8
-        },
-        {
-          "width": 169.2,
-          "height": 6.4
-        },
-        {
-          "width": 171.6,
-          "height": 8
-        },
-        {
-          "width": 173.6,
-          "height": 9.6
-        },
-        {
-          "width": 176,
-          "height": 11.2
-        },
-        {
-          "width": 178,
-          "height": 12.8
-        },
-        {
-          "width": 180.4,
-          "height": 14.4
-        },
-        {
-          "width": 180.4,
-          "height": 14.4
-        },
-        {
-          "width": 182.8,
-          "height": 16.4
-        },
-        {
-          "width": 185.2,
-          "height": 18
-        },
-        {
-          "width": 187.2,
-          "height": 19.6
-        },
-        {
-          "width": 188,
-          "height": 25.6
-        },
-        {
-          "width": 188,
-          "height": 36.4
-        },
-        {
-          "width": 188,
-          "height": 45.6
-        },
-        {
-          "width": 188,
-          "height": 59.2
-        },
-        {
-          "width": 188,
-          "height": 72.8
-        },
-        {
-          "width": 188,
-          "height": 79.6
-        },
-        {
-          "width": 188,
-          "height": 92.8
-        },
-        {
-          "width": 188,
-          "height": 104.4
-        },
-        {
-          "width": 188,
-          "height": 115.2
-        },
-        {
-          "width": 188,
-          "height": 125.2
-        },
-        {
-          "width": 188,
-          "height": 134.8
-        },
-        {
-          "width": 188,
-          "height": 143.2
-        },
-        {
-          "width": 188,
-          "height": 151.2
-        },
-        {
-          "width": 188,
-          "height": 158.8
-        },
-        {
-          "width": 188,
-          "height": 160
-        },
-        {
-          "width": 188,
-          "height": 167.2
-        },
-        {
-          "width": 188,
-          "height": 174.4
-        },
-        {
-          "width": 188,
-          "height": 180.8
-        },
-        {
-          "width": 188,
-          "height": 187.6
-        },
-        {
-          "width": 188,
-          "height": 188
-        },
-        {
-          "width": 188,
-          "height": 207.2
-        },
-        {
-          "width": 188,
-          "height": 240
-        },
-        {
-          "width": 188,
-          "height": 275.2
-        },
-        {
-          "width": 188,
-          "height": 306.8
-        },
-        {
-          "width": 188,
-          "height": 333.2
-        },
-        {
-          "width": 188,
-          "height": 353.6
-        },
-        {
-          "width": 188,
-          "height": 368.8
-        },
-        {
-          "width": 188,
-          "height": 380
-        },
-        {
-          "width": 188,
-          "height": 387.6
-        },
-        {
-          "width": 188,
-          "height": 392.4
-        },
-        {
-          "width": 188,
-          "height": 395.6
-        },
-        {
-          "width": 188,
-          "height": 398
-        },
-        {
-          "width": 188,
-          "height": 398.8
-        },
-        {
-          "width": 188,
-          "height": 399.6
-        },
-        {
-          "width": 188,
-          "height": 400
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        0,
-        0,
-        0,
-        0,
-        0.0067873597,
-        0.0612576,
-        0.19080025,
-        0.39327443,
-        0.5711931,
-        0.70855826,
-        0.8074064,
-        0.8754226,
-        0.9207788,
-        0.95032376,
-        0.9692185,
-        0.98112255,
-        0.9885286,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
deleted file mode 100644
index a82db34..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
+++ /dev/null
@@ -1,444 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50.8
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 51.2,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 60.8,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 63.2,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 389.6
-        },
-        {
-          "width": 188,
-          "height": 378.8
-        },
-        {
-          "width": 188,
-          "height": 366
-        },
-        {
-          "width": 188,
-          "height": 352
-        },
-        {
-          "width": 188,
-          "height": 352
-        },
-        {
-          "width": 188,
-          "height": 316.8
-        },
-        {
-          "width": 188,
-          "height": 261.2
-        },
-        {
-          "width": 188,
-          "height": 202.8
-        },
-        {
-          "width": 188,
-          "height": 150.8
-        },
-        {
-          "width": 188,
-          "height": 107.6
-        },
-        {
-          "width": 186,
-          "height": 74.4
-        },
-        {
-          "width": 177.2,
-          "height": 49.6
-        },
-        {
-          "width": 170.8,
-          "height": 29.6
-        },
-        {
-          "width": 166.8,
-          "height": 12.8
-        },
-        {
-          "width": 164,
-          "height": 2.4
-        },
-        {
-          "width": 162,
-          "height": 0
-        },
-        {
-          "width": 160.8,
-          "height": 0
-        },
-        {
-          "width": 160.4,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9833227,
-        0.8263634,
-        0.623688,
-        0.44261706,
-        0.3016883,
-        0.1997872,
-        0.12944388,
-        0.08242595,
-        0.051743627,
-        0.032093227,
-        0.019698441,
-        0.0119793415,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
deleted file mode 100644
index 1cd971a..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
+++ /dev/null
@@ -1,724 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816,
-    832,
-    848,
-    864,
-    880,
-    896,
-    912,
-    928,
-    944,
-    960,
-    976,
-    992,
-    1008,
-    1024,
-    1040,
-    1056,
-    1072,
-    1088,
-    1104
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 61.2,
-          "y": 50
-        },
-        {
-          "x": 60.4,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 58,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 56.4,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 55.2,
-          "y": 50
-        },
-        {
-          "x": 54.4,
-          "y": 50
-        },
-        {
-          "x": 53.6,
-          "y": 50
-        },
-        {
-          "x": 53.2,
-          "y": 50
-        },
-        {
-          "x": 52.8,
-          "y": 50
-        },
-        {
-          "x": 52,
-          "y": 50
-        },
-        {
-          "x": 51.6,
-          "y": 50
-        },
-        {
-          "x": 51.2,
-          "y": 50
-        },
-        {
-          "x": 50.8,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50
-        },
-        {
-          "x": 50.8,
-          "y": 50
-        },
-        {
-          "x": 51.2,
-          "y": 50
-        },
-        {
-          "x": 51.6,
-          "y": 50
-        },
-        {
-          "x": 52,
-          "y": 50
-        },
-        {
-          "x": 52.8,
-          "y": 50
-        },
-        {
-          "x": 53.2,
-          "y": 50
-        },
-        {
-          "x": 53.6,
-          "y": 50
-        },
-        {
-          "x": 54.4,
-          "y": 50
-        },
-        {
-          "x": 55.2,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 56.4,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 58,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 60.4,
-          "y": 50
-        },
-        {
-          "x": 61.2,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "width": 162.4,
-          "height": 1.6
-        },
-        {
-          "width": 164,
-          "height": 2.8
-        },
-        {
-          "width": 166,
-          "height": 4
-        },
-        {
-          "width": 167.6,
-          "height": 5.2
-        },
-        {
-          "width": 169.2,
-          "height": 6.4
-        },
-        {
-          "width": 170.8,
-          "height": 7.6
-        },
-        {
-          "width": 172.4,
-          "height": 8.8
-        },
-        {
-          "width": 174,
-          "height": 10
-        },
-        {
-          "width": 175.2,
-          "height": 10.8
-        },
-        {
-          "width": 176.8,
-          "height": 12
-        },
-        {
-          "width": 178,
-          "height": 12.8
-        },
-        {
-          "width": 179.2,
-          "height": 13.6
-        },
-        {
-          "width": 180.8,
-          "height": 14.8
-        },
-        {
-          "width": 182,
-          "height": 15.6
-        },
-        {
-          "width": 182.8,
-          "height": 16.4
-        },
-        {
-          "width": 184,
-          "height": 17.2
-        },
-        {
-          "width": 184.8,
-          "height": 17.6
-        },
-        {
-          "width": 186,
-          "height": 18.4
-        },
-        {
-          "width": 186.8,
-          "height": 19.2
-        },
-        {
-          "width": 187.6,
-          "height": 19.6
-        },
-        {
-          "width": 188,
-          "height": 21.2
-        },
-        {
-          "width": 188,
-          "height": 24.8
-        },
-        {
-          "width": 188,
-          "height": 30
-        },
-        {
-          "width": 188,
-          "height": 38
-        },
-        {
-          "width": 188,
-          "height": 46
-        },
-        {
-          "width": 188,
-          "height": 54
-        },
-        {
-          "width": 188,
-          "height": 61.2
-        },
-        {
-          "width": 188,
-          "height": 66.8
-        },
-        {
-          "width": 188,
-          "height": 71.6
-        },
-        {
-          "width": 188,
-          "height": 75.6
-        },
-        {
-          "width": 188,
-          "height": 78
-        },
-        {
-          "width": 188,
-          "height": 79.6
-        },
-        {
-          "width": 188,
-          "height": 80.8
-        },
-        {
-          "width": 188,
-          "height": 80.8
-        },
-        {
-          "width": 188,
-          "height": 80.4
-        },
-        {
-          "width": 188,
-          "height": 79.6
-        },
-        {
-          "width": 187.6,
-          "height": 78
-        },
-        {
-          "width": 186.8,
-          "height": 76.4
-        },
-        {
-          "width": 186,
-          "height": 74
-        },
-        {
-          "width": 184.8,
-          "height": 71.6
-        },
-        {
-          "width": 184,
-          "height": 69.2
-        },
-        {
-          "width": 182.8,
-          "height": 66
-        },
-        {
-          "width": 182,
-          "height": 62.8
-        },
-        {
-          "width": 180.8,
-          "height": 59.2
-        },
-        {
-          "width": 179.2,
-          "height": 55.6
-        },
-        {
-          "width": 178,
-          "height": 52
-        },
-        {
-          "width": 176.8,
-          "height": 48
-        },
-        {
-          "width": 175.2,
-          "height": 44
-        },
-        {
-          "width": 174,
-          "height": 40
-        },
-        {
-          "width": 172.4,
-          "height": 37.6
-        },
-        {
-          "width": 170.8,
-          "height": 38
-        },
-        {
-          "width": 169.2,
-          "height": 30.4
-        },
-        {
-          "width": 167.6,
-          "height": 25.2
-        },
-        {
-          "width": 166,
-          "height": 20.4
-        },
-        {
-          "width": 164,
-          "height": 16
-        },
-        {
-          "width": 162.4,
-          "height": 12.4
-        },
-        {
-          "width": 160.8,
-          "height": 9.2
-        },
-        {
-          "width": 160,
-          "height": 6.8
-        },
-        {
-          "width": 160,
-          "height": 5.2
-        },
-        {
-          "width": 160,
-          "height": 3.6
-        },
-        {
-          "width": 160,
-          "height": 2.4
-        },
-        {
-          "width": 160,
-          "height": 1.6
-        },
-        {
-          "width": 160,
-          "height": 0.8
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        0,
-        0,
-        0,
-        0,
-        0.012518823,
-        0.0741024,
-        0.2254293,
-        0.42628878,
-        0.5976641,
-        0.7280312,
-        0.82100236,
-        0.8845844,
-        0.9267946,
-        0.95419544,
-        0.9716705,
-        0.98265487,
-        0.98947525,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9944124,
-        0.9417388,
-        0.8184184,
-        0.6157812,
-        0.4361611,
-        0.2968906,
-        0.19641554,
-        0.12716137,
-        0.080921985,
-        0.050773025,
-        0.03147719,
-        0.019312752,
-        0.011740655,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
deleted file mode 100644
index 1030455..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
+++ /dev/null
@@ -1,324 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 53.2,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 61.6,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 372
-        },
-        {
-          "width": 188,
-          "height": 312.8
-        },
-        {
-          "width": 188,
-          "height": 246.8
-        },
-        {
-          "width": 188,
-          "height": 185.2
-        },
-        {
-          "width": 188,
-          "height": 133.6
-        },
-        {
-          "width": 188,
-          "height": 93.2
-        },
-        {
-          "width": 181.6,
-          "height": 62.8
-        },
-        {
-          "width": 174,
-          "height": 40.8
-        },
-        {
-          "width": 168.8,
-          "height": 22.4
-        },
-        {
-          "width": 165.2,
-          "height": 10
-        },
-        {
-          "width": 162.8,
-          "height": 2.4
-        },
-        {
-          "width": 161.2,
-          "height": 0
-        },
-        {
-          "width": 160.4,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.91758585,
-        0.72435355,
-        0.52812576,
-        0.3665868,
-        0.24600428,
-        0.16102076,
-        0.103373945,
-        0.06533456,
-        0.04075712,
-        0.025142312,
-        0.015358448,
-        0.0092999935,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
deleted file mode 100644
index 622c29e..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
+++ /dev/null
@@ -1,244 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 59.2,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 169.6,
-          "height": 6.8
-        },
-        {
-          "width": 188,
-          "height": 26.8
-        },
-        {
-          "width": 188,
-          "height": 95.6
-        },
-        {
-          "width": 188,
-          "height": 163.2
-        },
-        {
-          "width": 188,
-          "height": 222
-        },
-        {
-          "width": 188,
-          "height": 269.6
-        },
-        {
-          "width": 188,
-          "height": 307.2
-        },
-        {
-          "width": 188,
-          "height": 335.2
-        },
-        {
-          "width": 188,
-          "height": 356
-        },
-        {
-          "width": 188,
-          "height": 370.4
-        },
-        {
-          "width": 188,
-          "height": 380.8
-        },
-        {
-          "width": 188,
-          "height": 387.6
-        },
-        {
-          "width": 188,
-          "height": 392.4
-        },
-        {
-          "width": 188,
-          "height": 395.2
-        },
-        {
-          "width": 188,
-          "height": 397.2
-        },
-        {
-          "width": 188,
-          "height": 398
-        },
-        {
-          "width": 188,
-          "height": 398.8
-        },
-        {
-          "width": 188,
-          "height": 399.2
-        },
-        {
-          "width": 188,
-          "height": 399.2
-        },
-        {
-          "width": 188,
-          "height": 399.6
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        0,
-        0.05698657,
-        0.24197984,
-        0.44158113,
-        0.6097554,
-        0.73685503,
-        0.8271309,
-        0.8886989,
-        0.9294886,
-        0.9559254,
-        0.97276413,
-        0.98333716,
-        0.98989624,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
index f4e2328..1bc83e0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
@@ -37,7 +37,6 @@
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.FeatureCaptures.elementAlpha
@@ -47,8 +46,8 @@
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.featureOfElement
 import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
 import kotlin.math.sin
 import kotlinx.coroutines.CoroutineScope
 import org.junit.Rule
@@ -63,26 +62,48 @@
 import platform.test.motion.compose.recordMotion
 import platform.test.motion.compose.runTest
 import platform.test.motion.testing.createGoldenPathManager
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.PathElementNoContext
 
-@RunWith(AndroidJUnit4::class)
 @MotionTest
-class ContentRevealTest {
+@RunWith(ParameterizedAndroidJunit4::class)
+class ContentRevealTest(private val isFloating: Boolean) {
+
+    private val pathConfig =
+        PathConfig(
+            PathElementNoContext("floating", isDir = false) {
+                if (isFloating) "floating" else "edge"
+            }
+        )
 
     private val goldenPaths =
-        createGoldenPathManager("frameworks/base/packages/SystemUI/compose/scene/tests/goldens")
+        createGoldenPathManager(
+            "frameworks/base/packages/SystemUI/compose/scene/tests/goldens",
+            pathConfig,
+        )
 
     @get:Rule val motionRule = createFixedConfigurationComposeMotionTestRule(goldenPaths)
 
     private val fakeHaptics = FakeHaptics()
 
+    private val motionSpec = VerticalExpandContainerSpec(isFloating)
+
     @Test
     fun verticalReveal_triggeredRevealOpenTransition() {
-        assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneClosed, SceneOpen))
+        assertVerticalContainerRevealMotion(
+            TriggeredRevealMotion(SceneClosed, SceneOpen),
+            "verticalReveal_triggeredRevealOpenTransition",
+        )
     }
 
     @Test
     fun verticalReveal_triggeredRevealCloseTransition() {
-        assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneOpen, SceneClosed))
+        assertVerticalContainerRevealMotion(
+            TriggeredRevealMotion(SceneOpen, SceneClosed),
+            "verticalReveal_triggeredRevealCloseTransition",
+        )
     }
 
     @Test
@@ -90,15 +111,18 @@
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneClosed) {
                 val gestureDurationMillis = 1000L
+                // detach position for the floating container is larger
+                val gestureHeight = if (isFloating) 160.dp.toPx() else 100.dp.toPx()
                 swipe(
                     curve = {
                         val progress = it / gestureDurationMillis.toFloat()
-                        val y = sin(progress * Math.PI).toFloat() * 100.dp.toPx()
+                        val y = sin(progress * Math.PI).toFloat() * gestureHeight
                         Offset(centerX, y)
                     },
                     gestureDurationMillis,
                 )
-            }
+            },
+            "verticalReveal_gesture_magneticDetachAndReattach",
         )
     }
 
@@ -107,7 +131,8 @@
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneClosed) {
                 swipeDown(endY = 200.dp.toPx(), durationMillis = 500)
-            }
+            },
+            "verticalReveal_gesture_dragOpen",
         )
     }
 
@@ -117,7 +142,8 @@
             GestureRevealMotion(SceneClosed) {
                 val end = Offset(centerX, 80.dp.toPx())
                 swipeWithVelocity(start = topCenter, end = end, endVelocity = FlingVelocity.toPx())
-            }
+            },
+            "verticalReveal_gesture_flingOpen",
         )
     }
 
@@ -126,7 +152,8 @@
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneOpen) {
                 swipeUp(200.dp.toPx(), 0.dp.toPx(), durationMillis = 500)
-            }
+            },
+            "verticalReveal_gesture_dragFullyClose",
         )
     }
 
@@ -134,8 +161,9 @@
     fun verticalReveal_gesture_dragHalfClose() {
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneOpen) {
-                swipeUp(350.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
-            }
+                swipeUp(250.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
+            },
+            "verticalReveal_gesture_dragHalfClose",
         )
     }
 
@@ -146,7 +174,8 @@
                 val start = Offset(centerX, 260.dp.toPx())
                 val end = Offset(centerX, 200.dp.toPx())
                 swipeWithVelocity(start, end, FlingVelocity.toPx())
-            }
+            },
+            "verticalReveal_gesture_flingClose",
         )
     }
 
@@ -164,11 +193,14 @@
         val gestureControl: TouchInjectionScope.() -> Unit,
     ) : RevealMotion
 
-    private fun assertVerticalContainerRevealMotion(testInstructions: RevealMotion) =
+    private fun assertVerticalContainerRevealMotion(
+        testInstructions: RevealMotion,
+        goldenName: String,
+    ) =
         motionRule.runTest {
             val transitions = transitions {
                 from(SceneClosed, to = SceneOpen) {
-                    verticalContainerReveal(RevealElement, MotionSpec, fakeHaptics)
+                    verticalContainerReveal(RevealElement, motionSpec, fakeHaptics)
                 }
             }
 
@@ -221,9 +253,9 @@
                         SceneTransitionLayoutForTesting(
                             state,
                             modifier =
-                                Modifier.padding(50.dp)
+                                Modifier.padding(5.dp)
                                     .background(Color.Yellow)
-                                    .size(ContainerSize.width, ContainerSize.height + 200.dp)
+                                    .size(ContainerSize.width, ContainerSize.height + 100.dp)
                                     .testTag("stl"),
                         ) {
                             scene(
@@ -241,7 +273,7 @@
                     recordingSpec,
                 )
 
-            assertThat(motion).timeSeriesMatchesGolden()
+            assertThat(motion).timeSeriesMatchesGolden(goldenName)
         }
 
     @Composable
@@ -256,7 +288,7 @@
                 modifier =
                     Modifier.element(RevealElement)
                         .size(ContainerSize)
-                        .edgeContainerExpansionBackground(Color.DarkGray, MotionSpec)
+                        .verticalExpandContainerBackground(Color.DarkGray, motionSpec)
             )
         }
     }
@@ -266,7 +298,9 @@
     }
 
     companion object {
-        val ContainerSize = DpSize(200.dp, 400.dp)
+        @get:Parameters @JvmStatic val parameterValues = listOf(true, false)
+
+        val ContainerSize = DpSize(150.dp, 300.dp)
 
         val FlingVelocity = 1000.dp // dp/sec
 
@@ -274,6 +308,5 @@
         val SceneOpen = SceneKey("SceneB")
 
         val RevealElement = ElementKey("RevealElement")
-        val MotionSpec = EdgeContainerExpansionSpec()
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 37acbe2..5f71b19 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -22,10 +22,10 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -111,7 +111,7 @@
 
             override fun onZenDataChanged(data: ZenData) {}
 
-            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+            override fun onFontAxesChanged(axes: ClockAxisStyle) {
                 view.updateAxes(axes)
             }
 
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 bc4bdf4..3cfa78d 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
@@ -26,6 +26,7 @@
 import com.android.systemui.log.core.MessageBuffer
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockConfig
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockEventListener
@@ -33,7 +34,6 @@
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
@@ -232,7 +232,7 @@
 
         override fun onZenDataChanged(data: ZenData) {}
 
-        override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {}
+        override fun onFontAxesChanged(axes: ClockAxisStyle) {}
     }
 
     open inner class DefaultClockAnimations(
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 c3935e6..d778bc0 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
@@ -20,6 +20,8 @@
 import android.view.LayoutInflater
 import com.android.systemui.customization.R
 import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AxisPresetConfig
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
 import com.android.systemui.plugins.clocks.ClockLogger
@@ -28,7 +30,7 @@
 import com.android.systemui.plugins.clocks.ClockPickerConfig
 import com.android.systemui.plugins.clocks.ClockProvider
 import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.shared.clocks.FlexClockController.Companion.AXIS_PRESETS
+import com.android.systemui.shared.clocks.FlexClockController.Companion.buildPresetGroup
 import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes
 
 private val TAG = DefaultClockProvider::class.simpleName
@@ -80,7 +82,7 @@
         return if (isClockReactiveVariantsEnabled) {
             val buffers = messageBuffers ?: ClockMessageBuffers(ClockLogger.DEFAULT_MESSAGE_BUFFER)
             val fontAxes = getDefaultAxes(settings).merge(settings.axes)
-            val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
+            val clockSettings = settings.copy(axes = ClockAxisStyle(fontAxes))
             val typefaceCache =
                 TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) {
                     FLEX_TYPEFACE
@@ -106,17 +108,35 @@
             throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
         }
 
-        return ClockPickerConfig(
-            settings.clockId ?: DEFAULT_CLOCK_ID,
-            resources.getString(R.string.clock_default_name),
-            resources.getString(R.string.clock_default_description),
-            resources.getDrawable(R.drawable.clock_default_thumbnail, null),
-            isReactiveToTone = true,
-            axes =
-                if (!isClockReactiveVariantsEnabled) emptyList()
-                else getDefaultAxes(settings).merge(settings.axes),
-            axisPresets = if (!isClockReactiveVariantsEnabled) emptyList() else AXIS_PRESETS,
-        )
+        if (!isClockReactiveVariantsEnabled) {
+            return ClockPickerConfig(
+                settings.clockId ?: DEFAULT_CLOCK_ID,
+                resources.getString(R.string.clock_default_name),
+                resources.getString(R.string.clock_default_description),
+                resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+                isReactiveToTone = true,
+                axes = emptyList(),
+                presetConfig = null,
+            )
+        } else {
+            val fontAxes = getDefaultAxes(settings).merge(settings.axes)
+            return ClockPickerConfig(
+                settings.clockId ?: DEFAULT_CLOCK_ID,
+                resources.getString(R.string.clock_default_name),
+                resources.getString(R.string.clock_default_description),
+                resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+                isReactiveToTone = true,
+                axes = fontAxes,
+                presetConfig =
+                    AxisPresetConfig(
+                            listOf(
+                                buildPresetGroup(resources, isRound = true),
+                                buildPresetGroup(resources, isRound = false),
+                            )
+                        )
+                        .let { cfg -> cfg.copy(current = cfg.findStyle(ClockAxisStyle(fontAxes))) },
+            )
+        }
     }
 
     companion object {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 5acd446..96c3ac7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,20 +16,24 @@
 
 package com.android.systemui.shared.clocks
 
+import android.content.res.Resources
 import com.android.systemui.animation.GSFAxes
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.AxisPresetConfig
 import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockConfig
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockEventListener
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFontAxis
 import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.put
+import com.android.systemui.shared.clocks.FontUtils.toClockAxis
 import com.android.systemui.shared.clocks.view.FlexClockView
 import java.io.PrintWriter
 import java.util.Locale
@@ -96,8 +100,8 @@
                 largeClock.events.onZenDataChanged(data)
             }
 
-            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
-                val fontAxes = getDefaultAxes(clockCtx.settings).merge(axes).map { it.toSetting() }
+            override fun onFontAxesChanged(axes: ClockAxisStyle) {
+                val fontAxes = ClockAxisStyle(getDefaultAxes(clockCtx.settings).merge(axes))
                 smallClock.events.onFontAxesChanged(fontAxes)
                 largeClock.events.onFontAxesChanged(fontAxes)
             }
@@ -162,66 +166,46 @@
                 ),
             )
 
-        private val LEGACY_FLEX_SETTINGS =
-            listOf(
-                GSFAxes.WEIGHT.toClockAxisSetting(600f),
-                GSFAxes.WIDTH.toClockAxisSetting(100f),
-                GSFAxes.ROUND.toClockAxisSetting(100f),
-                GSFAxes.SLANT.toClockAxisSetting(0f),
-            )
+        private val LEGACY_FLEX_SETTINGS = ClockAxisStyle {
+            put(GSFAxes.WEIGHT, 600f)
+            put(GSFAxes.WIDTH, 100f)
+            put(GSFAxes.ROUND, 100f)
+            put(GSFAxes.SLANT, 0f)
+        }
 
-        val AXIS_PRESETS =
-            listOf(
-                FONT_AXES.map { it.toSetting() },
-                LEGACY_FLEX_SETTINGS,
-                listOf( // Porcelain
-                    GSFAxes.WEIGHT.toClockAxisSetting(500f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Midnight
-                    GSFAxes.WEIGHT.toClockAxisSetting(300f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(-10f),
-                ),
-                listOf( // Sterling
-                    GSFAxes.WEIGHT.toClockAxisSetting(1000f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Smoky Green
-                    GSFAxes.WEIGHT.toClockAxisSetting(150f),
-                    GSFAxes.WIDTH.toClockAxisSetting(50f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Iris
-                    GSFAxes.WEIGHT.toClockAxisSetting(500f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Margarita
-                    GSFAxes.WEIGHT.toClockAxisSetting(300f),
-                    GSFAxes.WIDTH.toClockAxisSetting(30f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(-10f),
-                ),
-                listOf( // Raspberry
-                    GSFAxes.WEIGHT.toClockAxisSetting(700f),
-                    GSFAxes.WIDTH.toClockAxisSetting(140f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(-7f),
-                ),
-                listOf( // Ultra Blue
-                    GSFAxes.WEIGHT.toClockAxisSetting(850f),
-                    GSFAxes.WIDTH.toClockAxisSetting(130f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
+        private val PRESET_COUNT = 8
+        private val PRESET_WIDTH_INIT = 30f
+        private val PRESET_WIDTH_STEP = 12.5f
+        private val PRESET_WEIGHT_INIT = 800f
+        private val PRESET_WEIGHT_STEP = -100f
+        private val BASE_PRESETS: List<ClockAxisStyle> = run {
+            val presets = mutableListOf<ClockAxisStyle>()
+            var weight = PRESET_WEIGHT_INIT
+            var width = PRESET_WIDTH_INIT
+            for (i in 1..PRESET_COUNT) {
+                presets.add(
+                    ClockAxisStyle {
+                        put(GSFAxes.WEIGHT, weight)
+                        put(GSFAxes.WIDTH, width)
+                        put(GSFAxes.ROUND, 0f)
+                        put(GSFAxes.SLANT, 0f)
+                    }
+                )
+
+                weight += PRESET_WEIGHT_STEP
+                width += PRESET_WIDTH_STEP
+            }
+
+            return@run presets
+        }
+
+        fun buildPresetGroup(resources: Resources, isRound: Boolean): AxisPresetConfig.Group {
+            val round = if (isRound) GSFAxes.ROUND.maxValue else GSFAxes.ROUND.minValue
+            return AxisPresetConfig.Group(
+                presets = BASE_PRESETS.map { it.copy { put(GSFAxes.ROUND, round) } },
+                // TODO(b/395647577): Placeholder Icon; Replace or remove
+                icon = resources.getDrawable(R.drawable.clock_default_thumbnail, null),
             )
+        }
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 578a489..171a68f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -25,16 +25,18 @@
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
 import com.android.systemui.plugins.clocks.ClockFaceLayout
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.get
+import com.android.systemui.shared.clocks.FontUtils.set
 import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
 import com.android.systemui.shared.clocks.view.FlexClockView
 import com.android.systemui.shared.clocks.view.HorizontalAlignment
@@ -129,17 +131,10 @@
             layerController.faceEvents.onThemeChanged(theme)
         }
 
-        override fun onFontAxesChanged(settings: List<ClockFontAxisSetting>) {
-            var axes = settings
-            if (!isLargeClock) {
-                axes =
-                    axes.map { axis ->
-                        if (axis.key == GSFAxes.WIDTH.tag && axis.value > SMALL_CLOCK_MAX_WDTH) {
-                            axis.copy(value = SMALL_CLOCK_MAX_WDTH)
-                        } else {
-                            axis
-                        }
-                    }
+        override fun onFontAxesChanged(settings: ClockAxisStyle) {
+            var axes = ClockAxisStyle(settings)
+            if (!isLargeClock && axes[GSFAxes.WIDTH] > SMALL_CLOCK_MAX_WDTH) {
+                axes[GSFAxes.WIDTH] = SMALL_CLOCK_MAX_WDTH
             }
 
             layerController.events.onFontAxesChanged(axes)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
index 212b1e2..722d76b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
@@ -18,26 +18,36 @@
 
 import com.android.systemui.animation.AxisDefinition
 import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockFontAxis
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 
-fun AxisDefinition.toClockAxis(
-    type: AxisType,
-    currentValue: Float? = null,
-    name: String,
-    description: String,
-): ClockFontAxis {
-    return ClockFontAxis(
-        key = this.tag,
-        type = type,
-        maxValue = this.maxValue,
-        minValue = this.minValue,
-        currentValue = currentValue ?: this.defaultValue,
-        name = name,
-        description = description,
-    )
-}
+object FontUtils {
+    fun AxisDefinition.toClockAxis(
+        type: AxisType,
+        currentValue: Float? = null,
+        name: String,
+        description: String,
+    ): ClockFontAxis {
+        return ClockFontAxis(
+            key = this.tag,
+            type = type,
+            maxValue = this.maxValue,
+            minValue = this.minValue,
+            currentValue = currentValue ?: this.defaultValue,
+            name = name,
+            description = description,
+        )
+    }
 
-fun AxisDefinition.toClockAxisSetting(value: Float? = null): ClockFontAxisSetting {
-    return ClockFontAxisSetting(this.tag, value ?: this.defaultValue)
+    fun ClockAxisStyle.put(def: AxisDefinition, value: Float? = null) {
+        this.put(def.tag, value ?: def.defaultValue)
+    }
+
+    operator fun ClockAxisStyle.set(def: AxisDefinition, value: Float) {
+        this[def.tag] = value
+    }
+
+    operator fun ClockAxisStyle.get(def: AxisDefinition): Float {
+        return this[def.tag] ?: def.defaultValue
+    }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 1d963af..7be9a93 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -26,10 +26,10 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -172,7 +172,7 @@
 
             override fun onZenDataChanged(data: ZenData) {}
 
-            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+            override fun onFontAxesChanged(axes: ClockAxisStyle) {
                 view.updateAxes(axes)
             }
         }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index ba32ab0..4531aed 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -26,7 +26,7 @@
 import androidx.core.view.children
 import com.android.app.animation.Interpolators
 import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockLogger
 import com.android.systemui.plugins.clocks.VPoint
 import com.android.systemui.plugins.clocks.VPointF
@@ -272,7 +272,7 @@
         invalidate()
     }
 
-    fun updateAxes(axes: List<ClockFontAxisSetting>) {
+    fun updateAxes(axes: ClockAxisStyle) {
         childViews.forEach { view -> view.updateAxes(axes) }
         requestLayout()
     }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index da9e26a..377a24c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -36,13 +36,12 @@
 import android.widget.TextView
 import com.android.app.animation.Interpolators
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.AxisDefinition
 import com.android.systemui.animation.GSFAxes
 import com.android.systemui.animation.TextAnimator
 import com.android.systemui.animation.TextAnimatorListener
 import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.replace
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.toFVar
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockLogger
 import com.android.systemui.plugins.clocks.VPoint
 import com.android.systemui.plugins.clocks.VPointF
@@ -56,9 +55,9 @@
 import com.android.systemui.shared.clocks.DimensionParser
 import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
 import com.android.systemui.shared.clocks.FontTextStyle
+import com.android.systemui.shared.clocks.FontUtils.set
 import com.android.systemui.shared.clocks.ViewUtils.measuredSize
 import com.android.systemui.shared.clocks.ViewUtils.size
-import com.android.systemui.shared.clocks.toClockAxisSetting
 import java.lang.Thread
 import kotlin.math.max
 import kotlin.math.min
@@ -123,9 +122,9 @@
     private val isLegacyFlex = clockCtx.settings.clockId == FLEX_CLOCK_ID
     private val fixedAodAxes =
         when {
-            !isLegacyFlex -> listOf(AOD_WEIGHT_AXIS, WIDTH_AXIS)
-            isLargeClock -> listOf(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
-            else -> listOf(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+            !isLegacyFlex -> fromAxes(AOD_WEIGHT_AXIS, WIDTH_AXIS)
+            isLargeClock -> fromAxes(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+            else -> fromAxes(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
         }
 
     private var lsFontVariation: String
@@ -135,11 +134,11 @@
     init {
         val roundAxis = if (!isLegacyFlex) ROUND_AXIS else FLEX_ROUND_AXIS
         val lsFontAxes =
-            if (!isLegacyFlex) listOf(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
-            else listOf(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
+            if (!isLegacyFlex) fromAxes(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
+            else fromAxes(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
 
         lsFontVariation = lsFontAxes.toFVar()
-        aodFontVariation = (fixedAodAxes + listOf(roundAxis, SLANT_AXIS)).toFVar()
+        aodFontVariation = fixedAodAxes.copyWith(fromAxes(roundAxis, SLANT_AXIS)).toFVar()
         fidgetFontVariation = buildFidgetVariation(lsFontAxes).toFVar()
     }
 
@@ -201,9 +200,9 @@
         invalidate()
     }
 
-    fun updateAxes(lsAxes: List<ClockFontAxisSetting>) {
+    fun updateAxes(lsAxes: ClockAxisStyle) {
         lsFontVariation = lsAxes.toFVar()
-        aodFontVariation = lsAxes.replace(fixedAodAxes).toFVar()
+        aodFontVariation = lsAxes.copyWith(fixedAodAxes).toFVar()
         fidgetFontVariation = buildFidgetVariation(lsAxes).toFVar()
         logger.updateAxes(lsFontVariation, aodFontVariation)
 
@@ -220,19 +219,16 @@
         invalidate()
     }
 
-    fun buildFidgetVariation(axes: List<ClockFontAxisSetting>): List<ClockFontAxisSetting> {
-        val result = mutableListOf<ClockFontAxisSetting>()
-        for (axis in axes) {
-            result.add(
-                FIDGET_DISTS.get(axis.key)?.let { (dist, midpoint) ->
-                    ClockFontAxisSetting(
-                        axis.key,
-                        axis.value + dist * if (axis.value > midpoint) -1 else 1,
-                    )
-                } ?: axis
-            )
-        }
-        return result
+    fun buildFidgetVariation(axes: ClockAxisStyle): ClockAxisStyle {
+        return ClockAxisStyle(
+            axes.items
+                .map { (key, value) ->
+                    FIDGET_DISTS.get(key)?.let { (dist, midpoint) ->
+                        key to value + dist * if (value > midpoint) -1 else 1
+                    } ?: (key to value)
+                }
+                .toMap()
+        )
     }
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -666,18 +662,22 @@
             )
 
         val AOD_COLOR = Color.WHITE
-        val LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(400f)
-        val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(200f)
-        val WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(85f)
-        val ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(0f)
-        val SLANT_AXIS = GSFAxes.SLANT.toClockAxisSetting(0f)
+        private val LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 400f
+        private val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT to 200f
+        private val WIDTH_AXIS = GSFAxes.WIDTH to 85f
+        private val ROUND_AXIS = GSFAxes.ROUND to 0f
+        private val SLANT_AXIS = GSFAxes.SLANT to 0f
 
         // Axes for Legacy version of the Flex Clock
-        val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(600f)
-        val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(74f)
-        val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(133f)
-        val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(100f)
-        val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(43f)
-        val FLEX_ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(100f)
+        private val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 600f
+        private val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT to 74f
+        private val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT to 133f
+        private val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH to 100f
+        private val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH to 43f
+        private val FLEX_ROUND_AXIS = GSFAxes.ROUND to 100f
+
+        private fun fromAxes(vararg axes: Pair<AxisDefinition, Float>): ClockAxisStyle {
+            return ClockAxisStyle(axes.map { (def, value) -> def.tag to value }.toMap())
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 14d34d7..162218d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -87,7 +87,7 @@
                 contentResolver,
                 selectedUserInteractor,
                 lazyKeyguardUpdateMonitor,
-                dumpManager
+                dumpManager,
             )
     }
 
@@ -116,9 +116,9 @@
             )
         )
         assertFalse(
-                activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
-                )
+            activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+            )
         )
         assertTrue(
             activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -212,7 +212,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "",
-            currentUser
+            currentUser,
         )
         updateSetting(
             secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -285,7 +285,7 @@
             ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
             "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
                 "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
 
@@ -328,7 +328,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "${ActiveUnlockConfig.BiometricType.NONE.intValue}",
-            currentUser
+            currentUser,
         )
         updateSetting(
             secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -358,7 +358,7 @@
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
                 "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
-            currentUser
+            currentUser,
         )
         updateSetting(
             secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -397,10 +397,10 @@
     @Test
     fun isWakeupConsideredUnlockIntent_singleValue() {
         // GIVEN lift is considered an unlock intent
-        secureSettings.putIntForUser(
+        secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
-            PowerManager.WAKE_REASON_LIFT,
-            currentUser
+            PowerManager.WAKE_REASON_LIFT.toString(),
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
@@ -422,7 +422,7 @@
             PowerManager.WAKE_REASON_LIFT.toString() +
                 "|" +
                 PowerManager.WAKE_REASON_TAP.toString(),
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
@@ -452,7 +452,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
             " ",
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
@@ -479,7 +479,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
             PowerManager.WAKE_REASON_LIFT.toString(),
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
 
@@ -501,7 +501,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
             " ",
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
 
@@ -521,7 +521,7 @@
             PowerManager.WAKE_REASON_LIFT.toString() +
                 "|" +
                 PowerManager.WAKE_REASON_TAP.toString(),
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
 
@@ -544,7 +544,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "-1",
-            currentUser
+            currentUser,
         )
 
         // WHEN the setting updates
@@ -581,7 +581,7 @@
                 eq(uri),
                 eq(false),
                 capture(settingsObserverCaptor),
-                eq(UserHandle.USER_ALL)
+                eq(UserHandle.USER_ALL),
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 245388c..b0db8b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -22,12 +22,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.testKosmos
 import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
@@ -47,16 +47,14 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardUnfoldTransitionTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private val progressProvider: FakeUnfoldTransitionProvider =
         kosmos.fakeUnfoldTransitionProgressProvider
 
-    @Mock
-    private lateinit var keyguardRootView: KeyguardRootView
+    @Mock private lateinit var keyguardRootView: KeyguardRootView
 
-    @Mock
-    private lateinit var notificationShadeWindowView: NotificationShadeWindowView
+    @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
 
     @Mock private lateinit var statusBarStateController: StatusBarStateController
 
@@ -71,10 +69,14 @@
         xTranslationMax =
             context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
 
-        underTest = KeyguardUnfoldTransition(
-            context, keyguardRootView, notificationShadeWindowView,
-            statusBarStateController, progressProvider
-        )
+        underTest =
+            KeyguardUnfoldTransition(
+                context,
+                keyguardRootView,
+                notificationShadeWindowView,
+                statusBarStateController,
+                progressProvider,
+            )
 
         underTest.setup()
         underTest.statusViewCentered = false
@@ -88,9 +90,8 @@
         underTest.statusViewCentered = true
 
         val view = View(context)
-        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
-            view
-        )
+        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+            .thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
@@ -110,9 +111,8 @@
         whenever(statusBarStateController.getState()).thenReturn(SHADE)
 
         val view = View(context)
-        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
-            view
-        )
+        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+            .thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
@@ -133,9 +133,11 @@
 
         val view = View(context)
         whenever(
-            notificationShadeWindowView
-                .findViewById<View>(customR.id.lockscreen_clock_view_large)
-        ).thenReturn(view)
+                notificationShadeWindowView.findViewById<View>(
+                    customR.id.lockscreen_clock_view_large
+                )
+            )
+            .thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
index cde42bd..7660623 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
@@ -23,7 +23,6 @@
 import com.android.internal.accessibility.AccessibilityShortcutController
 import com.android.internal.accessibility.common.ShortcutConstants
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -31,6 +30,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@
 
     private lateinit var extraDimDialogDelegate: ExtraDimDialogDelegate
 
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
 
     @Mock private lateinit var dialog: SystemUIDialog
@@ -79,7 +79,7 @@
                 kosmos.testDispatcher,
                 dialogFactory,
                 accessibilityManager,
-                userTracker
+                userTracker,
             )
     }
 
@@ -94,7 +94,7 @@
             verify(dialog)
                 .setPositiveButton(
                     eq(R.string.accessibility_deprecate_extra_dim_dialog_button),
-                    clickListener.capture()
+                    clickListener.capture(),
                 )
 
             clickListener.firstValue.onClick(dialog, 0)
@@ -110,7 +110,7 @@
                                 .flattenToString()
                         )
                     ),
-                    anyInt()
+                    anyInt(),
                 )
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 7e4704a..d118ace 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -141,6 +141,10 @@
     @Mock
     private QSSettingsPackageRepository mQSSettingsPackageRepository;
     @Mock
+    private HearingDevicesInputRoutingController.Factory mInputRoutingFactory;
+    @Mock
+    private HearingDevicesInputRoutingController mInputRoutingController;
+    @Mock
     private CachedBluetoothDevice mCachedDevice;
     @Mock
     private BluetoothDevice mDevice;
@@ -184,6 +188,7 @@
         when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
         when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
         when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
+        when(mInputRoutingFactory.create(any())).thenReturn(mInputRoutingController);
 
         mContext.setMockPackageManager(mPackageManager);
     }
@@ -349,6 +354,7 @@
 
         setUpDeviceDialogWithoutPairNewDeviceButton();
         mDialog.show();
+        mExecutor.runAllReady();
 
         ViewGroup ambientLayout = getAmbientLayout(mDialog);
         assertThat(ambientLayout.getVisibility()).isEqualTo(View.VISIBLE);
@@ -401,7 +407,8 @@
                 mExecutor,
                 mAudioManager,
                 mUiEventLogger,
-                mQSSettingsPackageRepository
+                mQSSettingsPackageRepository,
+                mInputRoutingFactory
         );
         mDialog = mDialogDelegate.createDialog();
     }
@@ -438,7 +445,6 @@
         return dialog.requireViewById(R.id.ambient_layout);
     }
 
-
     private int countChildWithoutSpace(ViewGroup viewGroup) {
         int spaceCount = 0;
         for (int i = 0; i < viewGroup.getChildCount(); i++) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
new file mode 100644
index 0000000..0a41b77
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid
+
+import android.media.AudioDeviceInfo
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesInputRoutingController.InputRoutingControlAvailableCallback
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+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.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesInputRoutingControllerTest : SysuiTestCase() {
+
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private var hapClientProfile: HapClientProfile = mock()
+    private var cachedDevice: CachedBluetoothDevice = mock()
+    private var memberCachedDevice: CachedBluetoothDevice = mock()
+    private var btDevice: android.bluetooth.BluetoothDevice = mock()
+    private var audioManager: AudioManager = mock()
+    private lateinit var underTest: HearingDevicesInputRoutingController
+    private val testDispatcher = kosmos.testDispatcher
+
+    @Before
+    fun setUp() {
+        hapClientProfile.stub { on { isProfileReady } doReturn true }
+        cachedDevice.stub {
+            on { device } doReturn btDevice
+            on { profiles } doReturn listOf(hapClientProfile)
+        }
+        memberCachedDevice.stub {
+            on { device } doReturn btDevice
+            on { profiles } doReturn listOf(hapClientProfile)
+        }
+
+        underTest = HearingDevicesInputRoutingController(mContext, audioManager, testDispatcher)
+        underTest.setDevice(cachedDevice)
+    }
+
+    @Test
+    fun isInputRoutingControlAvailable_validInput_supportHapProfile_returnTrue() {
+        testScope.runTest {
+            val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn listOf(hapClientProfile)
+            }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isTrue()
+        }
+    }
+
+    @Test
+    fun isInputRoutingControlAvailable_notSupportHapProfile_returnFalse() {
+        testScope.runTest {
+            val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn emptyList()
+            }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isFalse()
+        }
+    }
+
+    @Test
+    fun isInputRoutingControlAvailable_validInputMember_supportHapProfile_returnTrue() {
+        testScope.runTest {
+            val mockInfoAddress2 = arrayOf(mockTestAddressInfo(TEST_ADDRESS_2))
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn listOf(hapClientProfile)
+                on { memberDevice } doReturn (setOf(memberCachedDevice))
+            }
+            memberCachedDevice.stub { on { address } doReturn TEST_ADDRESS_2 }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress2
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isTrue()
+        }
+    }
+
+    @Test
+    fun isAvailable_notValidInputDevice_returnFalse() {
+        testScope.runTest {
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn listOf(hapClientProfile)
+            }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn emptyArray()
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isFalse()
+        }
+    }
+
+    @Test
+    fun selectInputRouting_builtinMic_setMicrophonePreferredForCallsFalse() {
+        underTest.selectInputRouting(
+            HearingDevicesInputRoutingController.InputRoutingValue.BUILTIN_MIC.ordinal
+        )
+
+        verify(btDevice).isMicrophonePreferredForCalls = false
+    }
+
+    private fun mockTestAddressInfo(address: String): AudioDeviceInfo {
+        val info: AudioDeviceInfo = mock()
+        info.stub {
+            on { type } doReturn AudioDeviceInfo.TYPE_BLE_HEADSET
+            on { this.address } doReturn address
+        }
+
+        return info
+    }
+
+    companion object {
+        private const val TEST_ADDRESS = "55:66:77:88:99:AA"
+        private const val TEST_ADDRESS_2 = "55:66:77:88:99:BB"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8c5fad3..8573312 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -50,6 +49,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -74,7 +74,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BackActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val executor = FakeExecutor(FakeSystemClock())
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
index 648d74d..29a0b69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CameraAutoRotateRepositoryImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val settings = kosmos.fakeSettings
     private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
index b73a212..2e357d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@
 @RunWith(AndroidJUnit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
 class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testUser = UserHandle.of(1)
     private val privacyManager = mock<SensorPrivacyManager>()
@@ -46,7 +46,7 @@
         CameraSensorPrivacyRepositoryImpl(
             testScope.testScheduler,
             testScope.backgroundScope,
-            privacyManager
+            privacyManager,
         )
 
     @Test
@@ -87,7 +87,7 @@
                 .addSensorPrivacyListener(
                     ArgumentMatchers.eq(SensorPrivacyManager.Sensors.CAMERA),
                     ArgumentMatchers.eq(testUser.identifier),
-                    captor.capture()
+                    captor.capture(),
                 )
             val sensorPrivacyCallback = captor.value!!
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
index 6c8097e..b3d8983 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@
 @RunWith(AndroidJUnit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
 class FakeCameraAutoRotateRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val underTest = kosmos.fakeCameraAutoRotateRepository
     private val testUser = UserHandle.of(1)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
index 7161c2c..6b9a7de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@
 @RunWith(AndroidJUnit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
 class FakeCameraSensorPrivacyRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val underTest = kosmos.fakeCameraSensorPrivacyRepository
     private val testUser = UserHandle.of(1)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
index 239e026..652a2ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -25,6 +25,10 @@
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
@@ -33,7 +37,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ActionIntentCreatorTest : SysuiTestCase() {
-    val creator = ActionIntentCreator()
+    private val scheduler = TestCoroutineScheduler()
+    private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+    private val testScope = TestScope(mainDispatcher)
+
+    val creator = ActionIntentCreator(testScope.backgroundScope)
 
     @Test
     fun test_getTextEditorIntent() {
@@ -65,7 +73,7 @@
     }
 
     @Test
-    fun test_getImageEditIntent() {
+    fun test_getImageEditIntent() = runTest {
         context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
         val fakeUri = Uri.parse("content://foo")
         var intent = creator.getImageEditIntent(fakeUri, context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index 126b3fa..10c5c52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -34,6 +34,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DefaultIntentCreatorTest extends SysuiTestCase {
@@ -73,12 +75,16 @@
     }
 
     @Test
-    public void test_getImageEditIntent() {
+    public void test_getImageEditIntentAsync() {
         getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                 "");
         Uri fakeUri = Uri.parse("content://foo");
-        Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
+        final AtomicReference<Intent> intentHolder = new AtomicReference<>(null);
+        mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+            intentHolder.set(output);
+        });
 
+        Intent intent = intentHolder.get();
         assertEquals(Intent.ACTION_EDIT, intent.getAction());
         assertEquals("image/*", intent.getType());
         assertEquals(null, intent.getComponent());
@@ -90,8 +96,10 @@
                 "com.android.remotecopy.RemoteCopyActivity");
         getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                 fakeComponent.flattenToString());
-        intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
-        assertEquals(fakeComponent, intent.getComponent());
+        mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+            intentHolder.set(output);
+        });
+        assertEquals(fakeComponent, intentHolder.get().getComponent());
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index df10d05..b08e676 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.communal.widgets
 
+import android.appwidget.AppWidgetProviderInfo
 import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalEnabled
@@ -49,6 +51,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -65,6 +68,7 @@
 
     private lateinit var appWidgetIdToRemove: MutableSharedFlow<Int>
 
+    private lateinit var communalInteractorSpy: CommunalInteractor
     private lateinit var underTest: CommunalAppWidgetHostStartable
 
     @Before
@@ -78,12 +82,13 @@
         helper = kosmos.fakeGlanceableHubMultiUserHelper
         appWidgetIdToRemove = MutableSharedFlow()
         whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
+        communalInteractorSpy = spy(kosmos.communalInteractor)
 
         underTest =
             CommunalAppWidgetHostStartable(
                 { appWidgetHost },
                 { communalWidgetHost },
-                { kosmos.communalInteractor },
+                { communalInteractorSpy },
                 { kosmos.communalSettingsInteractor },
                 { kosmos.keyguardInteractor },
                 { kosmos.fakeUserTracker },
@@ -259,6 +264,41 @@
         }
 
     @Test
+    fun removeNotLockscreenWidgets_whenCommunalIsAvailable() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is available
+                setCommunalAvailable(true)
+                kosmos.fakeUserTracker.set(
+                    userInfos = listOf(MAIN_USER_INFO),
+                    selectedUserIndex = 0,
+                )
+                fakeCommunalWidgetRepository.addWidget(
+                    appWidgetId = 1,
+                    userId = MAIN_USER_INFO.id,
+                    category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+                )
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+                fakeCommunalWidgetRepository.addWidget(
+                    appWidgetId = 3,
+                    userId = MAIN_USER_INFO.id,
+                    category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+                )
+
+                underTest.start()
+                runCurrent()
+
+                val communalWidgets by
+                    collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+                assertThat(communalWidgets).hasSize(1)
+                assertThat(communalWidgets!![0].appWidgetId).isEqualTo(2)
+
+                verify(communalInteractorSpy).deleteWidget(1)
+                verify(communalInteractorSpy).deleteWidget(3)
+            }
+        }
+
+    @Test
     fun onStartHeadlessSystemUser_registerWidgetManager_whenCommunalIsAvailable() =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 197b0ee..8591375 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -26,6 +26,7 @@
 import android.view.IWindowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
@@ -71,13 +72,19 @@
     // that the initial state (soon after construction) contains the expected ones set in every
     // test.
     private val displayRepository: DisplayRepositoryImpl by lazy {
-        DisplayRepositoryImpl(
+        // TODO b/401305290 - move this to kosmos
+        val displayRepositoryFromLib =
+            com.android.app.displaylib.DisplayRepositoryImpl(
                 displayManager,
+                testHandler,
+                testScope.backgroundScope,
+                UnconfinedTestDispatcher(),
+            )
+        DisplayRepositoryImpl(
                 commandQueue,
                 windowManager,
-                testHandler,
-                TestScope(UnconfinedTestDispatcher()),
-                UnconfinedTestDispatcher(),
+                testScope.backgroundScope,
+                displayRepositoryFromLib,
             )
             .also {
                 verify(displayManager, never()).registerDisplayListener(any(), any())
@@ -403,7 +410,7 @@
             val pendingDisplay by lastPendingDisplay()
 
             sendOnDisplayConnected(1, TYPE_EXTERNAL)
-            val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+            val initialPendingDisplay: PendingDisplay? = pendingDisplay
             assertThat(pendingDisplay).isNotNull()
             sendOnDisplayChanged(1)
 
@@ -416,7 +423,7 @@
             val pendingDisplay by lastPendingDisplay()
 
             sendOnDisplayConnected(1, TYPE_EXTERNAL)
-            val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+            val initialPendingDisplay: PendingDisplay? = pendingDisplay
             assertThat(pendingDisplay).isNotNull()
             sendOnDisplayConnected(2, TYPE_EXTERNAL)
 
@@ -648,7 +655,7 @@
         return flowValue
     }
 
-    private fun TestScope.lastPendingDisplay(): FlowValue<DisplayRepository.PendingDisplay?> {
+    private fun TestScope.lastPendingDisplay(): FlowValue<PendingDisplay?> {
         val flowValue = collectLastValue(displayRepository.pendingDisplay)
         captureAddedRemovedListener()
         verify(displayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index e41d46c..28b9e73 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -19,6 +19,7 @@
 import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.testScope
@@ -31,7 +32,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyString
-import org.mockito.kotlin.eq
+import org.mockito.kotlin.any
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
@@ -105,7 +106,7 @@
 
     @Test
     fun start_registersDumpable() {
-        verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest))
+        verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any())
     }
 
     private fun createDisplay(displayId: Int): Display =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index f2a6c11..7229761 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.education.data.model.EduDeviceConnectionTime
 import com.android.systemui.education.data.model.GestureEduModel
 import com.android.systemui.education.domain.interactor.mockEduInputManager
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import java.io.File
 import javax.inject.Provider
@@ -48,7 +48,7 @@
 class ContextualEducationRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: UserContextualEducationRepository
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val dsScopeProvider: Provider<CoroutineScope> = Provider {
         TestScope(kosmos.testDispatcher).backgroundScope
@@ -70,7 +70,7 @@
                 testContext,
                 dsScopeProvider,
                 kosmos.mockEduInputManager,
-                kosmos.testDispatcher
+                kosmos.testDispatcher,
             )
         underTest.setUser(testUserId)
     }
@@ -109,7 +109,7 @@
                     lastEducationTime = kosmos.fakeEduClock.instant(),
                     usageSessionStartTime = kosmos.fakeEduClock.instant(),
                     userId = testUserId,
-                    gestureType = BACK
+                    gestureType = BACK,
                 )
             underTest.updateGestureEduModel(BACK) { newModel }
             val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
index 5030d1e..0f75e57c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
@@ -21,9 +21,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -45,7 +45,7 @@
 @RunWith(AndroidJUnit4::class)
 class HapticSliderPluginTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var vibratorHelper: VibratorHelper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
index 798c5ab..39b5e81 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
@@ -23,9 +23,9 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.inputdevice.data.model.UserDeviceConnectionStatus
 import com.android.systemui.keyboard.data.repository.keyboardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.user.data.repository.userRepository
@@ -41,7 +41,7 @@
 class UserInputDeviceRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: UserInputDeviceRepository
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyboardRepository = kosmos.keyboardRepository
     private val touchpadRepository = kosmos.touchpadRepository
@@ -54,7 +54,7 @@
                 kosmos.testDispatcher,
                 keyboardRepository,
                 touchpadRepository,
-                kosmos.userRepository
+                kosmos.userRepository,
             )
         userRepository.setUserInfos(USER_INFOS)
     }
@@ -72,7 +72,7 @@
             assertThat(isAnyKeyboardConnected)
                 .containsExactly(
                     UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
-                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
                 )
                 .inOrder()
         }
@@ -90,16 +90,13 @@
             assertThat(isAnyTouchpadConnected)
                 .containsExactly(
                     UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
-                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
                 )
                 .inOrder()
         }
 
     companion object {
         private val USER_INFOS =
-            listOf(
-                UserInfo(100, "First User", 0),
-                UserInfo(101, "Second User", 0),
-            )
+            listOf(UserInfo(100, "First User", 0), UserInfo(101, "Second User", 0))
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
index 274880b..6bd0fb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
@@ -23,9 +23,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@
 
     @Mock private lateinit var inputMethodManager: InputMethodManager
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     private lateinit var underTest: InputMethodRepository
@@ -72,7 +72,7 @@
                     inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
                         any(),
                         anyBoolean(),
-                        eq(USER_HANDLE)
+                        eq(USER_HANDLE),
                     )
                 )
                 .thenReturn(listOf())
@@ -97,7 +97,7 @@
                     inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
                         eq(selectedImiId),
                         anyBoolean(),
-                        eq(USER_HANDLE)
+                        eq(USER_HANDLE),
                     )
                 )
                 .thenReturn(
@@ -125,7 +125,7 @@
             verify(inputMethodManager)
                 .showInputMethodPickerFromSystem(
                     /* showAuxiliarySubtypes = */ eq(true),
-                    /* displayId = */ eq(displayId)
+                    /* displayId = */ eq(displayId),
                 )
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
index 8e6de2f..9e129a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.inputmethod.data.model.InputMethodModel
 import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
 import com.android.systemui.inputmethod.data.repository.inputMethodRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlinx.coroutines.test.runTest
@@ -34,7 +34,7 @@
 @RunWith(AndroidJUnit4::class)
 class InputMethodInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val fakeInputMethodRepository = kosmos.fakeInputMethodRepository
 
@@ -148,7 +148,7 @@
             subtypes =
                 List(auxiliarySubtypes + nonAuxiliarySubtypes) {
                     InputMethodModel.Subtype(subtypeId = it, isAuxiliary = it < auxiliarySubtypes)
-                }
+                },
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 1bb4805..655c646 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
 import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
@@ -43,6 +42,7 @@
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.phone.systemUIDialogFactory
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
@@ -60,7 +60,7 @@
     private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
     private val mockUserContext: Context = mock()
     private val kosmos =
-        Kosmos().also {
+        testKosmos().also {
             it.testCase = this
             it.testDispatcher = UnconfinedTestDispatcher()
             it.shortcutHelperSystemShortcutsSource = fakeSystemSource
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index be9e93c..ba57ffd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -52,19 +52,19 @@
     fun setup() {
         val dialogFactory = mock<StickyKeyDialogFactory>()
         whenever(dialogFactory.create(any())).thenReturn(dialog)
-        val keyboardRepository = Kosmos().keyboardRepository
+        val keyboardRepository = testKosmos().keyboardRepository
         val viewModel =
             StickyKeysIndicatorViewModel(
                 stickyKeysRepository,
                 keyboardRepository,
-                testScope.backgroundScope
+                testScope.backgroundScope,
             )
         coordinator =
             StickyKeysIndicatorCoordinator(
                 testScope.backgroundScope,
                 dialogFactory,
                 viewModel,
-                mock<StickyKeysLogger>()
+                mock<StickyKeysLogger>(),
             )
         coordinator.startListening()
         keyboardRepository.setIsAnyKeyboardConnected(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9daf0ff..1c00419 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
@@ -64,7 +63,7 @@
     private val inputManager = mock<InputManager>()
     private val keyboardRepository = FakeKeyboardRepository()
     private val secureSettings = kosmos.fakeSettings
-    private val userRepository = Kosmos().fakeUserRepository
+    private val userRepository = testKosmos().fakeUserRepository
     private val captor =
         ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index baf3b5b..3f1cadc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -23,12 +23,12 @@
 import com.android.systemui.defaultDeviceState
 import com.android.systemui.deviceStateManager
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
 import java.util.function.Predicate
@@ -68,7 +68,7 @@
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
     @Mock private lateinit var powerManager: PowerManager
     @Mock private lateinit var wallpaperManager: WallpaperManager
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val deviceStateManager = kosmos.deviceStateManager
 
     @Mock
@@ -92,7 +92,7 @@
             surfaceControl1,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
 
     private var surfaceControl2 = mock(SurfaceControl::class.java)
@@ -113,7 +113,7 @@
             surfaceControl2,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
     private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
 
@@ -135,7 +135,7 @@
             surfaceControlWp,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
     private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
 
@@ -157,7 +157,7 @@
             surfaceControlLockWp,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
     private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget>
     private var shouldPerformSmartspaceTransition = false
@@ -179,14 +179,14 @@
                     notificationShadeWindowController,
                     powerManager,
                     wallpaperManager,
-                    deviceStateManager
+                    deviceStateManager,
                 ) {
                 override fun shouldPerformSmartspaceTransition(): Boolean =
                     shouldPerformSmartspaceTransition
             }
         keyguardUnlockAnimationController.setLauncherUnlockController(
             "",
-            launcherUnlockAnimationController
+            launcherUnlockAnimationController,
         )
 
         whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
@@ -227,7 +227,7 @@
             arrayOf(),
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
@@ -259,7 +259,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Since the animation is running, we should not have finished the remote animation.
@@ -282,7 +282,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
@@ -303,7 +303,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
@@ -327,7 +327,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            true /* requestedShowSurfaceBehindKeyguard */
+            true, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
@@ -351,7 +351,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            true /* requestedShowSurfaceBehindKeyguard */
+            true, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -373,7 +373,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -389,7 +389,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            true /* requestedShowSurfaceBehindKeyguard */
+            true, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
@@ -406,7 +406,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -427,7 +427,7 @@
             wallpaperTargets,
             lockWallpaperTargets,
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         for (i in 0..10) {
@@ -471,7 +471,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -492,7 +492,7 @@
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
         verify(
                 surfaceTransactionApplier,
-                times(1).description("WallpaperSurface was expected to receive scheduleApply once")
+                times(1).description("WallpaperSurface was expected to receive scheduleApply once"),
             )
             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
@@ -523,7 +523,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -539,7 +539,7 @@
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
         verify(
                 surfaceTransactionApplier,
-                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
+                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply"),
             )
             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
@@ -562,7 +562,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Stop the animator - we just want to test whether the override is not applied.
@@ -578,7 +578,7 @@
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
         verify(
                 surfaceTransactionApplier,
-                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
+                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply"),
             )
             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
@@ -588,7 +588,7 @@
         assertEquals(
             "Wallpaper surface was expected to have opacity 1",
             1f,
-            captorWp.getLastValue().alpha
+            captorWp.getLastValue().alpha,
         )
 
         verifyNoMoreInteractions(surfaceTransactionApplier)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 97c746c..d0762a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -25,10 +25,13 @@
 import android.view.RemoteAnimationTarget
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.window.flags.Flags
@@ -63,6 +66,9 @@
     @Mock
     private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
     @Mock private lateinit var keyguardTransitions: KeyguardTransitions
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+    @Mock private lateinit var keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor
+    @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
 
     @Before
     fun setUp() {
@@ -77,6 +83,9 @@
                 keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
                 keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
                 keyguardTransitions = keyguardTransitions,
+                selectedUserInteractor = selectedUserInteractor,
+                lockPatternUtils = lockPatternUtils,
+                keyguardShowWhileAwakeInteractor = keyguardShowWhileAwakeInteractor,
             )
     }
 
@@ -236,6 +245,8 @@
             .whenever(keyguardDismissTransitionInteractor)
             .startDismissKeyguardTransition(any(), any())
 
+        whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(-1)
+
         underTest.onKeyguardGoingAwayRemoteAnimationStart(
             transit = 0,
             apps = arrayOf(mock<RemoteAnimationTarget>()),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
index 6bc8000..04f7fe1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
@@ -21,10 +21,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.policy.CastDevice
 import com.android.systemui.statusbar.policy.fakeCastController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MediaRouterRepositoryTest : SysuiTestCase() {
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val castController = kosmos.fakeCastController
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 858ed6a..473d7b6d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
@@ -64,7 +64,7 @@
         mSetFlagsRule.setFlagsParameterization(flags)
     }
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     private lateinit var testableLooper: TestableLooper
@@ -85,7 +85,7 @@
                     Handler(testableLooper.looper),
                     TEST_SETTING,
                     USER,
-                    DEFAULT_VALUE
+                    DEFAULT_VALUE,
                 ) {
                 override fun handleValueChanged(value: Int, observedChange: Boolean) {
                     callback(value, observedChange)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index 0049042..a2829b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.pipeline.data.model.RestoreData
 import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
 import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
@@ -36,6 +35,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.WorkModeTile
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -47,7 +47,7 @@
 @RunWith(AndroidJUnit4::class)
 class WorkTileAutoAddableTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private val restoreProcessor: RestoreProcessor
         get() = kosmos.workTileRestoreProcessor
@@ -62,7 +62,7 @@
             FakeUserTracker(
                 _userId = USER_INFO_0.id,
                 _userInfo = USER_INFO_0,
-                _userProfiles = listOf(USER_INFO_0)
+                _userProfiles = listOf(USER_INFO_0),
             )
 
         underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
index 3a9c3d0..4e0adca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.MediumTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.FakeQSFactory
@@ -39,6 +38,7 @@
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -55,22 +55,12 @@
 @RunWith(AndroidJUnit4::class)
 class NoLowNumberOfTilesTest : SysuiTestCase() {
 
-    private val USER_0_INFO =
-        UserInfo(
-            0,
-            "zero",
-            "",
-            UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
-        )
+    private val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
 
-    private val defaultTiles =
-        listOf(
-            TileSpec.create("internet"),
-            TileSpec.create("bt"),
-        )
+    private val defaultTiles = listOf(TileSpec.create("internet"), TileSpec.create("bt"))
 
     private val kosmos =
-        Kosmos().apply {
+        testKosmos().apply {
             fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
             fakeUserTracker.set(listOf(USER_0_INFO), 0)
             qsTileFactory = FakeQSFactory(::tileCreator)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
index 9d9bfda..77030ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.MediumTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.FakeQSFactory
@@ -34,6 +33,7 @@
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -52,7 +52,9 @@
 @RunWith(AndroidJUnit4::class)
 class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
 
-    private val kosmos by lazy { Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) } }
+    private val kosmos by lazy {
+        testKosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) }
+    }
     // Getter here so it can change when there is a managed profile.
     private val workTileAvailable: Boolean
         get() = hasManagedProfile()
@@ -143,30 +145,15 @@
     }
 
     private fun TestScope.createManagedProfileAndAdd() {
-        kosmos.fakeUserTracker.set(
-            listOf(USER_0_INFO, MANAGED_USER_INFO),
-            0,
-        )
+        kosmos.fakeUserTracker.set(listOf(USER_0_INFO, MANAGED_USER_INFO), 0)
         runCurrent()
     }
 
     private companion object {
         val WORK_TILE_SPEC = "work".toTileSpec()
-        val USER_0_INFO =
-            UserInfo(
-                0,
-                "zero",
-                "",
-                UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
-            )
+        val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
         val MANAGED_USER_INFO =
-            UserInfo(
-                10,
-                "ten-managed",
-                "",
-                0,
-                UserManager.USER_TYPE_PROFILE_MANAGED,
-            )
+            UserInfo(10, "ten-managed", "", 0, UserManager.USER_TYPE_PROFILE_MANAGED)
 
         fun String.toTileSpec() = TileSpec.create(this)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
index 557f4ea..311f1f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
@@ -23,13 +23,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AirplaneModeMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
 
     private lateinit var mapper: AirplaneModeMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 24e4668..bb58eca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -23,12 +23,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
 import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.time.FakeSystemClock
 import java.time.Instant
 import java.time.LocalDateTime
@@ -41,7 +41,7 @@
 @RunWith(AndroidJUnit4::class)
 class AlarmTileMapperTest : SysuiTestCase() {
     private val oneMinute = 60000L
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val alarmTileConfig = kosmos.qsAlarmTileConfig
     private val fakeClock = FakeSystemClock()
     // Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
index 44c175a..1ef1a72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
@@ -23,10 +23,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.utils.leaks.FakeBatteryController
 import com.google.common.truth.Truth
 import kotlinx.coroutines.flow.flowOf
@@ -40,7 +40,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class BatterySaverTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val batteryController = FakeBatteryController(LeakCheck())
     private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index 2ddaddd..cb50ec9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -22,12 +22,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
 import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,7 +35,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BatterySaverTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val batterySaverTileConfig = kosmos.qsBatterySaverTileConfig
     private lateinit var mapper: BatterySaverTileMapper
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index a3c159820..bcd443c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
 import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ColorCorrectionTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig
     private val subtitleArray by lazy {
         context.resources.getStringArray(R.array.tile_states_color_correction)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index 83e61d1..3c0e7d51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -24,7 +24,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.external.TileServiceKey
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -34,6 +33,7 @@
 import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
 import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
 import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.test.runCurrent
@@ -45,7 +45,7 @@
 @RunWith(AndroidJUnit4::class)
 class CustomTileRepositoryTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
+    private val kosmos = testKosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
     private val underTest: CustomTileRepository =
         with(kosmos) {
             CustomTileRepositoryImpl(
@@ -213,7 +213,7 @@
                 underTest.updateWithTile(
                     TEST_USER_1,
                     Tile().apply { subtitle = "test_subtitle" },
-                    true
+                    true,
                 )
                 runCurrent()
 
@@ -247,7 +247,7 @@
                 underTest.updateWithTile(
                     TEST_USER_1,
                     Tile().apply { subtitle = "test_subtitle" },
-                    true
+                    true,
                 )
                 runCurrent()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index a115c12..2da144e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -21,11 +21,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
 import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import org.junit.Test
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FlashlightMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsFlashlightTileConfig
     private val mapper by lazy {
         FlashlightMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index 8f8f710..45720b86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import com.android.systemui.qs.tiles.impl.fontscaling.qsFontScalingTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FontScalingTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val fontScalingTileConfig = kosmos.qsFontScalingTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index 9bd4895..93f2bc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchableView
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
@@ -37,6 +36,7 @@
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import com.android.systemui.statusbar.phone.FakeKeyguardStateController
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -67,7 +67,7 @@
 
     @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val scope = kosmos.testScope
     private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
     private val keyguardStateController = FakeKeyguardStateController()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
index 3d3447d..c410342 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -21,19 +21,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class HearingDevicesTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
     private val mapper by lazy {
         HearingDevicesTileMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 4b9d11d..4d38e75 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -24,11 +24,11 @@
 import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -46,7 +46,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testUser = UserHandle.of(1)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
index 1b497a2..0ba057b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -22,13 +22,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
 import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -47,7 +47,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 54a653d..e15664e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
@@ -38,13 +37,14 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
 import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class InternetTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val internetTileConfig = kosmos.qsInternetTileConfig
     private val handler = kosmos.fakeExecutorHandler
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 63607f1..45582f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
@@ -54,6 +53,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -67,7 +67,7 @@
 @RunWith(AndroidJUnit4::class)
 class InternetTileDataInteractorTest : SysuiTestCase() {
     private val testUser = UserHandle.of(1)
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     private lateinit var underTest: InternetTileDataInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index 261e3de..d019a45 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
@@ -29,6 +28,7 @@
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -44,7 +44,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class InternetTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
     private lateinit var underTest: InternetTileUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index 780d58c..48e1146 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -22,20 +22,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
 import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ColorInversionTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig
     private val subtitleArrayId =
         SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
index 7562ac0..57f85bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -22,13 +22,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.settings.fakeUserFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.flow.flowOf
@@ -42,7 +42,7 @@
 @RunWith(AndroidJUnit4::class)
 class IssueRecordingDataInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val userTracker = kosmos.userTracker
     private val userFileManager = kosmos.fakeUserFileManager
     private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
index fa6d8bf..0201168 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.qsEventLogger
@@ -30,6 +29,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
 import com.android.systemui.recordissue.RecordIssueModule
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class IssueRecordingMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val uiConfig =
         QSTileUIConfig.Resource(R.drawable.qs_record_issue_icon_off, R.string.qs_record_issue_label)
     private val config =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 9c2be89..125419b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.dialogTransitionAnimator
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -39,6 +38,7 @@
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.test.runTest
@@ -56,7 +56,7 @@
     @Mock private lateinit var recordingController: RecordingController
 
     val user = UserHandle(1)
-    val kosmos = Kosmos().also { it.testCase = this }
+    val kosmos = testKosmos().also { it.testCase = this }
 
     private lateinit var userContextProvider: UserContextProvider
     private lateinit var underTest: IssueRecordingUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 4ebe23dc..a6ad549 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -21,11 +21,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth
 import junit.framework.Assert
 import org.junit.Test
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LocationTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsLocationTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
index 8b21cb4..435aea0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
@@ -32,6 +31,7 @@
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.statusbar.phone.FakeKeyguardStateController
 import com.android.systemui.statusbar.policy.LocationController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        val kosmos = Kosmos()
+        val kosmos = testKosmos()
         underTest =
             LocationTileUserActionInteractor(
                 EmptyCoroutineContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
index a0aa2d4..a5e2922 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.accessibility.data.repository.NightDisplayRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.android.systemui.user.utils.UserScopedService
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testUser = UserHandle.of(1)!!
     private val testStartTime = LocalTime.MIDNIGHT
     private val testEndTime = LocalTime.NOON
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
index adc8bcb..51975b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
@@ -26,12 +26,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.NightDisplayRepository
 import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.intentInputs
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
+import com.android.systemui.testKosmos
 import com.android.systemui.user.utils.UserScopedService
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -51,7 +51,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
     private val testUser = UserHandle.of(1)
     private val colorDisplayManager =
@@ -89,7 +89,7 @@
         NightDisplayTileUserActionInteractor(
             nightDisplayRepository,
             qsTileIntentUserActionHandler,
-            kosmos.qsTileLogger
+            kosmos.qsTileLogger,
         )
 
     @Test
@@ -143,7 +143,7 @@
             underTest.handleInput(
                 QSTileInputTestKtx.longClick(
                     NightDisplayTileModel.AutoModeOff(enabledState, false),
-                    testUser
+                    testUser,
                 )
             )
 
@@ -163,7 +163,7 @@
             underTest.handleInput(
                 QSTileInputTestKtx.longClick(
                     NightDisplayTileModel.AutoModeOff(enabledState, false),
-                    testUser
+                    testUser,
                 )
             )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 7c85326..0b0b88e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -24,13 +24,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.base.logging.QSTileLogger
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
 import com.android.systemui.qs.tiles.impl.night.qsNightDisplayTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import java.time.LocalTime
 import java.time.format.DateTimeFormatter
@@ -41,7 +41,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsNightDisplayTileConfig
 
     private val testStartTime = LocalTime.MIDNIGHT
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
index b6caa22..ecf6f2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
 import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import kotlin.test.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NotesTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsNotesTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
index 4786fc4..84ab690 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -24,9 +24,9 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.toCollection
@@ -38,12 +38,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NotesTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testUser = UserHandle.of(1)
     private lateinit var underTest: NotesTileDataInteractor
 
-
     @EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
     @Test
     fun availability_qsFlagEnabled_notesRoleEnabled_returnTrue() =
@@ -92,7 +91,7 @@
     fun tileData_notEmpty() = runTest {
         underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
         val flowValue by
-        collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+            collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
 
         runCurrent()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
index 54911e6..282af91 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.notetask.NoteTaskController
 import com.android.systemui.notetask.NoteTaskEntryPoint
@@ -29,18 +28,19 @@
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NotesTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val panelInteractor = mock<PanelInteractor>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
index 59eb069..16b0caa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
@@ -22,9 +22,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.oneHandedModeRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
+import com.android.systemui.testKosmos
 import com.android.wm.shell.onehanded.OneHanded
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
@@ -37,7 +37,7 @@
 @RunWith(AndroidJUnit4::class)
 class OneHandedModeTileDataInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testUser = UserHandle.of(1)!!
     private val oneHandedModeRepository = kosmos.oneHandedModeRepository
     private val underTest: OneHandedModeTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 5b39810..3fba857 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -22,13 +22,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
 import com.android.systemui.qs.tiles.impl.onehanded.qsOneHandedModeTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,7 +36,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class OneHandedModeTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsOneHandedModeTileConfig
     private val subtitleArrayId = SubtitleArrayMapping.getSubtitleId(config.tileSpec.spec)
     private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
index 312f180..ef21df6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -21,12 +21,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
 import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -36,7 +36,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     private val inputHandler = kosmos.qsTileIntentUserInputHandler
     private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
     private val intent = mock<Intent>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index c572ff6..9e20aae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -23,11 +23,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
 import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
@@ -36,7 +36,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QRCodeScannerTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsQRCodeScannerTileConfig
 
     private lateinit var mapper: QRCodeScannerTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
index dc3248d..fecb3de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
@@ -23,10 +23,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.reduceBrightColorsController
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -40,14 +40,14 @@
 class ReduceBrightColorsTileDataInteractorTest : SysuiTestCase() {
 
     private val isAvailable = true
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val reduceBrightColorsController = kosmos.reduceBrightColorsController
     private val underTest: ReduceBrightColorsTileDataInteractor =
         ReduceBrightColorsTileDataInteractor(
             testScope.testScheduler,
             isAvailable,
-            reduceBrightColorsController
+            reduceBrightColorsController,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
index 75b090c..7732138 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -28,11 +28,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
 import com.android.systemui.accessibility.reduceBrightColorsController
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -48,7 +48,7 @@
 @RunWith(AndroidJUnit4::class)
 class ReduceBrightColorsTileUserActionInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val controller = kosmos.reduceBrightColorsController
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index 00017f9..ebf70da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -23,12 +23,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
 import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,7 +36,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsReduceBrightColorsTileConfig
 
     private lateinit var mapper: ReduceBrightColorsTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
index 283fa60..6f05c31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
@@ -28,9 +28,9 @@
 import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepository
 import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.utils.leaks.FakeBatteryController
@@ -48,7 +48,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class RotationLockTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val batteryController = FakeBatteryController(LeakCheck())
     private val rotationController = FakeRotationLockController(LeakCheck())
@@ -65,7 +65,7 @@
         whenever(
                 packageManager.checkPermission(
                     eq(Manifest.permission.CAMERA),
-                    eq(TEST_PACKAGE_NAME)
+                    eq(TEST_PACKAGE_NAME),
                 )
             )
             .thenReturn(PackageManager.PERMISSION_GRANTED)
@@ -81,7 +81,7 @@
                     .apply {
                         addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
                     }
-                    .resources
+                    .resources,
             )
     }
 
@@ -182,7 +182,7 @@
             whenever(
                     packageManager.checkPermission(
                         eq(Manifest.permission.CAMERA),
-                        eq(TEST_PACKAGE_NAME)
+                        eq(TEST_PACKAGE_NAME),
                     )
                 )
                 .thenReturn(PackageManager.PERMISSION_DENIED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 7401014..0e6aaa6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.defaultDeviceState
 import com.android.systemui.deviceStateManager
 import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
 import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
@@ -33,6 +32,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.devicePostureController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -42,7 +42,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class RotationLockTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
     private val devicePostureController = kosmos.devicePostureController
     private val deviceStateManager = kosmos.deviceStateManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index 1fb5048..0799216 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
 import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DataSaverTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
 
     // Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
index 41174e7..127160d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
@@ -22,11 +22,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val screenRecordRepo = kosmos.screenRecordRepository
     private val underTest: ScreenRecordTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 778c73f..f47e057 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -36,6 +35,7 @@
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.testKosmos
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -49,7 +49,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyguardInteractor = kosmos.keyguardInteractor
     private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index 3632556..d118c09 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -23,13 +23,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
 import com.android.systemui.qs.tiles.impl.screenrecord.qsScreenRecordTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsScreenRecordTileConfig
 
     private lateinit var mapper: ScreenRecordTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
index 6c7bb1b..4a28fc0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
@@ -23,11 +23,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -44,7 +44,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SensorPrivacyToggleTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val mockSensorPrivacyController =
         mock<IndividualSensorPrivacyController> {
@@ -55,7 +55,7 @@
         SensorPrivacyToggleTileDataInteractor(
             testScope.testScheduler,
             mockSensorPrivacyController,
-            CAMERA
+            CAMERA,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
index 562e6eb..7856fca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
@@ -34,6 +33,7 @@
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -48,7 +48,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val keyguardInteractor = kosmos.keyguardInteractor
     // The keyguard repository below is the same one kosmos used to create the interactor above
@@ -64,7 +64,7 @@
             mockActivityStarter,
             mockSensorPrivacyController,
             fakeSafetyCenterManager,
-            CAMERA
+            CAMERA,
         )
 
     @Test
@@ -79,7 +79,7 @@
             .setSensorBlocked(
                 eq(SensorPrivacyManager.Sources.QS_TILE),
                 eq(CAMERA),
-                eq(!originalIsBlocked)
+                eq(!originalIsBlocked),
             )
     }
 
@@ -95,7 +95,7 @@
             .setSensorBlocked(
                 eq(SensorPrivacyManager.Sources.QS_TILE),
                 eq(CAMERA),
-                eq(!originalIsBlocked)
+                eq(!originalIsBlocked),
             )
     }
 
@@ -114,7 +114,7 @@
             .setSensorBlocked(
                 eq(SensorPrivacyManager.Sources.QS_TILE),
                 eq(CAMERA),
-                eq(!originalIsBlocked)
+                eq(!originalIsBlocked),
             )
         verify(mockActivityStarter).postQSRunnableDismissingKeyguard(any())
     }
@@ -150,7 +150,7 @@
                 mockActivityStarter,
                 mockSensorPrivacyController,
                 fakeSafetyCenterManager,
-                MICROPHONE
+                MICROPHONE,
             )
 
         micUserActionInteractor.handleInput(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index e4cd0e0..3b810dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -24,7 +24,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
 import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
@@ -33,13 +32,14 @@
 import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.MicrophonePrivacyTileResources
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val cameraConfig = kosmos.qsCameraSensorPrivacyToggleTileConfig
     private val micConfig = kosmos.qsMicrophoneSensorPrivacyToggleTileConfig
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 8f5f2d3..547bbbc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -25,12 +25,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
 import com.android.systemui.qs.tiles.impl.uimodenight.qsUiModeNightTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import kotlin.reflect.KClass
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,7 +38,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class UiModeNightTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsUiModeNightTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index 2c81f39..ed3fc50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -26,12 +26,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
 import com.android.systemui.qs.tiles.impl.work.qsWorkModeTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -43,7 +43,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class WorkModeTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsWorkModeTileConfig
     private val devicePolicyManager = kosmos.devicePolicyManager
     private val testLabel = context.getString(R.string.quick_settings_work_mode_label)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index a8b005f..12ed3f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.display.data.repository.displayStateRepository
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -40,6 +39,7 @@
 import com.android.systemui.settings.brightness.MirrorController
 import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -65,7 +65,7 @@
 @RunWith(AndroidJUnit4::class)
 class QSSceneAdapterImplTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().apply { testCase = this@QSSceneAdapterImplTest }
+    private val kosmos = testKosmos().apply { testCase = this@QSSceneAdapterImplTest }
     private val testDispatcher = kosmos.testDispatcher
     private val testScope = kosmos.testScope
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index 7bcaeab..390a5d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -28,12 +28,12 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.dialogTransitionAnimator
 import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.settings.userFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.android.traceur.TraceConfig
 import com.google.common.truth.Truth
@@ -52,7 +52,7 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class IssueRecordingServiceSessionTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val bgExecutor = kosmos.fakeExecutor
     private val userContextProvider: UserContextProvider = kosmos.userTracker
     private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 83bdcd2..0510e6ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -22,9 +22,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.settings.userFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import org.junit.Before
@@ -40,7 +40,7 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class IssueRecordingStateTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private lateinit var underTest: IssueRecordingState
     @Mock private lateinit var resolver: ContentResolver
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
index 737b101..6f0dd16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
@@ -20,10 +20,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth
 import org.junit.Before
 import org.junit.Test
@@ -34,7 +34,7 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class ScreenRecordingStartTimeStoreTest : SysuiTestCase() {
-    private val userTracker: UserTracker = Kosmos().also { it.testCase = this }.userTracker
+    private val userTracker: UserTracker = testKosmos().also { it.testCase = this }.userTracker
 
     private lateinit var underTest: ScreenRecordingStartTimeStore
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index a82a7de..7e9487b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -38,6 +37,7 @@
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.notification.init.NotificationsController
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -57,7 +57,7 @@
 @RunWith(AndroidJUnit4::class)
 class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testDispatcher = StandardTestDispatcher()
     private val iStatusBarService = mock<IStatusBarService>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index 9724974..bd54166 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -21,10 +21,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -39,7 +39,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val recordingController = mock<RecordingController>()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index c6ce581..0c90d07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -43,6 +42,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -63,7 +63,7 @@
 @SmallTest
 class ShadeControllerImplTest : SysuiTestCase() {
     private val executor = FakeExecutor(FakeSystemClock())
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     @Mock private lateinit var commandQueue: CommandQueue
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index 054c1b8..32eec56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -35,6 +34,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -52,7 +52,7 @@
 @RunWith(AndroidJUnit4::class)
 @EnableSceneContainer
 class ShadeControllerSceneImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ddad230..2f2fafa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -25,8 +25,8 @@
 import com.android.systemui.plugins.PluginLifecycleManager
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
@@ -543,7 +543,7 @@
 
     @Test
     fun jsonDeserialization_fontAxes() {
-        val expected = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+        val expected = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
         val json = JSONObject("""{"axes":[{"key":"KEY","value":10}]}""")
         val actual = ClockSettings.fromJson(json)
         assertEquals(expected, actual)
@@ -576,7 +576,7 @@
 
     @Test
     fun jsonSerialization_axisSettings() {
-        val settings = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+        val settings = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
         val actual = ClockSettings.toJson(settings)
         val expected = JSONObject("""{"metadata":{},"axes":[{"key":"KEY","value":10}]}""")
         assertEquals(expected.toString(), actual.toString())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 01046cd..3c19179 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -227,7 +227,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return row.getEntryAdapter().getSbn().getNotification();
         } else {
-            return row.getEntry().getSbn().getNotification();
+            return row.getEntryLegacy().getSbn().getNotification();
         }
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 326d8ff..3ecf302 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -32,14 +32,12 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.WallpaperController
 import com.android.systemui.util.mockito.eq
@@ -77,7 +75,6 @@
     private val kosmos = testKosmos()
 
     private val applicationScope = kosmos.testScope.backgroundScope
-    @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var blurUtils: BlurUtils
     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@@ -87,6 +84,8 @@
     @Mock private lateinit var wallpaperController: WallpaperController
     @Mock private lateinit var wallpaperInteractor: WallpaperInteractor
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+    @Mock private lateinit var shadeModeInteractor: ShadeModeInteractor
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
     @Mock private lateinit var root: View
@@ -103,7 +102,6 @@
     private var statusBarState = StatusBarState.SHADE
     private val maxBlur = 150
     private lateinit var notificationShadeDepthController: NotificationShadeDepthController
-    private val configurationController = FakeConfigurationController()
 
     @Before
     fun setup() {
@@ -133,13 +131,11 @@
                 wallpaperInteractor,
                 notificationShadeWindowController,
                 dozeParameters,
-                context,
-                ResourcesSplitShadeStateController(),
+                shadeModeInteractor,
                 windowRootViewBlurInteractor,
                 appZoomOutOptional,
                 applicationScope,
-                dumpManager,
-                configurationController,
+                dumpManager
             )
         notificationShadeDepthController.shadeAnimation = shadeAnimation
         notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -492,15 +488,10 @@
     }
 
     private fun enableSplitShade() {
-        setSplitShadeEnabled(true)
+        `when` (shadeModeInteractor.isSplitShade).thenReturn(true)
     }
 
     private fun disableSplitShade() {
-        setSplitShadeEnabled(false)
-    }
-
-    private fun setSplitShadeEnabled(enabled: Boolean) {
-        overrideResource(R.bool.config_use_split_notification_shade, enabled)
-        configurationController.notifyConfigurationChanged()
+        `when` (shadeModeInteractor.isSplitShade).thenReturn(false)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 03dee3a..72d21f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -24,13 +24,13 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.kotlin.JavaAdapter
@@ -54,7 +54,7 @@
     private lateinit var underTest: OperatorNameViewController
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = TestScope()
 
     private val view = OperatorNameView(mContext)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 485b9fe..2fa9a02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -17,12 +17,17 @@
 package com.android.systemui.statusbar.chips.call.ui.viewmodel
 
 import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -51,6 +56,7 @@
 import kotlin.test.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
@@ -481,6 +487,294 @@
             verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(pendingIntent, null)
         }
 
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_updatesCorrectly_withStateAndTransitionState() =
+        kosmos.runTest {
+            val pendingIntent = mock<PendingIntent>()
+            val intent = mock<Intent>()
+            whenever(pendingIntent.intent).thenReturn(intent)
+            val component = mock<ComponentName>()
+            whenever(intent.component).thenReturn(component)
+
+            val expandable = mock<Expandable>()
+            val activityController = mock<ActivityTransitionAnimator.Controller>()
+            whenever(
+                    expandable.activityTransitionController(
+                        anyOrNull(),
+                        anyOrNull(),
+                        any(),
+                        anyOrNull(),
+                        any(),
+                    )
+                )
+                .thenReturn(activityController)
+
+            val latest by collectLastValue(underTest.chip)
+
+            // Start off with no call.
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+            assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+            // Call starts [NoCall -> InCall(isAppVisible=true), NoTransition].
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+            val factory = latest!!.transitionManager!!.controllerFactory
+            assertThat(factory!!.component).isEqualTo(component)
+
+            // Request a return transition [InCall(isAppVisible=true), NoTransition ->
+            // ReturnRequested].
+            factory.onCompose(expandable)
+            var controller = factory.createController(forLaunch = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // Start the return transition [InCall(isAppVisible=true), ReturnRequested ->
+            // Returning].
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // End the return transition [InCall(isAppVisible=true), Returning -> NoTransition].
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // Settle the return transition [InCall(isAppVisible=true) ->
+            // InCall(isAppVisible=false), NoTransition].
+            kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // Trigger a launch transition [InCall(isAppVisible=false) -> InCall(isAppVisible=true),
+            // NoTransition].
+            kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, true)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // Request the return transition [InCall(isAppVisible=true), NoTransition ->
+            // LaunchRequested].
+            controller = factory.createController(forLaunch = true)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // Start the return transition [InCall(isAppVisible=true), LaunchRequested ->
+            // Launching].
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // End the return transition [InCall(isAppVisible=true), Launching -> NoTransition].
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+            // End the call with the app visible [InCall(isAppVisible=true) -> NoCall,
+            // NoTransition].
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+            assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+            // End the call with the app hidden [InCall(isAppVisible=false) -> NoCall,
+            // NoTransition].
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                isAppVisible = false,
+            )
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+            assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+        }
+
+    @Test
+    @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    fun chipLegacy_hasNoTransitionAnimationInformation() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            // NoCall
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest!!.transitionManager).isNull()
+
+            // InCall with visible app
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest!!.transitionManager).isNull()
+
+            // InCall with hidden app
+            kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+            assertThat(latest!!.transitionManager).isNull()
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_chipDataChangesMidTransition() =
+        kosmos.runTest {
+            val pendingIntent = mock<PendingIntent>()
+            val intent = mock<Intent>()
+            whenever(pendingIntent.intent).thenReturn(intent)
+            val component = mock<ComponentName>()
+            whenever(intent.component).thenReturn(component)
+
+            val expandable = mock<Expandable>()
+            val activityController = mock<ActivityTransitionAnimator.Controller>()
+            whenever(
+                    expandable.activityTransitionController(
+                        anyOrNull(),
+                        anyOrNull(),
+                        any(),
+                        anyOrNull(),
+                        any(),
+                    )
+                )
+                .thenReturn(activityController)
+
+            val latest by collectLastValue(underTest.chip)
+
+            // Start with the app visible and trigger a return animation.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            var factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            var controller = factory.createController(forLaunch = false)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip changes state.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 0,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // Reset the state and trigger a launch animation.
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            controller = factory.createController(forLaunch = true)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip changes state.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = -2,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_chipDisappearsMidTransition() =
+        kosmos.runTest {
+            val pendingIntent = mock<PendingIntent>()
+            val intent = mock<Intent>()
+            whenever(pendingIntent.intent).thenReturn(intent)
+            val component = mock<ComponentName>()
+            whenever(intent.component).thenReturn(component)
+
+            val expandable = mock<Expandable>()
+            val activityController = mock<ActivityTransitionAnimator.Controller>()
+            whenever(
+                    expandable.activityTransitionController(
+                        anyOrNull(),
+                        anyOrNull(),
+                        any(),
+                        anyOrNull(),
+                        any(),
+                    )
+                )
+                .thenReturn(activityController)
+
+            val latest by collectLastValue(underTest.chip)
+
+            // Start with the app visible and trigger a return animation.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            var factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            var controller = factory.createController(forLaunch = false)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip disappears.
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+
+            // Reset the state and trigger a launch animation.
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            controller = factory.createController(forLaunch = true)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip disappears.
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+        }
+
     companion object {
         fun createStatusBarIconViewOrNull(): StatusBarIconView? =
             if (StatusBarConnectedDisplays.isEnabled) {
@@ -500,6 +794,8 @@
                 }
                 .build()
 
+        private const val NOTIFICATION_KEY = "testKey"
+        private const val NOTIFICATION_UID = 12345
         private const val PROMOTED_BACKGROUND_COLOR = 65
         private const val PROMOTED_PRIMARY_TEXT_COLOR = 98
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
index b2174c1..21a4560 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
@@ -20,12 +20,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
 import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.mediaRouterChipInteractor
 import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
 import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -35,7 +35,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MediaRouterChipInteractorTest : SysuiTestCase() {
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val mediaRouterRepository = kosmos.fakeMediaRouterRepository
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
index 274efbb..5681297 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndCastScreenToOtherDeviceDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
     private lateinit var underTest: EndCastScreenToOtherDeviceDialogDelegate
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
index 88207d1..30a415c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
@@ -26,7 +26,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
@@ -35,6 +34,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -51,7 +51,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndGenericCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
     private lateinit var underTest: EndGenericCastToOtherDeviceDialogDelegate
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index ccc844a..d921ab3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -52,6 +51,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
 import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -69,7 +69,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
     private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
     private val mediaRouterRepo = kosmos.fakeMediaRouterRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
index 795988f..fac50b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -24,12 +24,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import org.junit.runner.RunWith
@@ -42,7 +42,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
 
     private val underTest = kosmos.endMediaProjectionDialogHelper
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
index f560ee7..981c952 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -39,6 +38,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -55,7 +55,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
 
     private val sysuiDialog = mock<SystemUIDialog>()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
index 95aa6cd..b2e90ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndShareScreenToAppDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
     private lateinit var underTest: EndShareScreenToAppDialogDelegate
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index e3a84fd..6d91fb5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -22,12 +22,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,7 +39,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ChipTransitionHelperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 2f6bedb..ea61b71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -29,7 +29,6 @@
 import com.android.internal.view.AppearanceRegion
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.data.model.StatusBarMode
 import com.android.systemui.statusbar.layout.BoundsPair
@@ -42,6 +41,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -59,7 +59,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class StatusBarModeRepositoryImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = TestScope()
     private val commandQueue = mock<CommandQueue>()
     private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 790b2c3..bfd700d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -151,7 +152,8 @@
                 .build();
 
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertFalse(entry.isBlockable());
     }
@@ -251,7 +253,8 @@
                 .build();
 
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
         assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
@@ -365,7 +368,8 @@
                 .setKey(sbn.getKey())
                 .build();
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertFalse(entry.isChannelVisibilityPrivate());
     }
@@ -378,7 +382,8 @@
                 .setKey(sbn.getKey())
                 .build();
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertFalse(entry.isChannelVisibilityPrivate());
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
index ef0a416..d532010 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
 import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -323,7 +324,10 @@
             setPulsing(true)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN group changes aren't allowed
             assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
@@ -349,7 +353,10 @@
             setPulsing(false)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN the notification list is invalidated
             verifyStabilityManagerWasInvalidated(times(1))
@@ -365,7 +372,10 @@
             setPulsing(false)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN invalidate is not called because this entry was never suppressed from reordering
             verifyStabilityManagerWasInvalidated(never())
@@ -382,7 +392,10 @@
             assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN invalidate is not called because this entry was never suppressed from
             // reordering;
@@ -415,7 +428,10 @@
             setPulsing(true)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
             // can now reorder, so invalidates
             verifyStabilityManagerWasInvalidated(times(1))
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 1b8d64d..387c62d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -71,6 +71,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
 import com.android.systemui.statusbar.notification.row.icon.appIconProvider
@@ -80,6 +81,9 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.kotlin.JavaAdapter
 import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
@@ -107,9 +111,6 @@
 import org.mockito.kotlin.whenever
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
 
 /** Tests for [NotificationGutsManager]. */
 @SmallTest
@@ -149,6 +150,7 @@
     @Mock private lateinit var launcherApps: LauncherApps
     @Mock private lateinit var shortcutManager: ShortcutManager
     @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+    @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
     @Mock private lateinit var contextTracker: UserContextProvider
     @Mock private lateinit var bubblesManager: BubblesManager
@@ -214,6 +216,7 @@
                 launcherApps,
                 shortcutManager,
                 channelEditorDialogController,
+                packageDemotionInteractor,
                 contextTracker,
                 assistantFeedbackController,
                 Optional.of(bubblesManager),
@@ -509,6 +512,7 @@
             .setImportance(NotificationManager.IMPORTANCE_HIGH)
             .build()
 
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
         val statusBarNotification = entry.sbn
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -521,6 +525,7 @@
                 any<NotificationIconStyleProvider>(),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                any<PackageDemotionInteractor>(),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -530,6 +535,7 @@
                 any<UiEventLogger>(),
                 /* isDeviceProvisioned = */ eq(false),
                 /* isNonblockable = */ eq(false),
+                /* isDismissable = */ eq(true),
                 /* wasShownHighPriority = */ eq(true),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
@@ -545,6 +551,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
 
@@ -560,6 +567,7 @@
                 any<NotificationIconStyleProvider>(),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                any<PackageDemotionInteractor>(),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -569,6 +577,7 @@
                 any<UiEventLogger>(),
                 /* isDeviceProvisioned = */ eq(true),
                 /* isNonblockable = */ eq(false),
+                /* isDismissable = */ eq(true),
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
@@ -584,6 +593,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
 
@@ -597,6 +607,7 @@
                 any<NotificationIconStyleProvider>(),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                any<PackageDemotionInteractor>(),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -606,6 +617,7 @@
                 any<UiEventLogger>(),
                 /* isDeviceProvisioned = */ eq(false),
                 /* isNonblockable = */ eq(false),
+                /* isDismissable = */ eq(true),
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 96ae070..0ac5fe9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -49,6 +49,7 @@
 import android.view.View.VISIBLE
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.core.view.isVisible
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
@@ -57,17 +58,18 @@
 import com.android.internal.logging.uiEventLoggerFake
 import com.android.systemui.Dependency
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.notification.AssistantFeedbackController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
 import com.android.systemui.statusbar.notification.row.icon.appIconProvider
 import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.testKosmos
 import com.android.telecom.telecomManager
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
@@ -89,7 +91,7 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class NotificationInfoTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
 
     private lateinit var underTest: NotificationInfo
     private lateinit var notificationChannel: NotificationChannel
@@ -105,6 +107,7 @@
     private val onUserInteractionCallback = mock<OnUserInteractionCallback>()
     private val mockINotificationManager = mock<INotificationManager>()
     private val channelEditorDialogController = mock<ChannelEditorDialogController>()
+    private val packageDemotionInteractor = mock<PackageDemotionInteractor>()
     private val assistantFeedbackController = mock<AssistantFeedbackController>()
 
     @Before
@@ -863,6 +866,31 @@
         assertThat(underTest.findViewById<View>(R.id.feedback).visibility).isEqualTo(GONE)
     }
 
+    @Test
+    @Throws(RemoteException::class)
+    fun testDismissListenerBound() {
+        val latch = CountDownLatch(1)
+        bindNotification(onCloseClick = { _: View? -> latch.countDown() })
+
+        val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+        assertThat(dismissView.isVisible).isTrue()
+        dismissView.performClick()
+
+        // Verify that listener was triggered.
+        assertThat(latch.count).isEqualTo(0)
+    }
+
+    @Test
+    @Throws(RemoteException::class)
+    fun testDismissHiddenWhenUndismissable() {
+
+        entry.sbn.notification.flags =
+            entry.sbn.notification.flags or android.app.Notification.FLAG_NO_DISMISS
+        bindNotification(isDismissable = false)
+        val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+        assertThat(dismissView.isVisible).isFalse()
+    }
+
     private fun bindNotification(
         pm: PackageManager = this.mockPackageManager,
         iNotificationManager: INotificationManager = this.mockINotificationManager,
@@ -871,6 +899,7 @@
         onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
         channelEditorDialogController: ChannelEditorDialogController =
             this.channelEditorDialogController,
+        packageDemotionInteractor: PackageDemotionInteractor = this.packageDemotionInteractor,
         pkg: String = TEST_PACKAGE_NAME,
         notificationChannel: NotificationChannel = this.notificationChannel,
         entry: NotificationEntry = this.entry,
@@ -880,6 +909,7 @@
         uiEventLogger: UiEventLogger = this.uiEventLogger,
         isDeviceProvisioned: Boolean = true,
         isNonblockable: Boolean = false,
+        isDismissable: Boolean = true,
         wasShownHighPriority: Boolean = true,
         assistantFeedbackController: AssistantFeedbackController = this.assistantFeedbackController,
         metricsLogger: MetricsLogger = kosmos.metricsLogger,
@@ -892,6 +922,7 @@
             iconStyleProvider,
             onUserInteractionCallback,
             channelEditorDialogController,
+            packageDemotionInteractor,
             pkg,
             notificationChannel,
             entry,
@@ -901,6 +932,7 @@
             uiEventLogger,
             isDeviceProvisioned,
             isNonblockable,
+            isDismissable,
             wasShownHighPriority,
             assistantFeedbackController,
             metricsLogger,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index af67a04..2d4063b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -27,7 +27,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.testing.TestableResources;
 import android.view.View;
@@ -39,7 +38,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.AnimatorTestRule;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -93,7 +91,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
     public void closeControls_withoutSave_performsUndo() {
         ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
         mUnderTest.mSelectedOption = options.getFirst();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
index 5638e0b..209dfb2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 
@@ -92,6 +93,8 @@
     @Mock
     private ChannelEditorDialogController mChannelEditorDialogController;
     @Mock
+    private PackageDemotionInteractor mPackageDemotionInteractor;
+    @Mock
     private AssistantFeedbackController mAssistantFeedbackController;
     @Mock
     private TelecomManager mTelecomManager;
@@ -138,6 +141,7 @@
                 mMockIconStyleProvider,
                 mOnUserInteractionCallback,
                 mChannelEditorDialogController,
+                mPackageDemotionInteractor,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
                 mEntry,
@@ -148,6 +152,7 @@
                 true,
                 false,
                 true,
+                true,
                 mAssistantFeedbackController,
                 mMetricsLogger,
                 null);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 048028c..db0c59f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -23,12 +23,12 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.statusbar.lockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -44,7 +44,7 @@
 class NotificationShelfInteractorTest : SysuiTestCase() {
 
     private val kosmos =
-        Kosmos().apply {
+        testKosmos().apply {
             testCase = this@NotificationShelfInteractorTest
             lockscreenShadeTransitionController = mock()
             screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 6381b4e..7265262 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testCase
@@ -35,6 +34,7 @@
 import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.statusbar.lockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -50,7 +50,7 @@
 class NotificationShelfViewModelTest : SysuiTestCase() {
 
     private val kosmos =
-        Kosmos().apply {
+        testKosmos().apply {
             testCase = this@NotificationShelfViewModelTest
             lockscreenShadeTransitionController = mock()
             screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index c5abd02..19e9838 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -106,6 +106,7 @@
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
+        when(row.getEntryLegacy()).thenReturn(entry);
         return entry;
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index eb95ddb..c58b4bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.res.R
@@ -48,6 +47,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -70,7 +70,7 @@
 @TestableLooper.RunWithLooper
 @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallControllerTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private val mainExecutor = kosmos.fakeExecutor
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index a446313..7de56dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -19,12 +19,11 @@
 import android.platform.test.annotations.DisableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -33,7 +32,7 @@
 @RunWith(AndroidJUnit4::class)
 @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val underTest = kosmos.ongoingCallRepository
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index f4204af7..84f1d5c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.activity.data.repository.activityManagerRepository
 import com.android.systemui.activity.data.repository.fake
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -36,6 +35,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
 import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -51,7 +51,7 @@
 @RunWith(AndroidJUnit4::class)
 @EnableChipsModernization
 class OngoingCallInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos().useUnconfinedTestDispatcher()
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val underTest = kosmos.ongoingCallInteractor
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 91b3896..39cf02d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -54,9 +54,7 @@
         MutableStateFlow(OngoingActivityChipModel.Inactive())
 
     override val ongoingActivityChips =
-        MutableStateFlow(
-            ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
-        )
+        ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
 
     override val ongoingActivityChipsLegacy =
         MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 2da692b..20cf3ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -49,6 +49,7 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.log.assertLogsWtf
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -107,7 +108,8 @@
 @RunWith(AndroidJUnit4::class)
 class HomeStatusBarViewModelImplTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
-    private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel }
+    private val Kosmos.underTest by
+        Kosmos.Fixture { kosmos.homeStatusBarViewModel.also { it.activateIn(kosmos.testScope) } }
 
     @Before
     fun setUp() {
@@ -891,32 +893,26 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarHidden_noSecureCamera_noHun_notAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             // home status bar not allowed
             kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
             kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
 
-            assertThat(latest!!.areChipsAllowed).isFalse()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
         }
 
     @Test
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_secureCamera_noHun_notAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.OCCLUDED,
@@ -924,7 +920,7 @@
             )
             kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
 
-            assertThat(latest!!.areChipsAllowed).isFalse()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
         }
 
     @Test
@@ -932,8 +928,6 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_notAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -943,7 +937,7 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isFalse()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
         }
 
     @Test
@@ -951,8 +945,6 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -962,16 +954,14 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
     @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
     @EnableChipsModernization
-    fun ongoingActivityChips_tatusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
+    fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -981,7 +971,7 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
@@ -989,8 +979,6 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -1000,7 +988,7 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
@@ -1008,17 +996,16 @@
     @EnableChipsModernization
     fun ongoingActivityChips_followsChipsViewModel() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
             transitionKeyguardToGone()
 
             screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
 
-            assertIsScreenRecordChip(latest!!.chips.active[0])
+            assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
 
             addOngoingCallState(key = "call")
 
-            assertIsScreenRecordChip(latest!!.chips.active[0])
-            assertIsCallChip(latest!!.chips.active[1], "call", context)
+            assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
+            assertIsCallChip(underTest.ongoingActivityChips.chips.active[1], "call", context)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 92bec70..18a124c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.display.data.repository.fakeDeviceStateRepository
 import com.android.systemui.foldedDeviceStateList
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
@@ -47,6 +46,7 @@
 import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.testKosmos
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.COOL_DOWN_DURATION
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
@@ -89,7 +89,7 @@
     private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
     @Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val mockContext = mock<Context>()
     private val resources = mock<Resources>()
     private val foldStateRepository = kosmos.fakeDeviceStateRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
index dfc4d0f..98d99f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
 import com.android.systemui.display.data.repository.fakeDeviceStateRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -36,6 +35,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.testKosmos
 import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
@@ -61,7 +61,7 @@
 class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope: TestScope = kosmos.testScope
     private val fakeDeviceStateRepository = kosmos.fakeDeviceStateRepository
     private val powerInteractor = kosmos.powerInteractor
@@ -93,7 +93,7 @@
                 fakeAnimationStatusRepository,
                 mockControllerFactory,
                 mockFoldLockSettingAvailabilityProvider,
-                mockJankMonitor
+                mockJankMonitor,
             )
         foldLightRevealAnimation.init()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
index b4ba41a..a180d51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
@@ -27,7 +27,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.deviceStateManager
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import org.junit.Before
@@ -39,7 +39,7 @@
 @RunWith(AndroidJUnit4::class)
 class UtilsTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val deviceStateManager = kosmos.deviceStateManager
     private lateinit var testableResources: TestableResources
 
@@ -53,7 +53,7 @@
     fun isFoldableReturnsFalse_overlayConfigurationValues() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf() // empty array <=> device is not foldable
+            intArrayOf(), // empty array <=> device is not foldable
         )
         whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
         assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -64,7 +64,7 @@
     fun isFoldableReturnsFalse_deviceStateManager() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf() // empty array <=> device is not foldable
+            intArrayOf(), // empty array <=> device is not foldable
         )
         whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
         assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -75,7 +75,7 @@
     fun isFoldableReturnsTrue_overlayConfigurationValues() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf(FOLDED_DEVICE_STATE.identifier)
+            intArrayOf(FOLDED_DEVICE_STATE.identifier),
         )
         whenever(deviceStateManager.supportedDeviceStates)
             .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
@@ -87,7 +87,7 @@
     fun isFoldableReturnsTrue_deviceStateManager() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf(FOLDED_DEVICE_STATE.identifier)
+            intArrayOf(FOLDED_DEVICE_STATE.identifier),
         )
         whenever(deviceStateManager.supportedDeviceStates)
             .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
index f232d52..93e5721 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
@@ -20,8 +20,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.volume.panel.domain.availableCriteria
 import com.android.systemui.volume.panel.domain.defaultCriteria
 import com.android.systemui.volume.panel.domain.model.ComponentModel
@@ -39,7 +39,7 @@
 @RunWith(AndroidJUnit4::class)
 class ComponentsInteractorImplTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private lateinit var underTest: ComponentsInteractor
 
@@ -60,12 +60,7 @@
     fun componentsAvailability_checked() {
         with(kosmos) {
             testScope.runTest {
-                enabledComponents =
-                    setOf(
-                        BOTTOM_BAR,
-                        COMPONENT_1,
-                        COMPONENT_2,
-                    )
+                enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
                 criteriaByKey =
                     mapOf(
                         BOTTOM_BAR to Provider { availableCriteria },
@@ -90,12 +85,7 @@
     fun noCriteria_fallbackToDefaultCriteria() {
         with(kosmos) {
             testScope.runTest {
-                enabledComponents =
-                    setOf(
-                        BOTTOM_BAR,
-                        COMPONENT_1,
-                        COMPONENT_2,
-                    )
+                enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
                 criteriaByKey =
                     mapOf(
                         BOTTOM_BAR to Provider { availableCriteria },
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
index 235475f..0fc3470 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
@@ -44,5 +44,5 @@
     fun onZenDataChanged(data: ZenData)
 
     /** Update reactive axes for this clock */
-    fun onFontAxesChanged(axes: List<ClockFontAxisSetting>)
+    fun onFontAxesChanged(axes: ClockAxisStyle)
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
index 0cbc30d..2147ca1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
@@ -35,10 +35,54 @@
     /** Font axes that can be modified on this clock */
     val axes: List<ClockFontAxis> = listOf(),
 
-    /** List of font presets for this clock. Can be assigned directly. */
-    val axisPresets: List<List<ClockFontAxisSetting>> = listOf(),
+    /** Presets for this clock. Null indicates the preset list should be disabled. */
+    val presetConfig: AxisPresetConfig? = null,
 )
 
+data class AxisPresetConfig(
+    /** Groups of Presets. Each group can be used together in a single control. */
+    val groups: List<Group>,
+
+    /** Preset item currently being used, null when the current style is not a preset */
+    val current: IndexedStyle? = null,
+) {
+    /** The selected clock axis style, and its indices */
+    data class IndexedStyle(
+        /** Index of the group that this clock axis style appears in */
+        val groupIndex: Int,
+
+        /** Index of the preset within the group */
+        val presetIndex: Int,
+
+        /** Reference to the style in question */
+        val style: ClockAxisStyle,
+    )
+
+    /** A group of preset styles */
+    data class Group(
+        /* List of preset styles in this group */
+        val presets: List<ClockAxisStyle>,
+
+        /* Icon to use when this preset-group is active */
+        val icon: Drawable,
+    )
+
+    fun findStyle(style: ClockAxisStyle): IndexedStyle? {
+        groups.forEachIndexed { groupIndex, group ->
+            group.presets.forEachIndexed { presetIndex, preset ->
+                if (preset == style) {
+                    return@findStyle IndexedStyle(
+                        groupIndex = groupIndex,
+                        presetIndex = presetIndex,
+                        style = preset,
+                    )
+                }
+            }
+        }
+        return null
+    }
+}
+
 /** Represents an Axis that can be modified */
 data class ClockFontAxis(
     /** Axis key, not user renderable */
@@ -62,19 +106,12 @@
     /** Description of the axis */
     val description: String,
 ) {
-    fun toSetting() = ClockFontAxisSetting(key, currentValue)
-
     companion object {
-        fun List<ClockFontAxis>.merge(
-            axisSettings: List<ClockFontAxisSetting>
-        ): List<ClockFontAxis> {
-            val result = mutableListOf<ClockFontAxis>()
-            for (axis in this) {
-                val setting = axisSettings.firstOrNull { axis.key == it.key }
-                val output = setting?.let { axis.copy(currentValue = it.value) } ?: axis
-                result.add(output)
-            }
-            return result
+        fun List<ClockFontAxis>.merge(axisStyle: ClockAxisStyle): List<ClockFontAxis> {
+            return this.map { axis ->
+                    axisStyle.get(axis.key)?.let { axis.copy(currentValue = it) } ?: axis
+                }
+                .toList()
         }
     }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
index e7b3662..cccc558 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
@@ -22,7 +22,7 @@
 data class ClockSettings(
     val clockId: ClockId? = null,
     val seedColor: Int? = null,
-    val axes: List<ClockFontAxisSetting> = listOf(),
+    val axes: ClockAxisStyle = ClockAxisStyle(),
 ) {
     // Exclude metadata from equality checks
     var metadata: JSONObject = JSONObject()
@@ -38,15 +38,15 @@
                 put(KEY_CLOCK_ID, setting.clockId)
                 put(KEY_SEED_COLOR, setting.seedColor)
                 put(KEY_METADATA, setting.metadata)
-                put(KEY_AXIS_LIST, ClockFontAxisSetting.toJson(setting.axes))
+                put(KEY_AXIS_LIST, ClockAxisStyle.toJson(setting.axes))
             }
         }
 
         fun fromJson(json: JSONObject): ClockSettings {
             val clockId = if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null
             val seedColor = if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
-            val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockFontAxisSetting::fromJson)
-            return ClockSettings(clockId, seedColor, axisList ?: listOf()).apply {
+            val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockAxisStyle::fromJson)
+            return ClockSettings(clockId, seedColor, axisList ?: ClockAxisStyle()).apply {
                 metadata = json.optJSONObject(KEY_METADATA) ?: JSONObject()
             }
         }
@@ -54,64 +54,102 @@
 }
 
 @Keep
-/** Axis setting value for a clock */
-data class ClockFontAxisSetting(
-    /** Axis key; matches ClockFontAxis.key */
-    val key: String,
+class ClockAxisStyle {
+    private val settings: MutableMap<String, Float>
 
-    /** Value to set this axis to */
-    val value: Float,
-) {
+    // Iterable would be implemented on ClockAxisStyle directly,
+    // but that doesn't appear to work with plugins/dynamic libs.
+    val items: Iterable<Map.Entry<String, Float>>
+        get() = settings.asIterable()
+
+    val isEmpty: Boolean
+        get() = settings.isEmpty()
+
+    constructor(initialize: ClockAxisStyle.() -> Unit = {}) {
+        settings = mutableMapOf()
+        this.initialize()
+    }
+
+    constructor(style: ClockAxisStyle) {
+        settings = style.settings.toMutableMap()
+    }
+
+    constructor(items: Map<String, Float>) {
+        settings = items.toMutableMap()
+    }
+
+    constructor(key: String, value: Float) {
+        settings = mutableMapOf(key to value)
+    }
+
+    constructor(items: List<ClockFontAxis>) {
+        settings = items.associate { it.key to it.currentValue }.toMutableMap()
+    }
+
+    fun copy(initialize: ClockAxisStyle.() -> Unit): ClockAxisStyle {
+        return ClockAxisStyle(this).apply { initialize() }
+    }
+
+    operator fun get(key: String): Float? = settings[key]
+
+    operator fun set(key: String, value: Float) = put(key, value)
+
+    fun put(key: String, value: Float) {
+        settings.put(key, value)
+    }
+
+    fun toFVar(): String {
+        val sb = StringBuilder()
+        for (axis in settings) {
+            if (sb.length > 0) sb.append(", ")
+            sb.append("'${axis.key}' ${axis.value.toInt()}")
+        }
+        return sb.toString()
+    }
+
+    fun copyWith(replacements: ClockAxisStyle): ClockAxisStyle {
+        val result = ClockAxisStyle(this)
+        for ((key, value) in replacements.settings) {
+            result[key] = value
+        }
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ClockAxisStyle) return false
+        return settings == other.settings
+    }
+
     companion object {
         private val KEY_AXIS_KEY = "key"
         private val KEY_AXIS_VALUE = "value"
 
-        fun toJson(setting: ClockFontAxisSetting): JSONObject {
-            return JSONObject().apply {
-                put(KEY_AXIS_KEY, setting.key)
-                put(KEY_AXIS_VALUE, setting.value)
-            }
-        }
-
-        fun toJson(settings: List<ClockFontAxisSetting>): JSONArray {
-            return JSONArray().apply {
-                for (axis in settings) {
-                    put(toJson(axis))
-                }
-            }
-        }
-
-        fun fromJson(jsonObj: JSONObject): ClockFontAxisSetting {
-            return ClockFontAxisSetting(
-                key = jsonObj.getString(KEY_AXIS_KEY),
-                value = jsonObj.getDouble(KEY_AXIS_VALUE).toFloat(),
-            )
-        }
-
-        fun fromJson(jsonArray: JSONArray): List<ClockFontAxisSetting> {
-            val result = mutableListOf<ClockFontAxisSetting>()
+        fun fromJson(jsonArray: JSONArray): ClockAxisStyle {
+            val result = ClockAxisStyle()
             for (i in 0..jsonArray.length() - 1) {
                 val obj = jsonArray.getJSONObject(i)
                 if (obj == null) continue
-                result.add(fromJson(obj))
+
+                result.put(
+                    key = obj.getString(KEY_AXIS_KEY),
+                    value = obj.getDouble(KEY_AXIS_VALUE).toFloat(),
+                )
             }
             return result
         }
 
-        fun List<ClockFontAxisSetting>.toFVar(): String {
-            val sb = StringBuilder()
-            for (axis in this) {
-                if (sb.length > 0) sb.append(", ")
-                sb.append("'${axis.key}' ${axis.value.toInt()}")
+        fun toJson(style: ClockAxisStyle): JSONArray {
+            return JSONArray().apply {
+                for ((key, value) in style.settings) {
+                    put(
+                        JSONObject().apply {
+                            put(KEY_AXIS_KEY, key)
+                            put(KEY_AXIS_VALUE, value)
+                        }
+                    )
+                }
             }
-            return sb.toString()
-        }
-
-        fun List<ClockFontAxisSetting>.replace(
-            replacements: List<ClockFontAxisSetting>
-        ): List<ClockFontAxisSetting> {
-            var remaining = this.filterNot { lhs -> replacements.any { rhs -> lhs.key == rhs.key } }
-            return remaining + replacements
         }
     }
 }
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 949a6ab..a1b26fc 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -85,13 +85,49 @@
             android:longClickable="false"/>
     </LinearLayout>
 
+    <LinearLayout
+        android:id="@+id/input_routing_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/preset_layout"
+        android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+        android:orientation="vertical"
+        android:visibility="gone">
+        <TextView
+            android:id="@+id/input_routing_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+            android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+            android:paddingStart="@dimen/hearing_devices_small_title_padding_horizontal"
+            android:text="@string/hearing_devices_input_routing_label"
+            android:textAppearance="@style/TextAppearance.Dialog.Title"
+            android:textSize="14sp"
+            android:gravity="center_vertical"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textDirection="locale"/>
+        <Spinner
+            android:id="@+id/input_routing_spinner"
+            style="@style/BluetoothTileDialog.Device"
+            android:layout_height="@dimen/bluetooth_dialog_device_height"
+            android:layout_marginTop="4dp"
+            android:paddingStart="0dp"
+            android:paddingEnd="0dp"
+            android:background="@drawable/hearing_devices_spinner_background"
+            android:popupBackground="@drawable/hearing_devices_spinner_popup_background"
+            android:dropDownWidth="match_parent"
+            android:longClickable="false"/>
+    </LinearLayout>
+
     <com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout
         android:id="@+id/ambient_layout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/preset_layout"
+        app:layout_constraintTop_toBottomOf="@id/input_routing_layout"
         android:layout_marginTop="@dimen/hearing_devices_layout_margin" />
 
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/notification_2025_info.xml b/packages/SystemUI/res/layout/notification_2025_info.xml
index 7b69166..fa852a2 100644
--- a/packages/SystemUI/res/layout/notification_2025_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_info.xml
@@ -18,6 +18,7 @@
 <!-- extends LinearLayout -->
 <com.android.systemui.statusbar.notification.row.NotificationInfo
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/notification_guts"
     android:layout_width="match_parent"
@@ -324,18 +325,34 @@
             </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
 
         </LinearLayout>
-
-        <LinearLayout
+        <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/bottom_buttons"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="@*android:dimen/notification_2025_margin"
             android:minHeight="@dimen/notification_2025_guts_button_size"
-            android:gravity="center_vertical"
-            >
+            android:gravity="center_vertical">
+
+            <TextView
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingTop="8dp"
+                android:paddingBottom="@*android:dimen/notification_2025_margin"
+                app:layout_constraintStart_toStartOf="parent"
+                android:gravity="center"
+                android:minWidth="@dimen/notification_2025_min_tap_target_size"
+                android:minHeight="@dimen/notification_2025_min_tap_target_size"
+                android:maxWidth="200dp"
+                style="@style/TextAppearance.NotificationInfo.Button"
+                android:textSize="@*android:dimen/notification_2025_action_text_size"
+                />
             <TextView
                 android:id="@+id/turn_off_notifications"
                 android:text="@string/inline_turn_off_notifications"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginEnd="32dp"
@@ -345,6 +362,8 @@
                 android:minWidth="@dimen/notification_2025_min_tap_target_size"
                 android:minHeight="@dimen/notification_2025_min_tap_target_size"
                 android:maxWidth="200dp"
+                app:layout_constraintStart_toEndOf="@id/inline_dismiss"
+                app:layout_constraintBaseline_toBaselineOf="@id/inline_dismiss"
                 style="@style/TextAppearance.NotificationInfo.Button"
                 android:textSize="@*android:dimen/notification_2025_action_text_size"/>
             <TextView
@@ -354,12 +373,18 @@
                 android:layout_height="wrap_content"
                 android:paddingTop="8dp"
                 android:paddingBottom="@*android:dimen/notification_2025_margin"
-                android:gravity="center"
+                android:gravity="end"
+                app:layout_constraintEnd_toEndOf="parent"
                 android:minWidth="@dimen/notification_2025_min_tap_target_size"
                 android:minHeight="@dimen/notification_2025_min_tap_target_size"
                 android:maxWidth="125dp"
                 style="@style/TextAppearance.NotificationInfo.Button"
                 android:textSize="@*android:dimen/notification_2025_action_text_size"/>
-        </LinearLayout>
+            <androidx.constraintlayout.helper.widget.Flow
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                app:constraint_referenced_ids="inline_dismiss,turn_off_notifications,done"
+                app:flow_wrapMode="chain"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
     </LinearLayout>
 </com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 089ceae..d4bd142 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -341,8 +341,8 @@
             android:paddingEnd="4dp"
             >
             <TextView
-                android:id="@+id/turn_off_notifications"
-                android:text="@string/inline_turn_off_notifications"
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentStart="true"
@@ -350,6 +350,19 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/inline_dismiss"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
             <TextView
                 android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 4850b35..d1755ef 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -143,8 +143,8 @@
             android:paddingEnd="4dp"
             >
             <TextView
-                android:id="@+id/turn_off_notifications"
-                android:text="@string/inline_turn_off_notifications"
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentStart="true"
@@ -152,6 +152,19 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/inline_dismiss"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
             <TextView
                 android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
index 2e0a0ca..3982a66 100644
--- a/packages/SystemUI/res/layout/promoted_notification_info.xml
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -373,8 +373,8 @@
             android:paddingEnd="4dp"
             >
             <TextView
-                android:id="@+id/turn_off_notifications"
-                android:text="@string/inline_turn_off_notifications"
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentStart="true"
@@ -382,6 +382,19 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/inline_dismiss"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
             <TextView
                 android:id="@+id/done"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f4c6904..15519ff 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -34,7 +34,7 @@
 
     <!-- Base colors for notification shade/scrim, the alpha component is adjusted programmatically
     to match the spec -->
-    <color name="shade_panel_base">@android:color/system_accent1_900</color>
+    <color name="shade_panel_base">@android:color/system_accent1_100</color>
     <color name="notification_scrim_base">@android:color/system_accent1_100</color>
 
     <!-- Fallback colors for notification shade/scrim -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cd94a26..3fdb98b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1016,6 +1016,13 @@
     <string name="hearing_devices_presets_error">Couldn\'t update preset</string>
     <!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
     <string name="hearing_devices_preset_label">Preset</string>
+    <!-- QuickSettings: Title for hearing aids input routing control in hearing device dialog. [CHAR LIMIT=40]-->
+    <string name="hearing_devices_input_routing_label">Default microphone for calls</string>
+    <!-- QuickSettings: Option for hearing aids input routing control in hearing device dialog. It will alter input routing for calls for hearing aid. [CHAR LIMIT=40]-->
+    <string-array name="hearing_device_input_routing_options">
+        <item>Hearing aid microphone</item>
+        <item>This phone\'s microphone</item>
+    </string-array>
     <!-- QuickSettings: Content description for the icon that indicates the item is selected [CHAR LIMIT=NONE]-->
     <string name="hearing_devices_spinner_item_selected">Selected</string>
     <!-- QuickSettings: Title for ambient controls. [CHAR LIMIT=40]-->
@@ -2056,7 +2063,7 @@
     <string name="inline_ok_button">Apply</string>
 
     <!-- Notification inline controls: button to show block screen [CHAR_LIMIT=35] -->
-    <string name="inline_turn_off_notifications">Turn off notifications</string>
+    <string name="inline_turn_off_notifications">Turn off</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance title -->
     <string name="notification_silence_title">Silent</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 763b107..f2f1773 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -572,10 +572,6 @@
     }
 
     fun handleFidgetTap(x: Float, y: Float) {
-        if (!com.android.systemui.Flags.clockFidgetAnimation()) {
-            return
-        }
-
         clock?.run {
             smallClock.animations.onFidgetTap(x, y)
             largeClock.animations.onFidgetTap(x, y)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d84b034..60ec051 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -124,7 +124,6 @@
 import com.android.settingslib.WirelessUtils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
 import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -301,7 +300,8 @@
     private final Provider<SceneInteractor> mSceneInteractor;
     private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
     private final Provider<CommunalSceneInteractor> mCommunalSceneInteractor;
-    private final KeyguardServiceShowLockscreenInteractor mKeyguardServiceShowLockscreenInteractor;
+    private final Provider<KeyguardServiceShowLockscreenInteractor>
+            mKeyguardServiceShowLockscreenInteractor;
     private final AuthController mAuthController;
     private final UiEventLogger mUiEventLogger;
     private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -2219,7 +2219,8 @@
             Provider<JavaAdapter> javaAdapter,
             Provider<SceneInteractor> sceneInteractor,
             Provider<CommunalSceneInteractor> communalSceneInteractor,
-            KeyguardServiceShowLockscreenInteractor keyguardServiceShowLockscreenInteractor) {
+            Provider<KeyguardServiceShowLockscreenInteractor>
+                    keyguardServiceShowLockscreenInteractor) {
         mContext = context;
         mSubscriptionManager = subscriptionManager;
         mUserTracker = userTracker;
@@ -2553,7 +2554,7 @@
 
         if (KeyguardWmStateRefactor.isEnabled()) {
             mJavaAdapter.get().alwaysCollectFlow(
-                    mKeyguardServiceShowLockscreenInteractor.getShowNowEvents(),
+                    mKeyguardServiceShowLockscreenInteractor.get().getShowNowEvents(),
                     this::onKeyguardServiceShowLockscreenNowEvents
             );
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index c78f75a..0894667 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -894,8 +894,8 @@
                 if (NotificationBundleUi.isEnabled()) {
                     return enr.getEntryAdapter().canDragAndDrop();
                 } else {
-                    boolean canBubble = enr.getEntry().canBubble();
-                    Notification notif = enr.getEntry().getSbn().getNotification();
+                    boolean canBubble = enr.getEntryLegacy().canBubble();
+                    Notification notif = enr.getEntryLegacy().getSbn().getNotification();
                     PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
                             : notif.fullScreenIntent;
                     if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index fb3bc62..14b13d1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -121,6 +121,13 @@
     private Spinner mPresetSpinner;
     private HearingDevicesPresetsController mPresetController;
     private HearingDevicesSpinnerAdapter mPresetInfoAdapter;
+
+    private View mInputRoutingLayout;
+    private Spinner mInputRoutingSpinner;
+    private HearingDevicesInputRoutingController.Factory mInputRoutingControllerFactory;
+    private HearingDevicesInputRoutingController mInputRoutingController;
+    private HearingDevicesSpinnerAdapter mInputRoutingAdapter;
+
     private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
             new HearingDevicesPresetsController.PresetCallback() {
                 @Override
@@ -169,7 +176,8 @@
             @Background Executor bgExecutor,
             AudioManager audioManager,
             HearingDevicesUiEventLogger uiEventLogger,
-            QSSettingsPackageRepository qsSettingsPackageRepository) {
+            QSSettingsPackageRepository qsSettingsPackageRepository,
+            HearingDevicesInputRoutingController.Factory inputRoutingControllerFactory) {
         mShowPairNewDevice = showPairNewDevice;
         mSystemUIDialogFactory = systemUIDialogFactory;
         mActivityStarter = activityStarter;
@@ -182,6 +190,7 @@
         mUiEventLogger = uiEventLogger;
         mLaunchSourceId = launchSourceId;
         mQSSettingsPackageRepository = qsSettingsPackageRepository;
+        mInputRoutingControllerFactory = inputRoutingControllerFactory;
     }
 
     @Override
@@ -239,6 +248,12 @@
                 mPresetLayout.setVisibility(
                         mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
             }
+            if (mInputRoutingController != null) {
+                mInputRoutingController.setDevice(device);
+                mInputRoutingController.isInputRoutingControlAvailable(
+                        available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+                                available ? VISIBLE : GONE)));
+            }
             if (mAmbientController != null) {
                 mAmbientController.loadDevice(device);
             }
@@ -310,6 +325,11 @@
                 setupDeviceListView(dialog, hearingDeviceItemList);
                 setupPairNewDeviceButton(dialog);
                 setupPresetSpinner(dialog, activeHearingDevice);
+                if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()
+                        && com.android.systemui.Flags
+                        .hearingDevicesInputRoutingUiImprovement()) {
+                    setupInputRoutingSpinner(dialog, activeHearingDevice);
+                }
                 if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
                     setupAmbientControls(activeHearingDevice);
                 }
@@ -383,6 +403,52 @@
         mBgExecutor.execute(() -> mPresetController.registerHapCallback());
     }
 
+    private void setupInputRoutingSpinner(SystemUIDialog dialog,
+            CachedBluetoothDevice activeHearingDevice) {
+        mInputRoutingController = mInputRoutingControllerFactory.create(dialog.getContext());
+        mInputRoutingController.setDevice(activeHearingDevice);
+
+        mInputRoutingSpinner = dialog.requireViewById(R.id.input_routing_spinner);
+        mInputRoutingAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
+        mInputRoutingAdapter.addAll(
+                HearingDevicesInputRoutingController.getInputRoutingOptions(dialog.getContext()));
+        mInputRoutingSpinner.setAdapter(mInputRoutingAdapter);
+        // Disable redundant Touch & Hold accessibility action for Switch Access
+        mInputRoutingSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+                    @NonNull AccessibilityNodeInfo info) {
+                info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+                super.onInitializeAccessibilityNodeInfo(host, info);
+            }
+        });
+        // Should call setSelection(index, false) for the spinner before setOnItemSelectedListener()
+        // to avoid extra onItemSelected() get called when first register the listener.
+        final int initialPosition =
+                mInputRoutingController.getUserPreferredInputRoutingValue();
+        mInputRoutingSpinner.setSelection(initialPosition, false);
+        mInputRoutingAdapter.setSelected(initialPosition);
+        mInputRoutingSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                mInputRoutingAdapter.setSelected(position);
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_INPUT_ROUTING_SELECT,
+                        mLaunchSourceId);
+                mInputRoutingController.selectInputRouting(position);
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // Do nothing
+            }
+        });
+
+        mInputRoutingLayout = dialog.requireViewById(R.id.input_routing_layout);
+        mInputRoutingController.isInputRoutingControlAvailable(
+                available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+                        available ? VISIBLE : GONE)));
+    }
+
     private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
         final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
         ambientLayout.setUiEventLogger(mUiEventLogger, mLaunchSourceId);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
new file mode 100644
index 0000000..e8fa7ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid
+
+import android.content.Context
+import android.media.AudioManager
+import android.util.Log
+import androidx.collection.ArraySet
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * The controller of the hearing device input routing.
+ *
+ * <p> It manages and update the input routing according to the value.
+ */
+open class HearingDevicesInputRoutingController
+@AssistedInject
+constructor(
+    @Assisted context: Context,
+    private val audioManager: AudioManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+    private val audioRoutingHelper = HearingAidAudioRoutingHelper(context)
+    private var cachedDevice: CachedBluetoothDevice? = null
+    private val bgCoroutineScope = CoroutineScope(backgroundDispatcher)
+
+    /** Factory to create a [HearingDevicesInputRoutingController] instance. */
+    @AssistedFactory
+    interface Factory {
+        fun create(context: Context): HearingDevicesInputRoutingController
+    }
+
+    /** Possible input routing UI. Need to align with [getInputRoutingOptions] */
+    enum class InputRoutingValue {
+        HEARING_DEVICE,
+        BUILTIN_MIC,
+    }
+
+    companion object {
+        private const val TAG = "HearingDevicesInputRoutingController"
+
+        /** Gets input routing options as strings. */
+        @JvmStatic
+        fun getInputRoutingOptions(context: Context): Array<String> {
+            return context.resources.getStringArray(R.array.hearing_device_input_routing_options)
+        }
+    }
+
+    fun interface InputRoutingControlAvailableCallback {
+        fun onResult(available: Boolean)
+    }
+
+    /**
+     * Sets the device for this controller to control the input routing.
+     *
+     * @param device the [CachedBluetoothDevice] set to the controller
+     */
+    fun setDevice(device: CachedBluetoothDevice?) {
+        this@HearingDevicesInputRoutingController.cachedDevice = device
+    }
+
+    fun isInputRoutingControlAvailable(callback: InputRoutingControlAvailableCallback) {
+        bgCoroutineScope.launch {
+            val result = isInputRoutingControlAvailableInternal()
+            callback.onResult(result)
+        }
+    }
+
+    /**
+     * Checks if input routing control is available for the currently set device.
+     *
+     * @return `true` if input routing control is available.
+     */
+    private suspend fun isInputRoutingControlAvailableInternal(): Boolean {
+        val device = cachedDevice ?: return false
+
+        val memberDevices = device.memberDevice
+
+        val inputInfos =
+            withContext(backgroundDispatcher) {
+                audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)
+            }
+        val supportedInputDeviceAddresses = ArraySet<String>()
+        supportedInputDeviceAddresses.add(device.address)
+        if (memberDevices.isNotEmpty()) {
+            memberDevices.forEach { supportedInputDeviceAddresses.add(it.address) }
+        }
+
+        val isValidInputDevice =
+            inputInfos.any { supportedInputDeviceAddresses.contains(it.address) }
+        // Not support ASHA hearing device for input routing feature
+        val isHapHearingDevice = device.profiles.any { profile -> profile is HapClientProfile }
+
+        if (isHapHearingDevice && !isValidInputDevice) {
+            Log.d(TAG, "Not supported input type hearing device.")
+        }
+        return isHapHearingDevice && isValidInputDevice
+    }
+
+    /** Gets the user's preferred [InputRoutingValue]. */
+    fun getUserPreferredInputRoutingValue(): Int {
+        val device = cachedDevice ?: return InputRoutingValue.HEARING_DEVICE.ordinal
+
+        return if (device.device.isMicrophonePreferredForCalls) {
+            InputRoutingValue.HEARING_DEVICE.ordinal
+        } else {
+            InputRoutingValue.BUILTIN_MIC.ordinal
+        }
+    }
+
+    /**
+     * Sets the input routing to [android.bluetooth.BluetoothDevice.setMicrophonePreferredForCalls]
+     * based on the input routing index.
+     *
+     * @param inputRoutingIndex The desired input routing index.
+     */
+    fun selectInputRouting(inputRoutingIndex: Int) {
+        val device = cachedDevice ?: return
+
+        val useBuiltinMic = (inputRoutingIndex == InputRoutingValue.BUILTIN_MIC.ordinal)
+        val status =
+            audioRoutingHelper.setPreferredInputDeviceForCalls(
+                device,
+                if (useBuiltinMic) HearingAidAudioRoutingConstants.RoutingValue.BUILTIN_DEVICE
+                else HearingAidAudioRoutingConstants.RoutingValue.AUTO,
+            )
+        if (!status) {
+            Log.d(TAG, "Fail to configure setPreferredInputDeviceForCalls")
+        }
+        setMicrophonePreferredForCallsForDeviceSet(device, !useBuiltinMic)
+    }
+
+    private fun setMicrophonePreferredForCallsForDeviceSet(
+        device: CachedBluetoothDevice?,
+        enabled: Boolean,
+    ) {
+        device ?: return
+        device.device.isMicrophonePreferredForCalls = enabled
+        val memberDevices = device.memberDevice
+        if (memberDevices.isNotEmpty()) {
+            memberDevices.forEach { d -> d.device.isMicrophonePreferredForCalls = enabled }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
index 4a695d6..82ac10d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -40,6 +40,8 @@
     HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS(2153),
     @UiEvent(doc = "Collapse the ambient volume controls")
     HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS(2154),
+    @UiEvent(doc = "Select a input routing option from input routing spinner")
+    HEARING_DEVICES_INPUT_ROUTING_SELECT(2155),
     @UiEvent(doc = "Click on the device settings to enter hearing devices page")
     HEARING_DEVICES_SETTINGS_CLICK(2172);
 
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
index df6c1b1..8cebe04 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -24,11 +24,17 @@
 import android.net.Uri
 import android.text.TextUtils
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
+import java.util.function.Consumer
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 @SysUISingleton
-class ActionIntentCreator @Inject constructor() : IntentCreator {
+class ActionIntentCreator
+@Inject
+constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator {
     override fun getTextEditorIntent(context: Context?) =
         Intent(context, EditTextActivity::class.java).apply {
             addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
@@ -65,7 +71,7 @@
             .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
     }
 
-    override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+    suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent {
         val editorPackage = context.getString(R.string.config_screenshotEditor)
         return Intent(Intent.ACTION_EDIT).apply {
             if (!TextUtils.isEmpty(editorPackage)) {
@@ -78,6 +84,14 @@
         }
     }
 
+    override fun getImageEditIntentAsync(
+        uri: Uri?,
+        context: Context,
+        outputConsumer: Consumer<Intent>,
+    ) {
+        applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) }
+    }
+
     override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
         val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
         return Intent(REMOTE_COPY_ACTION).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 314b6e7..984d247 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -558,8 +558,10 @@
 
     private void editImage(Uri uri) {
         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
-        mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
-        animateOut();
+        mIntentCreator.getImageEditIntentAsync(uri, mContext, intent -> {
+            mContext.startActivity(intent);
+            animateOut();
+        });
     }
 
     private void editText() {
@@ -747,8 +749,10 @@
                             mIntentCreator.getTextEditorIntent(mContext));
                     break;
                 case IMAGE:
-                    finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
-                            mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+                    mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext,
+                            intent -> {
+                                finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent);
+                            });
                     break;
                 default:
                     Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
index 4b24536..e9a9cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -27,6 +27,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.res.R;
 
+import java.util.function.Consumer;
+
 import javax.inject.Inject;
 
 @SysUISingleton
@@ -73,7 +75,7 @@
         return chooserIntent;
     }
 
-    public Intent getImageEditIntent(Uri uri, Context context) {
+    public void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer) {
         String editorPackage = context.getString(R.string.config_screenshotEditor);
         Intent editIntent = new Intent(Intent.ACTION_EDIT);
         if (!TextUtils.isEmpty(editorPackage)) {
@@ -83,7 +85,7 @@
         editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
-        return editIntent;
+        outputConsumer.accept(editIntent);
     }
 
     public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index c8a6b05..283596f 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -21,9 +21,11 @@
 import android.content.Intent;
 import android.net.Uri;
 
+import java.util.function.Consumer;
+
 public interface IntentCreator {
     Intent getTextEditorIntent(Context context);
     Intent getShareIntent(ClipData clipData, Context context);
-    Intent getImageEditIntent(Uri uri, Context context);
+    void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer);
     Intent getRemoteCopyIntent(ClipData clipData, Context context);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
index 9db7b50..1301fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.common.domain.interactor
 
 import android.util.Log
+import com.android.app.displaylib.PerDisplayRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.PerDisplayRepository
 import com.android.systemui.model.StateChange
 import com.android.systemui.model.SysUiState
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 06a14ea..440c300 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.widgets
 
+import android.appwidget.AppWidgetProviderInfo
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -101,6 +102,7 @@
                     val (_, isActive) = withPrev
                     // The validation is performed once the hub becomes active.
                     if (isActive) {
+                        removeNotLockscreenWidgets(widgets)
                         validateWidgetsAndDeleteOrphaned(widgets)
                     }
                 }
@@ -144,6 +146,19 @@
             }
         }
 
+    private fun removeNotLockscreenWidgets(widgets: List<CommunalWidgetContentModel>) {
+        widgets
+            .filter { widget ->
+                when (widget) {
+                    is CommunalWidgetContentModel.Available ->
+                        widget.providerInfo.widgetCategory and
+                            AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD != 0
+                    else -> false
+                }
+            }
+            .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+    }
+
     /**
      * Ensure the existence of all associated users for widgets, and remove widgets belonging to
      * users who have been deleted.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
index 39708a7..3520439 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.dagger
 
-import com.android.systemui.display.data.repository.DefaultDisplayOnlyInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
+import com.android.app.displaylib.DefaultDisplayOnlyInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
 import com.android.systemui.model.SysUIStateInstanceProvider
 import com.android.systemui.model.SysUiState
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f08126a..854c610 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -65,7 +65,7 @@
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.deviceentry.DeviceEntryModule;
 import com.android.systemui.display.DisplayModule;
-import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.app.displaylib.PerDisplayRepository;
 import com.android.systemui.doze.dagger.DozeComponent;
 import com.android.systemui.dreams.dagger.DreamModule;
 import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 9b181be..908d0aa 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -16,8 +16,15 @@
 
 package com.android.systemui.display
 
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import com.android.app.displaylib.DisplayLibBackground
+import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.createDisplayLibComponent
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
 import com.android.systemui.display.data.repository.DisplayRepository
@@ -28,6 +35,7 @@
 import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
 import com.android.systemui.display.data.repository.FocusedDisplayRepository
 import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
+import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
 import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
@@ -40,9 +48,11 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
 
 /** Module binding display related classes. */
-@Module(includes = [DisplayWindowPropertiesInteractorModule::class])
+@Module(includes = [DisplayWindowPropertiesInteractorModule::class, DisplayLibModule::class])
 interface DisplayModule {
     @Binds
     fun bindConnectedDisplayInteractor(
@@ -73,6 +83,13 @@
         impl: DisplayWindowPropertiesRepositoryImpl
     ): DisplayWindowPropertiesRepository
 
+    @Binds
+    fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback
+
+    @Binds
+    @DisplayLibBackground
+    fun bindDisplayLibBackground(@Background bgScope: CoroutineScope): CoroutineScope
+
     companion object {
         @Provides
         @SysUISingleton
@@ -103,3 +120,31 @@
         }
     }
 }
+
+/** Module to bind the DisplayRepository from displaylib to the systemui dagger graph. */
+@Module
+object DisplayLibModule {
+    @Provides
+    @SysUISingleton
+    fun displayLibComponent(
+        displayManager: DisplayManager,
+        @Background backgroundHandler: Handler,
+        @Background bgApplicationScope: CoroutineScope,
+        @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
+    ): DisplayLibComponent {
+        return createDisplayLibComponent(
+            displayManager,
+            backgroundHandler,
+            bgApplicationScope,
+            backgroundCoroutineDispatcher,
+        )
+    }
+
+    @Provides
+    @SysUISingleton
+    fun providesDisplayRepositoryFromLib(
+        displayLibComponent: DisplayLibComponent
+    ): com.android.app.displaylib.DisplayRepository {
+        return displayLibComponent.displayRepository
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt b/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
deleted file mode 100644
index 626a68f..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
+++ /dev/null
@@ -1,24 +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.display.data
-
-sealed interface DisplayEvent {
-    val displayId: Int
-    data class Added(override val displayId: Int) : DisplayEvent
-    data class Removed(override val displayId: Int) : DisplayEvent
-    data class Changed(override val displayId: Int) : DisplayEvent
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 721d116..051fe7e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,112 +17,30 @@
 package com.android.systemui.display.data.repository
 
 import android.annotation.SuppressLint
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
-import android.hardware.display.DisplayManager.DisplayListener
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED
-import android.os.Handler
-import android.util.Log
-import android.view.Display
 import android.view.IWindowManager
-import com.android.app.tracing.FlowTracing.traceEach
-import com.android.app.tracing.traceSection
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.display.data.DisplayEvent
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.Compile
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asFlow
 import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.stateIn
 
 /** Repository for providing access to display related information and events. */
-interface DisplayRepository {
-    /** Display change event indicating a change to the given displayId has occurred. */
-    val displayChangeEvent: Flow<Int>
-
-    /** Display addition event indicating a new display has been added. */
-    val displayAdditionEvent: Flow<Display?>
-
-    /** Display removal event indicating a display has been removed. */
-    val displayRemovalEvent: Flow<Int>
+interface DisplayRepository : DisplayRepositoryFromLib {
 
     /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
     val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-
-    /**
-     * Provides the current set of displays.
-     *
-     * Consider using [displayIds] if only the [Display.getDisplayId] is needed.
-     */
-    val displays: StateFlow<Set<Display>>
-
-    /**
-     * Provides the current set of display ids.
-     *
-     * Note that it is preferred to use this instead of [displays] if only the
-     * [Display.getDisplayId] is needed.
-     */
-    val displayIds: StateFlow<Set<Int>>
-
-    /**
-     * Pending display id that can be enabled/disabled.
-     *
-     * When `null`, it means there is no pending display waiting to be enabled.
-     */
-    val pendingDisplay: Flow<PendingDisplay?>
-
-    /** Whether the default display is currently off. */
-    val defaultDisplayOff: Flow<Boolean>
-
-    /**
-     * Given a display ID int, return the corresponding Display object, or null if none exist.
-     *
-     * This method is guaranteed to not result in any binder call.
-     */
-    fun getDisplay(displayId: Int): Display? =
-        displays.value.firstOrNull { it.displayId == displayId }
-
-    /** Represents a connected display that has not been enabled yet. */
-    interface PendingDisplay {
-        /** Id of the pending display. */
-        val id: Int
-
-        /** Enables the display, making it available to the system. */
-        suspend fun enable()
-
-        /**
-         * Ignores the pending display. When called, this specific display id doesn't appear as
-         * pending anymore until the display is disconnected and reconnected again.
-         */
-        suspend fun ignore()
-
-        /** Disables the display, making it unavailable to the system. */
-        suspend fun disable()
-    }
 }
 
 @SysUISingleton
@@ -130,310 +48,11 @@
 class DisplayRepositoryImpl
 @Inject
 constructor(
-    private val displayManager: DisplayManager,
     private val commandQueue: CommandQueue,
     private val windowManager: IWindowManager,
-    @Background backgroundHandler: Handler,
     @Background bgApplicationScope: CoroutineScope,
-    @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
-) : DisplayRepository {
-    private val allDisplayEvents: Flow<DisplayEvent> =
-        conflatedCallbackFlow {
-                val callback =
-                    object : DisplayListener {
-                        override fun onDisplayAdded(displayId: Int) {
-                            trySend(DisplayEvent.Added(displayId))
-                        }
-
-                        override fun onDisplayRemoved(displayId: Int) {
-                            trySend(DisplayEvent.Removed(displayId))
-                        }
-
-                        override fun onDisplayChanged(displayId: Int) {
-                            trySend(DisplayEvent.Changed(displayId))
-                        }
-                    }
-                displayManager.registerDisplayListener(
-                    callback,
-                    backgroundHandler,
-                    EVENT_TYPE_DISPLAY_ADDED or
-                        EVENT_TYPE_DISPLAY_CHANGED or
-                        EVENT_TYPE_DISPLAY_REMOVED,
-                )
-                awaitClose { displayManager.unregisterDisplayListener(callback) }
-            }
-            .onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
-            .debugLog("allDisplayEvents")
-            .flowOn(backgroundCoroutineDispatcher)
-
-    override val displayChangeEvent: Flow<Int> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
-
-    override val displayRemovalEvent: Flow<Int> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
-
-    // This is necessary because there might be multiple displays, and we could
-    // have missed events for those added before this process or flow started.
-    // Note it causes a binder call from the main thread (it's traced).
-    private val initialDisplays: Set<Display> =
-        traceSection("$TAG#initialDisplays") { displayManager.displays?.toSet() ?: emptySet() }
-    private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
-
-    /** Propagate to the listeners only enabled displays */
-    private val enabledDisplayIds: StateFlow<Set<Int>> =
-        allDisplayEvents
-            .scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
-                val id = event.displayId
-                when (event) {
-                    is DisplayEvent.Removed -> previousIds - id
-                    is DisplayEvent.Added,
-                    is DisplayEvent.Changed -> previousIds + id
-                }
-            }
-            .distinctUntilChanged()
-            .debugLog("enabledDisplayIds")
-            .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
-
-    private val defaultDisplay by lazy {
-        getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
-            ?: error("Unable to get default display.")
-    }
-
-    /**
-     * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
-     *
-     * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
-     */
-    private val enabledDisplays: StateFlow<Set<Display>> =
-        enabledDisplayIds
-            .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
-            .onEach {
-                if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
-            }
-            .flowOn(backgroundCoroutineDispatcher)
-            .debugLog("enabledDisplays")
-            .stateIn(
-                bgApplicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                // This triggers a single binder call on the UI thread per process. The
-                // alternative would be to use sharedFlows, but they are prohibited due to
-                // performance concerns.
-                // Ultimately, this is a trade-off between a one-time UI thread binder call and
-                // the constant overhead of sharedFlows.
-                initialValue = initialDisplays,
-            )
-
-    /**
-     * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
-     *
-     * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
-     */
-    override val displays: StateFlow<Set<Display>> = enabledDisplays
-
-    override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
-
-    /**
-     * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
-     * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
-     *    calling [getDisplay] for the newly added display will be non-null.
-     * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager].
-     */
-    override val displayAdditionEvent: Flow<Display?> =
-        displays
-            .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays }
-            .flatMapLatest { it.asFlow() }
-
-    val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
-    private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
-
-    private fun getInitialConnectedDisplays(): Set<Int> =
-        traceSection("$TAG#getInitialConnectedDisplays") {
-            displayManager
-                .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
-                .map { it.displayId }
-                .toSet()
-                .also {
-                    if (DEBUG) {
-                        Log.d(TAG, "getInitialConnectedDisplays: $it")
-                    }
-                }
-        }
-
-    /* keeps connected displays until they are disconnected. */
-    private val connectedDisplayIds: StateFlow<Set<Int>> =
-        conflatedCallbackFlow {
-                val connectedIds = getInitialConnectedDisplays().toMutableSet()
-                val callback =
-                    object : DisplayConnectionListener {
-                        override fun onDisplayConnected(id: Int) {
-                            if (DEBUG) {
-                                Log.d(TAG, "display with id=$id connected.")
-                            }
-                            connectedIds += id
-                            _ignoredDisplayIds.value -= id
-                            trySend(connectedIds.toSet())
-                        }
-
-                        override fun onDisplayDisconnected(id: Int) {
-                            connectedIds -= id
-                            if (DEBUG) {
-                                Log.d(TAG, "display with id=$id disconnected.")
-                            }
-                            _ignoredDisplayIds.value -= id
-                            trySend(connectedIds.toSet())
-                        }
-                    }
-                trySend(connectedIds.toSet())
-                displayManager.registerDisplayListener(
-                    callback,
-                    backgroundHandler,
-                    /* eventFlags */ 0,
-                    DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED,
-                )
-                awaitClose { displayManager.unregisterDisplayListener(callback) }
-            }
-            .distinctUntilChanged()
-            .debugLog("connectedDisplayIds")
-            .stateIn(
-                bgApplicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                // The initial value is set to empty, but connected displays are gathered as soon as
-                // the flow starts being collected. This is to ensure the call to get displays (an
-                // IPC) happens in the background instead of when this object
-                // is instantiated.
-                initialValue = emptySet(),
-            )
-
-    private val connectedExternalDisplayIds: Flow<Set<Int>> =
-        connectedDisplayIds
-            .map { connectedDisplayIds ->
-                traceSection("$TAG#filteringExternalDisplays") {
-                    connectedDisplayIds
-                        .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
-                        .toSet()
-                }
-            }
-            .flowOn(backgroundCoroutineDispatcher)
-            .debugLog("connectedExternalDisplayIds")
-
-    private fun getDisplayType(displayId: Int): Int? =
-        traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
-
-    private fun getDisplayFromDisplayManager(displayId: Int): Display? =
-        traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
-
-    /**
-     * Pending displays are the ones connected, but not enabled and not ignored.
-     *
-     * A connected display is ignored after the user makes the decision to use it or not. For now,
-     * the initial decision from the user is final and not reversible.
-     */
-    private val pendingDisplayIds: Flow<Set<Int>> =
-        combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
-                enabledDisplaysIds,
-                connectedExternalDisplayIds,
-                ignoredDisplayIds ->
-                if (DEBUG) {
-                    Log.d(
-                        TAG,
-                        "combining enabled=$enabledDisplaysIds, " +
-                            "connectedExternalDisplayIds=$connectedExternalDisplayIds, " +
-                            "ignored=$ignoredDisplayIds",
-                    )
-                }
-                connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
-            }
-            .debugLog("allPendingDisplayIds")
-
-    /** Which display id should be enabled among the pending ones. */
-    private val pendingDisplayId: Flow<Int?> =
-        pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
-
-    override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
-        pendingDisplayId
-            .map { displayId ->
-                val id = displayId ?: return@map null
-                object : DisplayRepository.PendingDisplay {
-                    override val id = id
-
-                    override suspend fun enable() {
-                        traceSection("DisplayRepository#enable($id)") {
-                            if (DEBUG) {
-                                Log.d(TAG, "Enabling display with id=$id")
-                            }
-                            displayManager.enableConnectedDisplay(id)
-                        }
-                        // After the display has been enabled, it is automatically ignored.
-                        ignore()
-                    }
-
-                    override suspend fun ignore() {
-                        traceSection("DisplayRepository#ignore($id)") {
-                            _ignoredDisplayIds.value += id
-                        }
-                    }
-
-                    override suspend fun disable() {
-                        ignore()
-                        traceSection("DisplayRepository#disable($id)") {
-                            if (DEBUG) {
-                                Log.d(TAG, "Disabling display with id=$id")
-                            }
-                            displayManager.disableConnectedDisplay(id)
-                        }
-                    }
-                }
-            }
-            .debugLog("pendingDisplay")
-
-    override val defaultDisplayOff: Flow<Boolean> =
-        displayChangeEvent
-            .filter { it == Display.DEFAULT_DISPLAY }
-            .map { defaultDisplay.state == Display.STATE_OFF }
-            .distinctUntilChanged()
-
-    private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
-        return if (DEBUG) {
-            traceEach(flowName, logcat = true, traceEmissionCount = true)
-        } else {
-            this
-        }
-    }
-
-    /**
-     * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
-     * account the diff between each root flow emission.
-     *
-     * This is needed to minimize the number of [getDisplayFromDisplayManager] in this class. Note
-     * that if the [createValue] returns a null element, it will not be added in the output set.
-     */
-    private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
-        data class State<T, V>(
-            val previousSet: Set<T>,
-            // Caches T values from the previousSet that were already converted to V
-            val valueMap: Map<T, V>,
-            val resultSet: Set<V>,
-        )
-
-        val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
-        return this.scan(emptyInitialState) { state, currentSet ->
-                if (currentSet == state.previousSet) {
-                    state
-                } else {
-                    val removed = state.previousSet - currentSet
-                    val added = currentSet - state.previousSet
-                    val newMap = state.valueMap.toMutableMap()
-
-                    added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
-                    removed.forEach { key -> newMap.remove(key) }
-
-                    val resultSet = newMap.values.toSet()
-                    State(currentSet, newMap, resultSet)
-                }
-            }
-            .filter { it != emptyInitialState }
-            .map { it.resultSet }
-    }
+    private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
+) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository {
 
     private val decorationEvents: Flow<Event> = callbackFlow {
         val callback =
@@ -487,20 +106,5 @@
 
     private companion object {
         const val TAG = "DisplayRepository"
-        val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
     }
 }
-
-/** Used to provide default implementations for all methods. */
-private interface DisplayConnectionListener : DisplayListener {
-
-    override fun onDisplayConnected(id: Int) {}
-
-    override fun onDisplayDisconnected(id: Int) {}
-
-    override fun onDisplayAdded(id: Int) {}
-
-    override fun onDisplayRemoved(id: Int) {}
-
-    override fun onDisplayChanged(id: Int) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
index a56710e..86c9d84c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.display.data.repository
 
+import com.android.app.displaylib.PerDisplayRepository
+
+// TODO b/401305290 - move to displaylib
 class FakePerDisplayRepository<T> : PerDisplayRepository<T> {
 
     private val instances = mutableMapOf<Int, T>()
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
new file mode 100644
index 0000000..efbae5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.display.data.repository
+
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpableFromToString
+import javax.inject.Inject
+
+/** Helper class to register PerDisplayRepository in the dump manager in SystemUI. */
+@SysUISingleton
+class PerDisplayRepoDumpHelper @Inject constructor(private val dumpManager: DumpManager) :
+    PerDisplayRepository.InitCallback {
+    /**
+     * Registers PerDisplayRepository in the dump manager.
+     *
+     * The repository will be identified by the given debug name.
+     */
+    override fun onInit(debugName: String, instance: Any) {
+        dumpManager.registerNormalDumpable(
+            "PerDisplayRepository-$debugName",
+            DumpableFromToString(instance),
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
deleted file mode 100644
index d1d0135..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.display.data.repository
-
-import android.util.Log
-import android.view.Display
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.tracing.traceSection
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dump.DumpManager
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import java.io.PrintWriter
-import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Used to create instances of type `T` for a specific display.
- *
- * This is useful for resources or objects that need to be managed independently for each connected
- * display (e.g., UI state, rendering contexts, or display-specific configurations).
- *
- * Note that in most cases this can be implemented by a simple `@AssistedFactory` with `displayId`
- * parameter
- *
- * ```kotlin
- * class SomeType @AssistedInject constructor(@Assisted displayId: Int,..)
- *      @AssistedFactory
- *      interface Factory {
- *         fun create(displayId: Int): SomeType
- *      }
- *  }
- * ```
- *
- * Then it can be used to create a [PerDisplayRepository] as follows:
- * ```kotlin
- * // Injected:
- * val repositoryFactory: PerDisplayRepositoryImpl.Factory
- * val instanceFactory: PerDisplayRepositoryImpl.Factory
- * // repository creation:
- * repositoryFactory.create(instanceFactory::create)
- * ```
- *
- * @see PerDisplayRepository For how to retrieve and manage instances created by this factory.
- */
-fun interface PerDisplayInstanceProvider<T> {
-    /** Creates an instance for a display. */
-    fun createInstance(displayId: Int): T?
-}
-
-/**
- * Extends [PerDisplayInstanceProvider], adding support for destroying the instance.
- *
- * This is useful for releasing resources associated with a display when it is disconnected or when
- * the per-display instance is no longer needed.
- */
-interface PerDisplayInstanceProviderWithTeardown<T> : PerDisplayInstanceProvider<T> {
-    /** Destroys a previously created instance of `T` forever. */
-    fun destroyInstance(instance: T)
-}
-
-/**
- * Provides access to per-display instances of type `T`.
- *
- * Acts as a repository, managing the caching and retrieval of instances created by a
- * [PerDisplayInstanceProvider]. It ensures that only one instance of `T` exists per display ID.
- */
-interface PerDisplayRepository<T> {
-    /** Gets the cached instance or create a new one for a given display. */
-    operator fun get(displayId: Int): T?
-
-    /** Debug name for this repository, mainly for tracing and logging. */
-    val debugName: String
-}
-
-/**
- * Default implementation of [PerDisplayRepository].
- *
- * This class manages a cache of per-display instances of type `T`, creating them using a provided
- * [PerDisplayInstanceProvider] and optionally tearing them down using a
- * [PerDisplayInstanceProviderWithTeardown] when displays are disconnected.
- *
- * It listens to the [DisplayRepository] to detect when displays are added or removed, and
- * automatically manages the lifecycle of the per-display instances.
- *
- * Note that this is a [PerDisplayStoreImpl] 2.0 that doesn't require [CoreStartable] bindings,
- * providing all args in the constructor.
- */
-class PerDisplayInstanceRepositoryImpl<T>
-@AssistedInject
-constructor(
-    @Assisted override val debugName: String,
-    @Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
-    @Background private val backgroundApplicationScope: CoroutineScope,
-    private val displayRepository: DisplayRepository,
-    private val dumpManager: DumpManager,
-) : PerDisplayRepository<T>, Dumpable {
-
-    private val perDisplayInstances = ConcurrentHashMap<Int, T?>()
-
-    init {
-        backgroundApplicationScope.launch("$debugName#start") { start() }
-    }
-
-    private suspend fun start() {
-        dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this)
-        displayRepository.displayIds.collectLatest { displayIds ->
-            val toRemove = perDisplayInstances.keys - displayIds
-            toRemove.forEach { displayId ->
-                Log.d(TAG, "<$debugName> destroying instance for displayId=$displayId.")
-                perDisplayInstances.remove(displayId)?.let { instance ->
-                    (instanceProvider as? PerDisplayInstanceProviderWithTeardown)?.destroyInstance(
-                        instance
-                    )
-                }
-            }
-        }
-    }
-
-    override fun get(displayId: Int): T? {
-        if (displayRepository.getDisplay(displayId) == null) {
-            Log.e(TAG, "<$debugName: Display with id $displayId doesn't exist.")
-            return null
-        }
-
-        // If it doesn't exist, create it and put it in the map.
-        return perDisplayInstances.computeIfAbsent(displayId) { key ->
-            Log.d(TAG, "<$debugName> creating instance for displayId=$key, as it wasn't available.")
-            val instance =
-                traceSection({ "creating instance of $debugName for displayId=$key" }) {
-                    instanceProvider.createInstance(key)
-                }
-            if (instance == null) {
-                Log.e(
-                    TAG,
-                    "<$debugName> returning null because createInstance($key) returned null.",
-                )
-            }
-            instance
-        }
-    }
-
-    @AssistedFactory
-    interface Factory<T> {
-        fun create(
-            debugName: String,
-            instanceProvider: PerDisplayInstanceProvider<T>,
-        ): PerDisplayInstanceRepositoryImpl<T>
-    }
-
-    companion object {
-        private const val TAG = "PerDisplayInstanceRepo"
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println(perDisplayInstances)
-    }
-}
-
-/**
- * Provides an instance of a given class **only** for the default display, even if asked for another
- * display.
- *
- * This is useful in case of **flag refactors**: it can be provided instead of an instance of
- * [PerDisplayInstanceRepositoryImpl] when a flag related to multi display refactoring is off.
- *
- * Note that this still requires all instances to be provided by a [PerDisplayInstanceProvider]. If
- * you want to provide an existing instance instead for the default display, either implement it in
- * a custom [PerDisplayInstanceProvider] (e.g. inject it in the constructor and return it if the
- * displayId is zero), or use [SingleInstanceRepositoryImpl].
- */
-class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
-    override val debugName: String,
-    private val instanceProvider: PerDisplayInstanceProvider<T>,
-) : PerDisplayRepository<T> {
-    private val lazyDefaultDisplayInstance by lazy {
-        instanceProvider.createInstance(Display.DEFAULT_DISPLAY)
-    }
-    override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
-}
-
-/**
- * Always returns [instance] for any display.
- *
- * This can be used to provide a single instance based on a flag value during a refactor. Similar to
- * [DefaultDisplayOnlyInstanceRepositoryImpl], but also avoids creating the
- * [PerDisplayInstanceProvider]. This is useful when you want to provide an existing instance only,
- * without even instantiating a [PerDisplayInstanceProvider].
- */
-class SingleInstanceRepositoryImpl<T>(override val debugName: String, private val instance: T) :
-    PerDisplayRepository<T> {
-    override fun get(displayId: Int): T? = instance
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 15a3cbd..84f103e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -18,6 +18,7 @@
 
 import android.companion.virtual.VirtualDeviceManager
 import android.view.Display
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -138,7 +139,8 @@
             .distinctUntilChanged()
             .flowOn(backgroundCoroutineDispatcher)
 
-    private fun DisplayRepository.PendingDisplay.toInteractorPendingDisplay(): PendingDisplay =
+    private fun DisplayRepositoryFromLib.PendingDisplay.toInteractorPendingDisplay():
+        PendingDisplay =
         object : PendingDisplay {
             override suspend fun enable() = this@toInteractorPendingDisplay.enable()
 
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
new file mode 100644
index 0000000..438931a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dump
+
+import com.android.systemui.Dumpable
+import java.io.PrintWriter
+
+/** Dumpable implementation that just calls toString() on the instance. */
+class DumpableFromToString<T>(private val instance: T) : Dumpable {
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("$instance")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index cf5c340..f2a10cc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -147,14 +147,14 @@
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
+import dagger.Lazy;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Helper to show the global actions dialog.  Each item is an {@link Action} that may show depending
  * on whether the keyguard is showing, and whether the device is provisioned.
@@ -270,6 +270,16 @@
     private final UserLogoutInteractor mLogoutInteractor;
     private final GlobalActionsInteractor mInteractor;
     private final Lazy<DisplayWindowPropertiesRepository> mDisplayWindowPropertiesRepositoryLazy;
+    private final Handler mHandler;
+
+    private final UserTracker.Callback mOnUserSwitched = new UserTracker.Callback() {
+        @Override
+        public void onBeforeUserSwitching(int newUser) {
+            // Dismiss the dialog as soon as we start switching. This will schedule a message
+            // in a handler so it will be pretty quick.
+            dismissDialog();
+        }
+    };
 
     @VisibleForTesting
     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -425,6 +435,29 @@
         mInteractor = interactor;
         mDisplayWindowPropertiesRepositoryLazy = displayWindowPropertiesRepository;
 
+        mHandler = new Handler(mMainHandler.getLooper()) {
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MESSAGE_DISMISS:
+                        if (mDialog != null) {
+                            if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
+                                // Hide instantly.
+                                mDialog.hide();
+                                mDialog.dismiss();
+                            } else {
+                                mDialog.dismiss();
+                            }
+                            mDialog = null;
+                        }
+                        break;
+                    case MESSAGE_REFRESH:
+                        refreshSilentMode();
+                        mAdapter.notifyDataSetChanged();
+                        break;
+                }
+            }
+        };
+
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -537,6 +570,7 @@
                 expandable != null ? expandable.dialogTransitionController(
                         new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
                                 INTERACTION_JANK_TAG)) : null;
+        mUserTracker.addCallback(mOnUserSwitched, mBackgroundExecutor);
         if (controller != null) {
             mDialogTransitionAnimator.show(mDialog, controller);
         } else {
@@ -1404,6 +1438,7 @@
         mWindowManagerFuncs.onGlobalActionsHidden();
         mLifecycle.setCurrentState(Lifecycle.State.CREATED);
         mInteractor.onDismissed();
+        mUserTracker.removeCallback(mOnUserSwitched);
     }
 
     /**
@@ -2228,29 +2263,6 @@
         mDialogPressDelay = 0; // ms
     }
 
-    private Handler mHandler = new Handler() {
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_DISMISS:
-                    if (mDialog != null) {
-                        if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
-                            // Hide instantly.
-                            mDialog.hide();
-                            mDialog.dismiss();
-                        } else {
-                            mDialog.dismiss();
-                        }
-                        mDialog = null;
-                    }
-                    break;
-                case MESSAGE_REFRESH:
-                    refreshSilentMode();
-                    mAdapter.notifyDataSetChanged();
-                    break;
-            }
-        }
-    };
-
     private void onAirplaneModeChanged() {
         // Let the service state callbacks handle the state.
         if (mHasTelephony || mAirplaneModeOn == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 7574649..7968508 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -673,7 +673,8 @@
             if (SceneContainerFlag.isEnabled()) {
                 mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout");
             } else if (KeyguardWmStateRefactor.isEnabled()) {
-                mKeyguardServiceShowLockscreenInteractor.onKeyguardServiceDoKeyguardTimeout();
+                mKeyguardServiceShowLockscreenInteractor
+                        .onKeyguardServiceDoKeyguardTimeout(options);
             }
 
             mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 5869274..51b953e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -22,11 +22,14 @@
 import android.view.IRemoteAnimationFinishedCallback
 import android.view.RemoteAnimationTarget
 import android.view.WindowManager
+import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.window.flags.Flags
 import com.android.wm.shell.keyguard.KeyguardTransitions
 import java.util.concurrent.Executor
@@ -46,6 +49,9 @@
     private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
     private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
     private val keyguardTransitions: KeyguardTransitions,
+    private val selectedUserInteractor: SelectedUserInteractor,
+    private val lockPatternUtils: LockPatternUtils,
+    private val keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor,
 ) {
 
     /**
@@ -92,12 +98,23 @@
      *   second timeout).
      */
     private var isKeyguardGoingAway = false
-        private set(value) {
+        private set(goingAway) {
             // TODO(b/278086361): Extricate the keyguard state controller.
-            keyguardStateController.notifyKeyguardGoingAway(value)
-            field = value
+            keyguardStateController.notifyKeyguardGoingAway(goingAway)
+
+            if (goingAway) {
+                keyguardGoingAwayRequestedForUserId = selectedUserInteractor.getSelectedUserId()
+            }
+
+            field = goingAway
         }
 
+    /**
+     * The current user ID when we asked WM to start the keyguard going away animation. This is used
+     * for validation when user switching occurs during unlock.
+     */
+    private var keyguardGoingAwayRequestedForUserId: Int = -1
+
     /** Callback provided by WM to call once we're done with the going away animation. */
     private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
 
@@ -171,6 +188,14 @@
         nonApps: Array<RemoteAnimationTarget>,
         finishedCallback: IRemoteAnimationFinishedCallback,
     ) {
+        goingAwayRemoteAnimationFinishedCallback = finishedCallback
+
+        if (maybeStartTransitionIfUserSwitchedDuringGoingAway()) {
+            Log.d(TAG, "User switched during keyguard going away - ending remote animation.")
+            endKeyguardGoingAwayAnimation()
+            return
+        }
+
         // If we weren't expecting the keyguard to be going away, WM triggered this transition.
         if (!isKeyguardGoingAway) {
             // Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
@@ -198,7 +223,6 @@
         }
 
         if (apps.isNotEmpty()) {
-            goingAwayRemoteAnimationFinishedCallback = finishedCallback
             keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
         } else {
             // Nothing to do here if we have no apps, end the animation, which will cancel it and WM
@@ -211,6 +235,7 @@
         // If WM cancelled the animation, we need to end immediately even if we're still using the
         // animation.
         endKeyguardGoingAwayAnimation()
+        maybeStartTransitionIfUserSwitchedDuringGoingAway()
     }
 
     /**
@@ -301,6 +326,29 @@
         }
     }
 
+    /**
+     * If necessary, start a transition to show/hide keyguard in response to a user switch during
+     * keyguard going away.
+     *
+     * Returns [true] if a transition was started, or false if a transition was not necessary.
+     */
+    private fun maybeStartTransitionIfUserSwitchedDuringGoingAway(): Boolean {
+        val currentUser = selectedUserInteractor.getSelectedUserId()
+        if (currentUser != keyguardGoingAwayRequestedForUserId) {
+            if (lockPatternUtils.isSecure(currentUser)) {
+                keyguardShowWhileAwakeInteractor.onSwitchedToSecureUserWhileKeyguardGoingAway()
+            } else {
+                keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+                    reason = "User switch during keyguard going away, and new user is insecure"
+                )
+            }
+
+            return true
+        } else {
+            return false
+        }
+    }
+
     companion object {
         private val TAG = "WindowManagerLsVis"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
new file mode 100644
index 0000000..16c2d14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.data.repository
+
+import android.os.IRemoteCallback
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Holds an IRemoteCallback along with the current user ID at the time the callback was provided.
+ */
+data class ShowLockscreenCallback(val userId: Int, val remoteCallback: IRemoteCallback)
+
+/** Maintains state related to KeyguardService requests to show the lockscreen. */
+@SysUISingleton
+class KeyguardServiceShowLockscreenRepository @Inject constructor() {
+    val showLockscreenCallbacks = ArrayList<ShowLockscreenCallback>()
+
+    /**
+     * Adds a callback that we'll notify when we show the lockscreen (or affirmatively decide not to
+     * show it).
+     */
+    fun addShowLockscreenCallback(forUser: Int, callback: IRemoteCallback) {
+        synchronized(showLockscreenCallbacks) {
+            showLockscreenCallbacks.add(ShowLockscreenCallback(forUser, callback))
+        }
+    }
+
+    companion object {
+        private const val TAG = "ShowLockscreenRepository"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index ab0efed..02e04aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -226,6 +226,10 @@
         }
 
     fun handleFidgetTap(x: Float, y: Float) {
+        if (!com.android.systemui.Flags.clockFidgetAnimation()) {
+            return
+        }
+
         if (selectedClockSize.value == ClockSizeSetting.DYNAMIC) {
             clockEventController.handleFidgetTap(x, y)
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
index b55bb38..07a31e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
@@ -17,16 +17,27 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.annotation.SuppressLint
+import android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.RemoteException
+import android.util.Log
+import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.data.repository.KeyguardServiceShowLockscreenRepository
+import com.android.systemui.keyguard.data.repository.ShowLockscreenCallback
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.launch
 
 /**
- * Logic around requests by [KeyguardService] to show keyguard right now, even though the device is
- * awake and not going to sleep.
+ * Logic around requests by [KeyguardService] and friends to show keyguard right now, even though
+ * the device is awake and not going to sleep.
  *
  * This can happen if WM#lockNow() is called, if KeyguardService#showDismissibleKeyguard is called
  * because we're folding with "continue using apps on fold" set to "swipe up to continue", or if the
@@ -38,7 +49,28 @@
 @SysUISingleton
 class KeyguardServiceShowLockscreenInteractor
 @Inject
-constructor(@Background val backgroundScope: CoroutineScope) {
+constructor(
+    @Background val backgroundScope: CoroutineScope,
+    private val selectedUserInteractor: SelectedUserInteractor,
+    private val repository: KeyguardServiceShowLockscreenRepository,
+    private val userTracker: UserTracker,
+    private val wmLockscreenVisibilityInteractor: Lazy<WindowManagerLockscreenVisibilityInteractor>,
+    private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) : CoreStartable {
+
+    override fun start() {
+        backgroundScope.launch {
+            // Whenever we tell ATMS that lockscreen is visible, notify any showLockscreenCallbacks.
+            // This is not the only place we notify the lockNowCallbacks - there are cases where we
+            // decide not to show the lockscreen despite being asked to, and we need to notify the
+            // callback in those cases as well.
+            wmLockscreenVisibilityInteractor.get().lockscreenVisibility.collect { visible ->
+                if (visible) {
+                    notifyShowLockscreenCallbacks()
+                }
+            }
+        }
+    }
 
     /**
      * Emits whenever [KeyguardService] receives a call that indicates we should show the lockscreen
@@ -57,9 +89,38 @@
     /**
      * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
      * the device locked while the screen was on.
+     *
+     * We'll show keyguard, and if provided, save the lock on user switch callback, to notify it
+     * later when we successfully show.
      */
-    fun onKeyguardServiceDoKeyguardTimeout() {
+    fun onKeyguardServiceDoKeyguardTimeout(options: Bundle? = null) {
         backgroundScope.launch {
+            if (options?.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) {
+                val userId = userTracker.userId
+
+                // This callback needs to be invoked after we show the lockscreen (or decide not to
+                // show it) otherwise System UI will crash in 20 seconds, as a security measure.
+                repository.addShowLockscreenCallback(
+                    userId,
+                    IRemoteCallback.Stub.asInterface(
+                        options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK)
+                    ),
+                )
+
+                Log.d(
+                    TAG,
+                    "Showing lockscreen now - setting required callback for user $userId. " +
+                        "SysUI will crash if this callback is not invoked.",
+                )
+
+                // If the keyguard is disabled or suppressed, we'll never actually show the
+                // lockscreen. Notify the callback so we don't crash.
+                if (!keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()) {
+                    Log.d(TAG, "Keyguard is disabled or suppressed, notifying callbacks now.")
+                    notifyShowLockscreenCallbacks()
+                }
+            }
+
             showNowEvents.emit(ShowWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
         }
     }
@@ -74,4 +135,33 @@
             showNowEvents.emit(ShowWhileAwakeReason.FOLDED_WITH_SWIPE_UP_TO_CONTINUE)
         }
     }
+
+    /** Notifies the callbacks that we've either locked, or decided not to lock. */
+    private fun notifyShowLockscreenCallbacks() {
+        var callbacks: MutableList<ShowLockscreenCallback>
+        synchronized(repository.showLockscreenCallbacks) {
+            callbacks = ArrayList(repository.showLockscreenCallbacks)
+            repository.showLockscreenCallbacks.clear()
+        }
+
+        val iter: MutableIterator<ShowLockscreenCallback> = callbacks.listIterator()
+        while (iter.hasNext()) {
+            val callback = iter.next()
+            iter.remove()
+            if (callback.userId != selectedUserInteractor.getSelectedUserId()) {
+                Log.i(TAG, "Not notifying lockNowCallback due to user mismatch")
+                continue
+            }
+            Log.i(TAG, "Notifying lockNowCallback")
+            try {
+                callback.remoteCallback.sendResult(null)
+            } catch (e: RemoteException) {
+                Log.e(TAG, "Could not issue LockNowCallback sendResult", e)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "ShowLockscreenInteractor"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
index a8000a56..c67939a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
@@ -16,15 +16,20 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.annotation.SuppressLint
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
 
 /** The reason we're showing lockscreen while awake, used for logging. */
 enum class ShowWhileAwakeReason(private val logReason: String) {
@@ -38,6 +43,9 @@
     ),
     KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
         "Timed out while the screen was kept on, or WM#lockNow() was called."
+    ),
+    SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY(
+        "User switch to secure user occurred during keyguardGoingAway sequence, so we're locking."
     );
 
     override fun toString(): String {
@@ -68,6 +76,7 @@
 class KeyguardShowWhileAwakeInteractor
 @Inject
 constructor(
+    @Background val backgroundScope: CoroutineScope,
     biometricSettingsRepository: BiometricSettingsRepository,
     keyguardEnabledInteractor: KeyguardEnabledInteractor,
     keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
@@ -91,6 +100,15 @@
             .filter { reshow -> reshow }
             .map { ShowWhileAwakeReason.KEYGUARD_REENABLED }
 
+    /**
+     * Emits whenever a user switch to a secure user occurs during keyguard going away.
+     *
+     * This is an event flow, hence the SharedFlow.
+     */
+    @SuppressLint("SharedFlowCreation")
+    val switchedToSecureUserDuringGoingAway: MutableSharedFlow<ShowWhileAwakeReason> =
+        MutableSharedFlow()
+
     /** Emits whenever we should show lockscreen while the screen is on, for any reason. */
     val showWhileAwakeEvents: Flow<ShowWhileAwakeReason> =
         merge(
@@ -108,5 +126,15 @@
             keyguardServiceShowLockscreenInteractor.showNowEvents.filter {
                 keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()
             },
+            switchedToSecureUserDuringGoingAway,
         )
+
+    /** A user switch to a secure user occurred while we were going away. We need to re-lock. */
+    fun onSwitchedToSecureUserWhileKeyguardGoingAway() {
+        backgroundScope.launch {
+            switchedToSecureUserDuringGoingAway.emit(
+                ShowWhileAwakeReason.SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index 2b4582a..780b7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -29,6 +29,7 @@
     private val auditLogger: KeyguardTransitionAuditLogger,
     private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
     private val keyguardStateCallbackInteractor: KeyguardStateCallbackInteractor,
+    private val keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
 ) : CoreStartable {
 
     override fun start() {
@@ -54,6 +55,7 @@
         auditLogger.start()
         statusBarDisableFlagsInteractor.start()
         keyguardStateCallbackInteractor.start()
+        keyguardServiceShowLockscreenInteractor.start()
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index cedf661..5b65531 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,7 +127,6 @@
         }
 
         holder.seekBar.setMax(data.duration)
-        val totalTimeDescription = data.durationDescription
         if (data.scrubbing) {
             holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
         }
@@ -147,17 +146,9 @@
                 }
             }
 
-            val elapsedTimeDescription = data.elapsedTimeDescription
             if (data.scrubbing) {
                 holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
             }
-
-            holder.seekBar.contentDescription =
-                holder.seekBar.context.getString(
-                    R.string.controls_media_seekbar_description,
-                    elapsedTimeDescription,
-                    totalTimeDescription,
-                )
         }
     }
 
@@ -166,6 +157,18 @@
         return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
     }
 
+    fun updateContentDescription(
+        elapsedTimeDescription: CharSequence,
+        durationDescription: CharSequence,
+    ) {
+        holder.seekBar.contentDescription =
+            holder.seekBar.context.getString(
+                R.string.controls_media_seekbar_description,
+                elapsedTimeDescription,
+                durationDescription,
+            )
+    }
+
     @VisibleForTesting
     open fun buildResetAnimator(targetTime: Int): Animator {
         val animator =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 006eb20..f69985e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -224,6 +224,8 @@
             this::setIsScrubbing;
     private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
             this::setIsSeekBarEnabled;
+    private final SeekBarViewModel.ContentDescriptionListener mContentDescriptionListener =
+            this::setSeekbarContentDescription;
 
     private final BroadcastDialogController mBroadcastDialogController;
     private boolean mIsCurrentBroadcastedApp = false;
@@ -327,6 +329,7 @@
         }
         mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
         mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
+        mSeekBarViewModel.removeContentDescriptionListener(mContentDescriptionListener);
         mSeekBarViewModel.onDestroy();
         mMediaViewController.onDestroy();
     }
@@ -395,6 +398,10 @@
         });
     }
 
+    private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
+        mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+    }
+
     /**
      * Reloads animator duration scale.
      */
@@ -424,6 +431,7 @@
         mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
         mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
         mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
+        mSeekBarViewModel.setContentDescriptionListener(mContentDescriptionListener);
         mMediaViewController.attach(player);
 
         vh.getPlayer().setOnLongClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index dba1900..e87d5de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -229,6 +229,20 @@
             }
         }
 
+    private val seekbarDescriptionListener =
+        object : SeekBarViewModel.ContentDescriptionListener {
+            override fun onContentDescriptionChanged(
+                elapsedTimeDescription: CharSequence,
+                durationDescription: CharSequence,
+            ) {
+                if (!SceneContainerFlag.isEnabled) return
+                seekBarObserver.updateContentDescription(
+                    elapsedTimeDescription,
+                    durationDescription,
+                )
+            }
+        }
+
     /**
      * Sets the listening state of the player.
      *
@@ -350,6 +364,7 @@
             }
             seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
             seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+            seekBarViewModel.removeContentDescriptionListener(seekbarDescriptionListener)
             seekBarViewModel.onDestroy()
         }
         mediaHostStatesManager.removeController(this)
@@ -653,6 +668,7 @@
         seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
         seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
         seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+        seekBarViewModel.setContentDescriptionListener(seekbarDescriptionListener)
 
         val mediaCard = mediaViewHolder.player
         attach(mediaViewHolder.player)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 8744c5c..78a8cf8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -104,19 +104,20 @@
         )
         set(value) {
             val enabledChanged = value.enabled != field.enabled
+            field = value
             if (enabledChanged) {
                 enabledChangeListener?.onEnabledChanged(value.enabled)
             }
+            _progress.postValue(value)
+
             bgExecutor.execute {
                 val durationDescription = formatTimeContentDescription(value.duration)
                 val elapsedDescription =
                     value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
-                field =
-                    value.copy(
-                        durationDescription = durationDescription,
-                        elapsedTimeDescription = elapsedDescription,
-                    )
-                _progress.postValue(field)
+                contentDescriptionListener?.onContentDescriptionChanged(
+                    elapsedDescription,
+                    durationDescription,
+                )
             }
         }
 
@@ -175,6 +176,7 @@
 
     private var scrubbingChangeListener: ScrubbingChangeListener? = null
     private var enabledChangeListener: EnabledChangeListener? = null
+    private var contentDescriptionListener: ContentDescriptionListener? = null
 
     /** Set to true when the user is touching the seek bar to change the position. */
     private var scrubbing = false
@@ -394,6 +396,16 @@
         }
     }
 
+    fun setContentDescriptionListener(listener: ContentDescriptionListener) {
+        contentDescriptionListener = listener
+    }
+
+    fun removeContentDescriptionListener(listener: ContentDescriptionListener) {
+        if (listener == contentDescriptionListener) {
+            contentDescriptionListener = null
+        }
+    }
+
     /** returns a pair of whether seekbar is enabled and the duration of media. */
     private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
         val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
@@ -468,6 +480,13 @@
         fun onEnabledChanged(enabled: Boolean)
     }
 
+    interface ContentDescriptionListener {
+        fun onContentDescriptionChanged(
+            elapsedTimeDescription: CharSequence,
+            durationDescription: CharSequence,
+        )
+    }
+
     private class SeekBarChangeListener(
         val viewModel: SeekBarViewModel,
         val falsingManager: FalsingManager,
@@ -639,7 +658,5 @@
         val duration: Int,
         /** whether seekBar is listening to progress updates */
         val listening: Boolean,
-        val elapsedTimeDescription: CharSequence = "",
-        val durationDescription: CharSequence = "",
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index 7543e0f..f072388 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -46,7 +46,6 @@
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -55,7 +54,6 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ButtonDefaults
@@ -75,6 +73,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -101,6 +100,7 @@
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
@@ -110,6 +110,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastRoundToInt
 import com.android.compose.PlatformButton
 import com.android.compose.PlatformIconButton
 import com.android.compose.PlatformOutlinedButton
@@ -138,6 +139,7 @@
 import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel
 import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel
 import kotlin.math.max
+import kotlin.math.min
 
 /**
  * Renders a media controls UI element.
@@ -406,7 +408,7 @@
             )
     ) {
         // Always add the first/top row, regardless of presentation style.
-        BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
+        Box(modifier = Modifier.fillMaxWidth()) {
             // Icon.
             Icon(
                 icon = viewModel.icon,
@@ -418,9 +420,26 @@
                         .clip(CircleShape),
             )
 
+            var cardMaxWidth: Int by remember { mutableIntStateOf(0) }
             Row(
                 horizontalArrangement = Arrangement.spacedBy(8.dp),
-                modifier = Modifier.align(Alignment.TopEnd),
+                modifier =
+                    Modifier.align(Alignment.TopEnd)
+                        // Output switcher chips must each be limited to at most 40% of the maximum
+                        // width of the card.
+                        //
+                        // This saves the maximum possible width of the card so it can be referred
+                        // to by child custom layout code below.
+                        //
+                        // The assumption is that the row can be as wide as the entire card.
+                        .layout { measurable, constraints ->
+                            cardMaxWidth = constraints.maxWidth
+                            val placeable = measurable.measure(constraints)
+
+                            layout(placeable.measuredWidth, placeable.measuredHeight) {
+                                placeable.place(0, 0)
+                            }
+                        },
             ) {
                 viewModel.outputSwitcherChips.fastForEach { chip ->
                     OutputSwitcherChip(
@@ -433,9 +452,23 @@
                                 //
                                 // The underlying assumption is that there'll never be more than one
                                 // chip with text and one more icon-only chip. Only the one with
-                                // text
-                                // can ever end up being too wide.
-                                .widthIn(max = this@BoxWithConstraints.maxWidth * 0.4f),
+                                // text can ever end up being too wide.
+                                .layout { measurable, constraints ->
+                                    val placeable =
+                                        measurable.measure(
+                                            constraints.copy(
+                                                maxWidth =
+                                                    min(
+                                                        (cardMaxWidth * 0.4f).fastRoundToInt(),
+                                                        constraints.maxWidth,
+                                                    )
+                                            )
+                                        )
+
+                                    layout(placeable.measuredWidth, placeable.measuredHeight) {
+                                        placeable.place(0, 0)
+                                    }
+                                },
                     )
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
index 71cb745..68cd807 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
@@ -17,9 +17,9 @@
 
 import android.util.Log
 import android.view.Display
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.display.data.repository.PerDisplayInstanceProviderWithTeardown
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.model.SysUiState.SysUiStateCallback
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 49fa3ba..88f679e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -72,4 +72,8 @@
     /** @return {@link NavigationBar} on the default display. */
     @Nullable
     NavigationBar getDefaultNavigationBar();
+
+    /** @return {@link NavigationBar} for a specific display, or null if not available. */
+    @Nullable
+    NavigationBar getNavigationBar(int displayId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
index 45ff7f4..f096510 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -54,4 +54,6 @@
     override fun isOverviewEnabled(displayId: Int) = false
 
     override fun getDefaultNavigationBar(): NavigationBar? = null
+
+    override fun getNavigationBar(displayId: Int): NavigationBar? = null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 50d0a45..8fbf8b60 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -469,7 +469,8 @@
         return (navBar == null) ? null : navBar.getView();
     }
 
-    private @Nullable NavigationBar getNavigationBar(int displayId) {
+    @Override
+    public @Nullable NavigationBar getNavigationBar(int displayId) {
         return mNavigationBars.get(displayId);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 914e0f7..58ddbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -27,10 +27,13 @@
 import com.android.app.viewcapture.ViewCapture;
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
 import com.android.systemui.navigationbar.views.NavigationBarFrame;
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 
 import dagger.Lazy;
 import dagger.Module;
@@ -71,6 +74,20 @@
         return context.getSystemService(WindowManager.class);
     }
 
+    /** A SysUiState for the navigation bar display. */
+    @Provides
+    @NavigationBarScope
+    @DisplayId
+    static SysUiState provideSysUiState(@DisplayId Context context,
+            SysUiState defaultState,
+            PerDisplayRepository<SysUiState> repository) {
+        if (ShadeWindowGoesAround.isEnabled()) {
+            return repository.get(context.getDisplayId());
+        } else {
+            return defaultState;
+        }
+    }
+
     /** A ViewCaptureAwareWindowManager specific to the display's context. */
     @Provides
     @NavigationBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index f95f459..8b5b3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -569,7 +569,7 @@
             NavigationModeController navigationModeController,
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            SysUiState sysUiFlagsContainer,
+            @DisplayId SysUiState sysUiFlagsContainer,
             UserTracker userTracker,
             CommandQueue commandQueue,
             Optional<Pip> pipOptional,
@@ -1694,7 +1694,7 @@
                         (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0)
                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
                         allowSystemGestureIgnoringBarVisibility())
-                .commitUpdate(mDisplayId);
+                .commitUpdate();
     }
 
     private void updateAssistantEntrypoints(boolean assistantAvailable,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index 36cb8fa..cbc4c26 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -740,15 +740,13 @@
 
     /** */
     public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
-        int displayId = mContext.getDisplayId();
-
         sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
                 .setFlag(SYSUI_STATE_HOME_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
                 .setFlag(SYSUI_STATE_SEARCH_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0)
-                .commitUpdate(displayId);
+                .commitUpdate();
     }
 
     public void setInScreenPinning(boolean active) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index a4386de..05a60a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -18,7 +18,6 @@
 
 import android.annotation.SuppressLint
 import android.content.Context
-import android.content.res.Configuration
 import android.graphics.PointF
 import android.graphics.Rect
 import android.os.Bundle
@@ -49,7 +48,6 @@
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
@@ -72,8 +70,6 @@
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.layout.positionOnScreen
 import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -255,7 +251,7 @@
 
     @Composable
     private fun Content() {
-        PlatformTheme(isDarkTheme = true /* Delete AlwaysDarkMode when removing this */) {
+        PlatformTheme {
             ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
                 // TODO(b/389985793): Make sure that there is no coroutine work or recompositions
                 // happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false.
@@ -747,25 +743,22 @@
                         )
                         val BrightnessSlider =
                             @Composable {
-                                AlwaysDarkMode {
-                                    Box(
-                                        Modifier.systemGestureExclusionInShade(
-                                            enabled = {
-                                                layoutState.transitionState is TransitionState.Idle
-                                            }
-                                        )
-                                    ) {
-                                        BrightnessSliderContainer(
-                                            viewModel =
-                                                containerViewModel.brightnessSliderViewModel,
-                                            containerColors =
-                                                ContainerColors(
-                                                    Color.Transparent,
-                                                    ContainerColors.defaultContainerColor,
-                                                ),
-                                            modifier = Modifier.fillMaxWidth(),
-                                        )
-                                    }
+                                Box(
+                                    Modifier.systemGestureExclusionInShade(
+                                        enabled = {
+                                            layoutState.transitionState is TransitionState.Idle
+                                        }
+                                    )
+                                ) {
+                                    BrightnessSliderContainer(
+                                        viewModel = containerViewModel.brightnessSliderViewModel,
+                                        containerColors =
+                                            ContainerColors(
+                                                Color.Transparent,
+                                                ContainerColors.defaultContainerColor,
+                                            ),
+                                        modifier = Modifier.fillMaxWidth(),
+                                    )
                                 }
                             }
                         val TileGrid =
@@ -1243,28 +1236,3 @@
 
 private inline val alwaysCompose
     get() = Flags.alwaysComposeQsUiFragment()
-
-/**
- * Forces the configuration and themes to be dark theme. This is needed in order to have
- * [colorResource] retrieve the dark mode colors.
- *
- * This should be removed when we remove the force dark mode in [PlatformTheme] at the root of the
- * compose hierarchy.
- */
-@Composable
-private fun AlwaysDarkMode(content: @Composable () -> Unit) {
-    val currentConfig = LocalConfiguration.current
-    val darkConfig =
-        Configuration(currentConfig).apply {
-            uiMode =
-                (uiMode and (Configuration.UI_MODE_NIGHT_MASK.inv())) or
-                    Configuration.UI_MODE_NIGHT_YES
-        }
-    val newContext = LocalContext.current.createConfigurationContext(darkConfig)
-    CompositionLocalProvider(
-        LocalConfiguration provides darkConfig,
-        LocalContext provides newContext,
-    ) {
-        content()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 22971a9..a7ebb22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -88,7 +88,11 @@
         LaunchedEffect(listening, pagerState) {
             snapshotFlow { listening() }
                 .collect {
-                    if (!listening()) {
+                    // Whenever we go from not listening to listening, we should be in the first
+                    // page. If we did this when going from listening to not listening, opening
+                    // edit mode in second page will cause it to go to first page during the
+                    // transition.
+                    if (listening()) {
                         pagerState.scrollToPage(0)
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 69b967a..ccbd8fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -63,6 +63,7 @@
 import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
@@ -89,6 +90,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.graphics.Color
@@ -113,7 +115,9 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastMap
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
 import com.android.compose.modifiers.height
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -131,6 +135,7 @@
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.GridBackgroundCornerRadius
 import com.android.systemui.qs.panels.ui.compose.selection.InteractiveTileContainer
 import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
 import com.android.systemui.qs.panels.ui.compose.selection.ResizingState
@@ -163,14 +168,27 @@
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
-
+    val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
     TopAppBar(
-        colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
-        title = { Text(text = stringResource(id = R.string.qs_edit)) },
+        colors =
+            TopAppBarDefaults.topAppBarColors(
+                containerColor = Color.Transparent,
+                titleContentColor = MaterialTheme.colorScheme.onSurface,
+            ),
+        title = {
+            Text(
+                text = stringResource(id = R.string.qs_edit),
+                modifier = Modifier.padding(start = 24.dp),
+            )
+        },
         navigationIcon = {
-            IconButton(onClick = onStopEditing) {
+            IconButton(
+                onClick = onStopEditing,
+                modifier = Modifier.drawBehind { drawCircle(primaryContainerColor) },
+            ) {
                 Icon(
                     Icons.AutoMirrored.Filled.ArrowBack,
+                    tint = MaterialTheme.colorScheme.onSurface,
                     contentDescription =
                         stringResource(id = com.android.internal.R.string.action_bar_up_description),
                 )
@@ -178,11 +196,19 @@
         },
         actions = {
             if (onReset != null) {
-                TextButton(onClick = onReset) {
+                TextButton(
+                    onClick = onReset,
+                    colors =
+                        ButtonDefaults.textButtonColors(
+                            containerColor = MaterialTheme.colorScheme.primary,
+                            contentColor = MaterialTheme.colorScheme.onPrimary,
+                        ),
+                ) {
                     Text(stringResource(id = com.android.internal.R.string.reset))
                 }
             }
         },
+        modifier = Modifier.padding(vertical = 8.dp),
     )
 }
 
@@ -215,7 +241,9 @@
         containerColor = Color.Transparent,
         topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
     ) { innerPadding ->
-        CompositionLocalProvider(LocalOverscrollFactory provides null) {
+        CompositionLocalProvider(
+            LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+        ) {
             val scrollState = rememberScrollState()
 
             AutoScrollGrid(listState, scrollState, innerPadding)
@@ -244,7 +272,7 @@
                     targetState = listState.dragInProgress || selectionState.selected,
                     label = "QSEditHeader",
                     contentAlignment = Alignment.Center,
-                    modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp),
+                    modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
                 ) { showRemoveTarget ->
                     EditGridHeader {
                         if (showRemoveTarget) {
@@ -289,10 +317,6 @@
                                 spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
                             modifier = modifier.fillMaxSize(),
                         ) {
-                            EditGridHeader {
-                                Text(text = stringResource(id = R.string.drag_to_add_tiles))
-                            }
-
                             val availableTiles = remember {
                                 mutableStateListOf<AvailableTileGridCell>().apply {
                                     addAll(toAvailableTiles(listState.tiles, otherTiles))
@@ -371,9 +395,7 @@
     modifier: Modifier = Modifier,
     content: @Composable BoxScope.() -> Unit,
 ) {
-    CompositionLocalProvider(
-        LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
-    ) {
+    CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
         Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) { content() }
     }
 }
@@ -420,6 +442,7 @@
             listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
         }
 
+    val primaryColor = MaterialTheme.colorScheme.primary
     TileLazyGrid(
         state = gridState,
         columns = GridCells.Fixed(columns),
@@ -428,9 +451,9 @@
             Modifier.fillMaxWidth()
                 .height { totalHeight.roundToPx() }
                 .border(
-                    width = 1.dp,
-                    color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
-                    shape = RoundedCornerShape((TileHeight / 2) + CurrentTilesGridPadding),
+                    width = 2.dp,
+                    color = primaryColor,
+                    shape = RoundedCornerShape(GridBackgroundCornerRadius),
                 )
                 .dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
                     onSetTiles(currentListState.tileSpecs())
@@ -439,6 +462,13 @@
                 .onGloballyPositioned { coordinates ->
                     gridContentOffset = coordinates.positionInRoot()
                 }
+                .drawBehind {
+                    drawRoundRect(
+                        primaryColor,
+                        cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+                        alpha = .15f,
+                    )
+                }
                 .testTag(CURRENT_TILES_GRID_TEST_TAG),
     ) {
         EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) {
@@ -469,7 +499,6 @@
         remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
             groupAndSort(tiles)
         }
-    val labelColors = EditModeTileDefaults.editTileColors()
 
     // Available tiles
     Column(
@@ -480,32 +509,45 @@
     ) {
         groupedTiles.forEach { (category, tiles) ->
             key(category) {
-                Text(
-                    text = category.label.load() ?: "",
-                    fontSize = 20.sp,
-                    color = labelColors.label,
+                val surfaceColor = MaterialTheme.colorScheme.surface
+                Column(
+                    verticalArrangement = spacedBy(16.dp),
                     modifier =
-                        Modifier.fillMaxWidth().padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
-                )
-                tiles.chunked(columns).forEach { row ->
-                    Row(
-                        horizontalArrangement = spacedBy(TileArrangementPadding),
-                        modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
-                    ) {
-                        row.forEach { tileGridCell ->
-                            key(tileGridCell.key) {
-                                AvailableTileGridCell(
-                                    cell = tileGridCell,
-                                    dragAndDropState = dragAndDropState,
-                                    selectionState = selectionState,
-                                    onAddTile = onAddTile,
-                                    modifier = Modifier.weight(1f).fillMaxHeight(),
+                        Modifier.drawBehind {
+                                drawRoundRect(
+                                    surfaceColor,
+                                    cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+                                    alpha = .32f,
                                 )
                             }
-                        }
+                            .padding(16.dp),
+                ) {
+                    Text(
+                        text = category.label.load() ?: "",
+                        fontSize = 20.sp,
+                        color = MaterialTheme.colorScheme.onSurface,
+                        modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
+                    )
+                    tiles.chunked(columns).forEach { row ->
+                        Row(
+                            horizontalArrangement = spacedBy(TileArrangementPadding),
+                            modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
+                        ) {
+                            row.forEach { tileGridCell ->
+                                key(tileGridCell.key) {
+                                    AvailableTileGridCell(
+                                        cell = tileGridCell,
+                                        dragAndDropState = dragAndDropState,
+                                        selectionState = selectionState,
+                                        onAddTile = onAddTile,
+                                        modifier = Modifier.weight(1f).fillMaxHeight(),
+                                    )
+                                }
+                            }
 
-                        // Spacers for incomplete rows
-                        repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+                            // Spacers for incomplete rows
+                            repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+                        }
                     }
                 }
             }
@@ -761,7 +803,7 @@
                 color = colors.label,
                 overflow = TextOverflow.Ellipsis,
                 textAlign = TextAlign.Center,
-                modifier = Modifier.align(Alignment.Center),
+                modifier = Modifier.align(Alignment.TopCenter),
             )
         }
     }
@@ -861,15 +903,16 @@
     const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel
     val CurrentTilesGridPadding = 10.dp
     val AvailableTilesGridMinHeight = 200.dp
+    val GridBackgroundCornerRadius = 42.dp
 
     @Composable
     fun editTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
-            iconBackground = MaterialTheme.colorScheme.surfaceVariant,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
-            icon = MaterialTheme.colorScheme.onSurfaceVariant,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = Color.Transparent,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
         )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index bf63c3858..6bafd43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -25,6 +25,7 @@
 import android.service.quicksettings.Tile.STATE_INACTIVE
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
@@ -59,6 +60,7 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalResources
 import androidx.compose.ui.semantics.Role
@@ -74,7 +76,9 @@
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.bounceable
+import com.android.compose.animation.rememberExpandableController
 import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.Flags
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
@@ -165,6 +169,7 @@
         // TODO(b/361789146): Draw the shapes instead of clipping
         val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
         val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
+        val animatedAlpha by animateFloatAsState(colors.alpha, label = "QSTileAlpha")
 
         TileExpandable(
             color = { animatedColor },
@@ -181,7 +186,8 @@
                         nextBounceable = currentBounceableInfo.nextTile,
                         orientation = Orientation.Horizontal,
                         bounceEnd = currentBounceableInfo.bounceEnd,
-                    ),
+                    )
+                    .graphicsLayer { alpha = animatedAlpha },
         ) { expandable ->
             val longClick: (() -> Unit)? =
                 {
@@ -260,8 +266,7 @@
     content: @Composable (Expandable) -> Unit,
 ) {
     Expandable(
-        color = color(),
-        shape = shape,
+        controller = rememberExpandableController(color = color, shape = shape),
         modifier = modifier.clip(shape).verticalSquish(squishiness),
         useModifierBasedImplementation = true,
     ) {
@@ -370,6 +375,7 @@
     val label: Color,
     val secondaryLabel: Color,
     val icon: Color,
+    val alpha: Float = 1f,
 )
 
 private object TileDefaults {
@@ -393,10 +399,10 @@
     @ReadOnlyComposable
     fun activeDualTargetTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
             iconBackground = MaterialTheme.colorScheme.primary,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
             icon = MaterialTheme.colorScheme.onPrimary,
         )
 
@@ -404,30 +410,19 @@
     @ReadOnlyComposable
     fun inactiveDualTargetTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
-            iconBackground = MaterialTheme.colorScheme.surfaceContainerHighest,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
-            icon = MaterialTheme.colorScheme.onSurfaceVariant,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = LocalAndroidColorScheme.current.surfaceEffect3,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
         )
 
     @Composable
     @ReadOnlyComposable
     fun inactiveTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
-            iconBackground = MaterialTheme.colorScheme.surfaceVariant,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
-            icon = MaterialTheme.colorScheme.onSurfaceVariant,
-        )
-
-    @Composable
-    @ReadOnlyComposable
-    fun unavailableTileColors(): TileColors =
-        TileColors(
-            background = MaterialTheme.colorScheme.surface,
-            iconBackground = MaterialTheme.colorScheme.surface,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = Color.Transparent,
             label = MaterialTheme.colorScheme.onSurface,
             secondaryLabel = MaterialTheme.colorScheme.onSurface,
             icon = MaterialTheme.colorScheme.onSurface,
@@ -435,6 +430,19 @@
 
     @Composable
     @ReadOnlyComposable
+    fun unavailableTileColors(): TileColors {
+        return TileColors(
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = LocalAndroidColorScheme.current.surfaceEffect2,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
+            alpha = .38f,
+        )
+    }
+
+    @Composable
+    @ReadOnlyComposable
     fun getColorForState(uiState: TileUiState, iconOnly: Boolean): TileColors {
         return when (uiState.state) {
             STATE_ACTIVE -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index a66b51f..57f63c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -62,7 +62,6 @@
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.toSize
 import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.size
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
 import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BADGE_ANGLE_RAD
@@ -155,6 +154,7 @@
                 Icon(
                     Icons.Default.Remove,
                     contentDescription = null,
+                    tint = MaterialTheme.colorScheme.onPrimaryContainer,
                     modifier =
                         Modifier.size(size).align(Alignment.Center).graphicsLayer {
                             this.alpha = badgeIconAlpha
@@ -218,14 +218,15 @@
                     )
                 }
         ) {
-            val secondaryColor = MaterialTheme.colorScheme.secondary
             val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
+            val primaryColor = MaterialTheme.colorScheme.primary
             Icon(
                 icon,
                 contentDescription = contentDescription,
+                tint = MaterialTheme.colorScheme.onPrimary,
                 modifier =
                     Modifier.size(size).align(Alignment.Center).drawBehind {
-                        drawCircle(secondaryColor, radius = BadgeSize.toPx() / 2)
+                        drawCircle(primaryColor, radius = BadgeSize.toPx() / 2)
                     },
             )
         }
@@ -291,7 +292,7 @@
     return animateColor { state ->
         when (state) {
             None -> Color.Transparent
-            Removable -> MaterialTheme.colorScheme.secondary
+            Removable -> MaterialTheme.colorScheme.primaryContainer
             Selected -> MaterialTheme.colorScheme.primary
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
index bc15bbb..263ef09e 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -20,6 +20,8 @@
 import android.hardware.devicestate.DeviceStateManager
 import android.hardware.devicestate.feature.flags.Flags
 import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -28,8 +30,11 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
 
 /**
  * Provides a {@link com.android.systemui.statusbar.phone.SystemUIDialog} to be shown on the inner
@@ -46,6 +51,7 @@
     private val rearDisplayStateInteractor: RearDisplayStateInteractor,
     private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
     @Application private val scope: CoroutineScope,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) : CoreStartable, AutoCloseable {
 
     companion object {
@@ -53,6 +59,16 @@
     }
 
     @VisibleForTesting var stateChangeListener: Job? = null
+    private val keyguardVisible = MutableStateFlow(false)
+    private val keyguardVisibleFlow = keyguardVisible.asStateFlow()
+
+    @VisibleForTesting
+    val keyguardCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onKeyguardVisibilityChanged(visible: Boolean) {
+                keyguardVisible.value = visible
+            }
+        }
 
     override fun close() {
         stateChangeListener?.cancel()
@@ -62,28 +78,39 @@
         if (Flags.deviceStateRdmV2()) {
             var dialog: SystemUIDialog? = null
 
-            stateChangeListener =
-                rearDisplayStateInteractor.state
-                    .map {
-                        when (it) {
-                            is RearDisplayStateInteractor.State.Enabled -> {
-                                val rearDisplayContext =
-                                    context.createDisplayContext(it.innerDisplay)
-                                val delegate =
-                                    rearDisplayInnerDialogDelegateFactory.create(
-                                        rearDisplayContext,
-                                        deviceStateManager::cancelStateRequest,
-                                    )
-                                dialog = delegate.createDialog().apply { show() }
-                            }
+            keyguardUpdateMonitor.registerCallback(keyguardCallback)
 
-                            is RearDisplayStateInteractor.State.Disabled -> {
-                                dialog?.dismiss()
-                                dialog = null
+            stateChangeListener =
+                scope.launch {
+                    combine(rearDisplayStateInteractor.state, keyguardVisibleFlow) {
+                            rearDisplayState,
+                            keyguardVisible ->
+                            Pair(rearDisplayState, keyguardVisible)
+                        }
+                        .collectLatest { (rearDisplayState, keyguardVisible) ->
+                            when (rearDisplayState) {
+                                is RearDisplayStateInteractor.State.Enabled -> {
+                                    if (!keyguardVisible) {
+                                        val rearDisplayContext =
+                                            context.createDisplayContext(
+                                                rearDisplayState.innerDisplay
+                                            )
+                                        val delegate =
+                                            rearDisplayInnerDialogDelegateFactory.create(
+                                                rearDisplayContext,
+                                                deviceStateManager::cancelStateRequest,
+                                            )
+                                        dialog = delegate.createDialog().apply { show() }
+                                    }
+                                }
+
+                                is RearDisplayStateInteractor.State.Disabled -> {
+                                    dialog?.dismiss()
+                                    dialog = null
+                                }
                             }
                         }
-                    }
-                    .launchIn(scope)
+                }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 4be35f1..d263965 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -89,6 +89,8 @@
 import com.android.systemui.contextualeducation.GestureType;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.display.data.repository.DisplayRepository;
+import com.android.systemui.display.data.repository.PerDisplayRepository;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -109,6 +111,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.shared.recents.ILauncherProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -156,7 +159,9 @@
     private final Executor mMainExecutor;
     private final ShellInterface mShellInterface;
     private final Lazy<ShadeViewController> mShadeViewControllerLazy;
-    private SysUiState mSysUiState;
+    private final PerDisplayRepository<SysUiState> mPerDisplaySysUiStateRepository;
+    private final DisplayRepository mDisplayRepository;
+    private SysUiState mDefaultDisplaySysUIState;
     private final Handler mHandler;
     private final Lazy<NavigationBarController> mNavBarControllerLazy;
     private final ScreenPinningRequest mScreenPinningRequest;
@@ -586,9 +591,12 @@
 
             // Force-update the systemui state flags
             updateSystemUiStateFlags();
-            // TODO b/398011576 - send the state for all displays.
-            notifySystemUiStateFlags(mSysUiState.getFlags(), Display.DEFAULT_DISPLAY);
-
+            if (ShadeWindowGoesAround.isEnabled()) {
+               notifySysUiStateFlagsForAllDisplays();
+            } else {
+                notifySystemUiStateFlags(mDefaultDisplaySysUIState.getFlags(),
+                        Display.DEFAULT_DISPLAY);
+            }
             notifyConnectionChanged();
         }
 
@@ -614,6 +622,18 @@
         }
     };
 
+    /** Propagates the flags for all displays to be notified to Launcher. */
+    @VisibleForTesting
+    public void notifySysUiStateFlagsForAllDisplays() {
+        var displays = mDisplayRepository.getDisplayIds().getValue();
+        for (int displayId : displays) {
+            var state = mPerDisplaySysUiStateRepository.get(displayId);
+            if (state != null) {
+                notifySystemUiStateFlags(state.getFlags(), displayId);
+            }
+        }
+    }
+
     private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
 
     // This is the death handler for the binder from the launcher service
@@ -671,7 +691,7 @@
             ScreenPinningRequest screenPinningRequest,
             NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController,
-            SysUiState sysUiState,
+            PerDisplayRepository<SysUiState> perDisplaySysUiStateRepository,
             Provider<SceneInteractor> sceneInteractor,
             Provider<ShadeInteractor> shadeInteractor,
             UserTracker userTracker,
@@ -686,7 +706,8 @@
             Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
             BroadcastDispatcher broadcastDispatcher,
             Optional<BackAnimation> backAnimation,
-            ProcessWrapper processWrapper
+            ProcessWrapper processWrapper,
+            DisplayRepository displayRepository
     ) {
         // b/241601880: This component should only be running for primary users or
         // secondaryUsers when visibleBackgroundUsers are supported.
@@ -718,10 +739,10 @@
                 com.android.internal.R.string.config_recentsComponentName));
         mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
                 .setPackage(mRecentsComponentName.getPackageName());
-        // TODO b/398011576 - Here we're still only handling the default display state. We should
-        //  have a callback for any sysuiState change.
-        mSysUiState = sysUiState;
-        mSysUiState.addCallback(mSysUiStateCallback);
+        mPerDisplaySysUiStateRepository = perDisplaySysUiStateRepository;
+        mDisplayRepository = displayRepository;
+        mDefaultDisplaySysUIState = perDisplaySysUiStateRepository.get(Display.DEFAULT_DISPLAY);
+        mDefaultDisplaySysUIState.addCallback(mSysUiStateCallback);
         mUiEventLogger = uiEventLogger;
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
@@ -770,7 +791,7 @@
                 if (mLauncherProxy != null) {
                     try {
                         if (DesktopModeStatus.canEnterDesktopMode(mContext)
-                                && (sysUiState.getFlags()
+                                && (mDefaultDisplaySysUIState.getFlags()
                                 & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0) {
                             return;
                         }
@@ -795,7 +816,7 @@
     }
 
     public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
-        mSysUiState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
+        mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
                 .commitUpdate(mContext.getDisplayId());
     }
 
@@ -804,23 +825,42 @@
         startConnectionToCurrentUser();
     }
 
-    private void updateSystemUiStateFlags() {
+    private void updateSysUIStateForNavbars() {
+        if (ShadeWindowGoesAround.isEnabled()) {
+            var displays = mDisplayRepository.getDisplayIds().getValue();
+            for (int displayId : displays) {
+                updateSysUIStateForNavbarWithDisplayId(displayId);
+            }
+        } else {
+            updateSysUIStateForNavbarWithDisplayId(Display.DEFAULT_DISPLAY);
+        }
+    }
+
+    private void updateSysUIStateForNavbarWithDisplayId(int displayId) {
         final NavigationBar navBarFragment =
-                mNavBarControllerLazy.get().getDefaultNavigationBar();
+                mNavBarControllerLazy.get().getNavigationBar(displayId);
         final NavigationBarView navBarView =
-                mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
+                mNavBarControllerLazy.get().getNavigationBarView(displayId);
         if (SysUiState.DEBUG) {
             Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
                     + " navBarView=" + navBarView
                     + " shadeViewController=" + mShadeViewControllerLazy.get());
         }
 
+        final SysUiState displaySysuiState = mPerDisplaySysUiStateRepository.get(displayId);
+        if (displaySysuiState == null) return;
+
         if (navBarFragment != null) {
             navBarFragment.updateSystemUiStateFlags();
         }
         if (navBarView != null) {
-            navBarView.updateDisabledSystemUiStateFlags(mSysUiState);
+            navBarView.updateDisabledSystemUiStateFlags(displaySysuiState);
         }
+    }
+
+    /** Force updates SystemUI state flags prior to sending them to Launcher. */
+    public void updateSystemUiStateFlags() {
+        updateSysUIStateForNavbars();
         mShadeViewControllerLazy.get().updateSystemUiStateFlags();
         if (mStatusBarWinController != null) {
             mStatusBarWinController.notifyStateChangedCallbacks();
@@ -845,7 +885,7 @@
     private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
             boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing,
             boolean panelExpanded, boolean isDreaming, boolean communalShowing) {
-        mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+        mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
                         keyguardShowing && !keyguardOccluded)
                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
                         keyguardShowing && keyguardOccluded)
@@ -1122,7 +1162,7 @@
             new WakefulnessLifecycle.Observer() {
                 @Override
                 public void onStartedWakingUp() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, true)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
                             .commitUpdate(mContext.getDisplayId());
@@ -1130,7 +1170,7 @@
 
                 @Override
                 public void onFinishedWakingUp() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, true)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
                             .commitUpdate(mContext.getDisplayId());
@@ -1138,7 +1178,7 @@
 
                 @Override
                 public void onStartedGoingToSleep() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, false)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
                             .commitUpdate(mContext.getDisplayId());
@@ -1146,7 +1186,7 @@
 
                 @Override
                 public void onFinishedGoingToSleep() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, false)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
                             .commitUpdate(mContext.getDisplayId());
@@ -1247,7 +1287,7 @@
         pw.print("  mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
         pw.print("  mNavBarMode="); pw.println(mNavBarMode);
         pw.print("  mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
-        mSysUiState.dump(pw, args);
+        mDefaultDisplaySysUIState.dump(pw, args);
     }
 
     public interface LauncherProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f45971b..2bacee1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -86,7 +86,7 @@
      */
     fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
         if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
-            !supportsBlursOnWindows() || earlyWakeupEnabled
+            !shouldBlur(radius) || earlyWakeupEnabled
         ) {
             return
         }
@@ -113,7 +113,7 @@
             return
         }
         createTransaction().use {
-            if (supportsBlursOnWindows()) {
+            if (shouldBlur(radius)) {
                 it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
                 if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
                     Trace.asyncTraceForTrackBegin(
@@ -142,6 +142,14 @@
         return SurfaceControl.Transaction()
     }
 
+    private fun shouldBlur(radius: Int): Boolean {
+        return supportsBlursOnWindows() ||
+                ((Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) &&
+                        supportsBlursOnWindowsBase() &&
+                        lastAppliedBlur > 0 &&
+                        radius == 0)
+    }
+
     /**
      * If this device can render blurs.
      *
@@ -149,8 +157,11 @@
      * @return {@code true} when supported.
      */
     open fun supportsBlursOnWindows(): Boolean {
+        return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled
+    }
+
+    private fun supportsBlursOnWindowsBase(): Boolean {
         return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
-                crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
                 !SystemProperties.getBoolean("persist.sysui.disableBlur", false)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 05ef164..d2f424a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -394,7 +394,7 @@
                 // Only drag down on sensitive views, otherwise the ExpandHelper will take this
                 return if (NotificationBundleUi.isEnabled)
                     view.entryAdapter?.isSensitive?.value == true
-                else view.entry.isSensitive.value
+                else view.entryLegacy.isSensitive.value
             }
         }
         return false
@@ -569,7 +569,7 @@
             if (NotificationBundleUi.isEnabled) {
                 userId = expandView.entryAdapter?.sbn?.userId!!
             } else {
-                userId = expandView.entry.sbn.userId
+                userId = expandView.entryLegacy.sbn.userId
             }
         }
         var fullShadeNeedsBouncer =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 339f898..9bf3d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -47,7 +47,6 @@
 import android.net.Uri;
 import android.os.Looper;
 import android.os.Process;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -78,6 +77,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -920,7 +920,9 @@
     // notification's "when" time, or the notification entry creation time
     private long getEarliestNotificationTime(NotificationEntry notif) {
         long notifWhenWallClock = notif.getSbn().getNotification().getWhen();
-        long creationTimeDelta = SystemClock.uptimeMillis() - notif.getCreationTime();
+        long creationTimeDelta = UseElapsedRealtimeForCreationTime.getCurrentTime()
+                - notif.getCreationTime();
+
         long creationTimeWallClock = System.currentTimeMillis() - creationTimeDelta;
         return Math.min(notifWhenWallClock, creationTimeWallClock);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 041ed65..485d5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@
                         if (NotificationBundleUi.isEnabled()) {
                             releaseNotificationIfKeptForRemoteInputHistory(row.getEntryAdapter());
                         } else {
-                            releaseNotificationIfKeptForRemoteInputHistory(row.getEntry());
+                            releaseNotificationIfKeptForRemoteInputHistory(row.getEntryLegacy());
                         }
                     }
                     return started;
@@ -189,8 +189,8 @@
                     statusBarNotification = row.getEntryAdapter().getSbn();
                 }
             } else {
-                if (row.getEntry() != null) {
-                    statusBarNotification = row.getEntry().getSbn();
+                if (row.getEntryLegacy() != null) {
+                    statusBarNotification = row.getEntryLegacy().getSbn();
                 }
             }
             if (statusBarNotification == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 2e83910..472dc82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,8 +19,6 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.Configuration
 import android.os.SystemClock
 import android.util.IndentingPrintWriter
 import android.util.Log
@@ -42,16 +40,14 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.WallpaperController
 import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
@@ -82,13 +78,11 @@
     private val wallpaperInteractor: WallpaperInteractor,
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val dozeParameters: DozeParameters,
-    @ShadeDisplayAware private val context: Context,
-    private val splitShadeStateController: SplitShadeStateController,
+    private val shadeModeInteractor: ShadeModeInteractor,
     private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
     private val appZoomOutOptional: Optional<AppZoomOut>,
     @Application private val applicationScope: CoroutineScope,
     dumpManager: DumpManager,
-    configurationController: ConfigurationController,
 ) : ShadeExpansionListener, Dumpable {
     companion object {
         private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -110,7 +104,6 @@
     private var isOpen: Boolean = false
     private var isBlurred: Boolean = false
     private var listeners = mutableListOf<DepthListener>()
-    private var inSplitShade: Boolean = false
 
     private var prevTracking: Boolean = false
     private var prevTimestamp: Long = -1
@@ -294,7 +287,7 @@
 
     private fun blurRadiusToZoomOut(blurRadius: Float): Float {
         var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
-        if (inSplitShade) {
+        if (shadeModeInteractor.isSplitShade) {
             zoomOut = 0f
         }
 
@@ -432,14 +425,6 @@
         }
         shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
         shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
-        updateResources()
-        configurationController.addCallback(
-            object : ConfigurationController.ConfigurationListener {
-                override fun onConfigChanged(newConfig: Configuration?) {
-                    updateResources()
-                }
-            }
-        )
         applicationScope.launch {
             wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
                 wallpaperSupportsAmbientMode = supported
@@ -469,10 +454,6 @@
         }
     }
 
-    private fun updateResources() {
-        inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
-    }
-
     fun addListener(listener: DepthListener) {
         listeners.add(listener)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f88c618..c2a87cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -675,7 +675,7 @@
         }
         StatusBarIconView icon = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getIcons().getShelfIcon()
-                : row.getEntry().getIcons().getShelfIcon();
+                : row.getEntryLegacy().getIcons().getShelfIcon();
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
         if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
             int top = (int) (maxTop - shelfIconPosition);
@@ -689,7 +689,7 @@
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
         StatusBarIconView icon = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getIcons().getShelfIcon()
-                : row.getEntry().getIcons().getShelfIcon();
+                : row.getEntryLegacy().getIcons().getShelfIcon();
         boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 6cebcd9..6d3c12d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -18,10 +18,10 @@
 per-file *Notification* = file:notification/OWNERS
 # Files that control blur effects on shade
 per-file *NotificationShadeDepth* = set noparent
-per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com
+per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
 per-file *NotificationShadeDepth* = file:../keyguard/OWNERS
 per-file *Blur* = set noparent
-per-file *Blur* = shanh@google.com, rahulbanerjee@google.com
+per-file *Blur* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
 # Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
 per-file *Mode* = file:notification/OWNERS
 per-file *SmartReply* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 7e70312..03108de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -17,10 +17,13 @@
 package com.android.systemui.statusbar.chips.call.ui.viewmodel
 
 import android.app.PendingIntent
+import android.content.ComponentName
 import android.content.Context
 import android.view.View
 import com.android.internal.jank.Cuj
 import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ComposableControllerFactory
+import com.android.systemui.animation.DelegateTransitionAnimatorController
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
@@ -48,7 +51,10 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.stateIn
 
 /** View model for the ongoing phone call chip shown in the status bar. */
@@ -63,14 +69,62 @@
     private val activityStarter: ActivityStarter,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) : OngoingActivityChipViewModel {
+    /** The transition cookie used to register and unregister launch and return animations. */
+    private val cookie =
+        ActivityTransitionAnimator.TransitionCookie("${CallChipViewModel::class.java}")
+
+    /**
+     * Used internally to determine when a launch or return animation is in progress, as these
+     * require special handling.
+     */
+    private val transitionState: MutableStateFlow<TransitionState> =
+        MutableStateFlow(TransitionState.NoTransition)
+
+    // Since we're combining the chip state and the transition state flows, getting the old value by
+    // using [pairwise()] would confuse things. This is because if the calculation is triggered by
+    // a change in transition state, the chip state will still show the previous and current values,
+    // making it difficult to figure out what actually changed. Instead we cache the old value here,
+    // so that at each update we can keep track of what actually changed.
+    private var latestState: OngoingCallModel = OngoingCallModel.NoCall
+    private var latestTransitionState: TransitionState = TransitionState.NoTransition
+
     private val chipWithReturnAnimation: StateFlow<OngoingActivityChipModel> =
         if (StatusBarChipsReturnAnimations.isEnabled) {
-            interactor.ongoingCallState
-                .map { state ->
-                    when (state) {
-                        is OngoingCallModel.NoCall -> OngoingActivityChipModel.Inactive()
+            combine(interactor.ongoingCallState, transitionState) { newState, newTransitionState ->
+                    val oldState = latestState
+                    latestState = newState
+                    val oldTransitionState = latestTransitionState
+                    latestTransitionState = newTransitionState
+
+                    logger.log(
+                        TAG,
+                        LogLevel.DEBUG,
+                        {},
+                        {
+                            "Call chip state updated: oldState=$oldState newState=$newState " +
+                                "oldTransitionState=$oldTransitionState " +
+                                "newTransitionState=$newTransitionState"
+                        },
+                    )
+
+                    when (newState) {
+                        is OngoingCallModel.NoCall ->
+                            OngoingActivityChipModel.Inactive(
+                                transitionManager = getTransitionManager(newState)
+                            )
+
                         is OngoingCallModel.InCall ->
-                            prepareChip(state, systemClock, isHidden = state.isAppVisible)
+                            prepareChip(
+                                newState,
+                                systemClock,
+                                isHidden =
+                                    shouldChipBeHidden(
+                                        oldState = oldState,
+                                        newState = newState,
+                                        oldTransitionState = oldTransitionState,
+                                        newTransitionState = newTransitionState,
+                                    ),
+                            )
                     }
                 }
                 .stateIn(
@@ -112,6 +166,12 @@
             chipLegacy
         }
 
+    /**
+     * The controller factory that the call chip uses to register and unregister its transition
+     * animations.
+     */
+    private var transitionControllerFactory: ComposableControllerFactory? = null
+
     /** Builds an [OngoingActivityChipModel.Active] from all the relevant information. */
     private fun prepareChip(
         state: OngoingCallModel.InCall,
@@ -149,6 +209,7 @@
                 onClickListenerLegacy = getOnClickListener(state.intent),
                 clickBehavior = getClickBehavior(state.intent),
                 isHidden = isHidden,
+                transitionManager = getTransitionManager(state),
             )
         } else {
             val startTimeInElapsedRealtime =
@@ -161,6 +222,7 @@
                 onClickListenerLegacy = getOnClickListener(state.intent),
                 clickBehavior = getClickBehavior(state.intent),
                 isHidden = isHidden,
+                transitionManager = getTransitionManager(state),
             )
         }
     }
@@ -191,9 +253,21 @@
                 onClick = { expandable ->
                     StatusBarChipsModernization.unsafeAssertInNewMode()
                     val animationController =
-                        expandable.activityTransitionController(
-                            Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
-                        )
+                        if (
+                            !StatusBarChipsReturnAnimations.isEnabled ||
+                                transitionControllerFactory == null
+                        ) {
+                            expandable.activityTransitionController(
+                                Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
+                            )
+                        } else {
+                            // When return animations are enabled, we use a long-lived registration
+                            // with controllers created on-demand by the animation library instead
+                            // of explicitly creating one at the time of the click. By not passing
+                            // a controller here, we let the framework do its work. Otherwise, the
+                            // explicit controller would take precedence and override the other one.
+                            null
+                        }
                     activityStarter.postStartActivityDismissingKeyguard(intent, animationController)
                 }
             )
@@ -210,6 +284,120 @@
         )
     }
 
+    private fun getTransitionManager(
+        state: OngoingCallModel
+    ): OngoingActivityChipModel.TransitionManager? {
+        if (!StatusBarChipsReturnAnimations.isEnabled) return null
+        return if (state is OngoingCallModel.NoCall) {
+            OngoingActivityChipModel.TransitionManager(
+                unregisterTransition = { activityStarter.unregisterTransition(cookie) }
+            )
+        } else {
+            val component = (state as OngoingCallModel.InCall).intent?.intent?.component
+            if (component != null) {
+                val factory = getTransitionControllerFactory(component)
+                OngoingActivityChipModel.TransitionManager(
+                    factory,
+                    registerTransition = {
+                        activityStarter.registerTransition(cookie, factory, scope)
+                    },
+                )
+            } else {
+                // Without a component we can't instantiate a controller factory, and without a
+                // factory registering an animation is impossible. In this case, the transition
+                // manager is empty and inert.
+                OngoingActivityChipModel.TransitionManager()
+            }
+        }
+    }
+
+    private fun getTransitionControllerFactory(
+        component: ComponentName
+    ): ComposableControllerFactory {
+        var factory = transitionControllerFactory
+        if (factory?.component == component) return factory
+
+        factory =
+            object :
+                ComposableControllerFactory(
+                    cookie,
+                    component,
+                    launchCujType = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+                ) {
+                override suspend fun createController(
+                    forLaunch: Boolean
+                ): ActivityTransitionAnimator.Controller {
+                    transitionState.value =
+                        if (forLaunch) {
+                            TransitionState.LaunchRequested
+                        } else {
+                            TransitionState.ReturnRequested
+                        }
+
+                    val controller =
+                        expandable
+                            .mapNotNull {
+                                it?.activityTransitionController(
+                                    launchCujType,
+                                    cookie,
+                                    component,
+                                    returnCujType,
+                                    isEphemeral = false,
+                                )
+                            }
+                            .first()
+
+                    return object : DelegateTransitionAnimatorController(controller) {
+                        override val isLaunching: Boolean
+                            get() = forLaunch
+
+                        override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
+                            delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+                            transitionState.value =
+                                if (isLaunching) {
+                                    TransitionState.Launching
+                                } else {
+                                    TransitionState.Returning
+                                }
+                        }
+
+                        override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+                            delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
+                            transitionState.value = TransitionState.NoTransition
+                        }
+
+                        override fun onTransitionAnimationCancelled(
+                            newKeyguardOccludedState: Boolean?
+                        ) {
+                            delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+                            transitionState.value = TransitionState.NoTransition
+                        }
+                    }
+                }
+            }
+
+        transitionControllerFactory = factory
+        return factory
+    }
+
+    /** Define the current state of this chip's transition animation. */
+    private sealed interface TransitionState {
+        /** Idle. */
+        data object NoTransition : TransitionState
+
+        /** Launch animation has been requested but hasn't started yet. */
+        data object LaunchRequested : TransitionState
+
+        /** Launch animation in progress. */
+        data object Launching : TransitionState
+
+        /** Return animation has been requested but hasn't started yet. */
+        data object ReturnRequested : TransitionState
+
+        /** Return animation in progress. */
+        data object Returning : TransitionState
+    }
+
     companion object {
         private val phoneIcon =
             Icon.Resource(
@@ -217,5 +405,42 @@
                 ContentDescription.Resource(R.string.ongoing_call_content_description),
             )
         private val TAG = "CallVM".pad()
+
+        /** Determines whether or not an active call chip should be hidden. */
+        private fun shouldChipBeHidden(
+            oldState: OngoingCallModel,
+            newState: OngoingCallModel.InCall,
+            oldTransitionState: TransitionState,
+            newTransitionState: TransitionState,
+        ): Boolean {
+            // The app is in the background and no transitions are ongoing (during transitions,
+            // [isAppVisible] must always be true). Show the chip.
+            if (!newState.isAppVisible) return false
+
+            // The call has just started and is visible. Hide the chip.
+            if (oldState is OngoingCallModel.NoCall) return true
+
+            // The state went from the app not being visible to visible. This happens when the chip
+            // is tapped and a launch animation is about to start. Keep the chip showing.
+            if (!(oldState as OngoingCallModel.InCall).isAppVisible) return false
+
+            // The app was and remains visible, but the transition state has changed. A launch or
+            // return animation has been requested or is ongoing. Keep the chip showing.
+            if (
+                newTransitionState is TransitionState.LaunchRequested ||
+                    newTransitionState is TransitionState.Launching ||
+                    newTransitionState is TransitionState.ReturnRequested ||
+                    newTransitionState is TransitionState.Returning
+            ) {
+                return false
+            }
+
+            // The app was and remains visible, so we generally want to hide the chip. The only
+            // exception is if a return transition has just ended. In this case, the transition
+            // state changes shortly before the app visibility does. If we hide the chip between
+            // these two updates, this results in a flicker. We bridge the gap by keeping the chip
+            // showing.
+            return oldTransitionState != TransitionState.Returning
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 06656b7..1a802d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -200,8 +200,8 @@
                     colors,
                     startTimeMs = this.promotedContent.time.elapsedRealtimeMillis,
                     isEventInFuture = this.promotedContent.time.isCountDown,
-                    onClickListenerLegacy,
-                    clickBehavior,
+                    onClickListenerLegacy = onClickListenerLegacy,
+                    clickBehavior = clickBehavior,
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
index f650d8d..fa8d256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -78,6 +78,7 @@
                 rememberChronometerState(
                     eventTimeMillis = viewModel.startTimeMs,
                     isCountDown = viewModel.isEventInFuture,
+                    timeSource = viewModel.timeSource,
                 )
             timerState.currentTimeText?.let { text ->
                 Text(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 4edb23d..58d3890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -90,6 +91,8 @@
             },
         borderStroke = borderStroke,
         onClick = onClick,
+        useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+        transitionControllerFactory = model.transitionManager?.controllerFactory,
     ) {
         ChipBody(model, iconViewStore, isClickable = onClick != null)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
index 407849b..700e6d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
@@ -21,12 +21,14 @@
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.dimensionResource
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
 
@@ -36,6 +38,18 @@
     iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
     modifier: Modifier = Modifier,
 ) {
+    if (StatusBarChipsReturnAnimations.isEnabled) {
+        SideEffect {
+            // Active chips must always be capable of animating to/from activities, even when they
+            // are hidden. Therefore we always register their transitions.
+            for (chip in chips.active) chip.transitionManager?.registerTransition?.invoke()
+            // Inactive chips and chips in the overflow are never shown, so they must not have any
+            // registered transition.
+            for (chip in chips.overflow) chip.transitionManager?.unregisterTransition?.invoke()
+            for (chip in chips.inactive) chip.transitionManager?.unregisterTransition?.invoke()
+        }
+    }
+
     val shownChips = chips.active.filter { !it.isHidden }
     if (shownChips.isNotEmpty()) {
         Row(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 106a363..3876d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -18,12 +18,15 @@
 
 import android.annotation.CurrentTimeMillisLong
 import android.annotation.ElapsedRealtimeLong
+import android.os.SystemClock
 import android.view.View
+import com.android.systemui.animation.ComposableControllerFactory
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.viewmodel.TimeSource
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 
 /** Model representing the display of an ongoing activity as a chip in the status bar. */
@@ -31,6 +34,9 @@
     /** Condensed name representing the model, used for logs. */
     abstract val logName: String
 
+    /** Object used to manage the behavior of this chip during activity launch and returns. */
+    abstract val transitionManager: TransitionManager?
+
     /**
      * This chip shouldn't be shown.
      *
@@ -38,7 +44,10 @@
      *   animated, and false if that transition should *not* be animated (i.e. the chip view should
      *   immediately disappear).
      */
-    data class Inactive(val shouldAnimate: Boolean = true) : OngoingActivityChipModel() {
+    data class Inactive(
+        val shouldAnimate: Boolean = true,
+        override val transitionManager: TransitionManager? = null,
+    ) : OngoingActivityChipModel() {
         override val logName = "Inactive(anim=$shouldAnimate)"
     }
 
@@ -59,6 +68,7 @@
         open val onClickListenerLegacy: View.OnClickListener?,
         /** Data class that determines how clicks on the chip should be handled. */
         open val clickBehavior: ClickBehavior,
+        override val transitionManager: TransitionManager?,
         /**
          * Whether this chip should be hidden. This can be the case depending on system states (like
          * which apps are in the foreground and whether there is an ongoing transition.
@@ -75,6 +85,7 @@
             override val colors: ColorsModel,
             override val onClickListenerLegacy: View.OnClickListener?,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -84,6 +95,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -105,6 +117,13 @@
              * [android.widget.Chronometer.setBase].
              */
             @ElapsedRealtimeLong val startTimeMs: Long,
+
+            /**
+             * The [TimeSource] that should be used to track the current time for this timer. Should
+             * be compatible with [startTimeMs].
+             */
+            val timeSource: TimeSource = TimeSource { SystemClock.elapsedRealtime() },
+
             /**
              * True if this chip represents an event starting in the future and false if this chip
              * represents an event that has already started. If true, [startTimeMs] should be in the
@@ -113,6 +132,7 @@
             val isEventInFuture: Boolean = false,
             override val onClickListenerLegacy: View.OnClickListener?,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -122,6 +142,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -148,6 +169,7 @@
             @CurrentTimeMillisLong val time: Long,
             override val onClickListenerLegacy: View.OnClickListener?,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -157,6 +179,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -176,6 +199,7 @@
             override val colors: ColorsModel,
             /** The number of seconds until an event is started. */
             val secondsUntilStarted: Long,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -185,6 +209,7 @@
                 colors,
                 onClickListenerLegacy = null,
                 clickBehavior = ClickBehavior.None,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -200,6 +225,7 @@
             val text: String,
             override val onClickListenerLegacy: View.OnClickListener? = null,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -209,6 +235,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -262,4 +289,17 @@
         /** Clicking the chip will show the heads up notification associated with the chip. */
         data class ShowHeadsUpNotification(val onClick: () -> Unit) : ClickBehavior
     }
+
+    /** Defines the behavior of the chip with respect to activity launch and return transitions. */
+    data class TransitionManager(
+        /** The factory used to create the controllers that animate the chip. */
+        val controllerFactory: ComposableControllerFactory? = null,
+        /**
+         * Used to create a registration for this chip using [controllerFactory]. Must be
+         * idempotent.
+         */
+        val registerTransition: () -> Unit = {},
+        /** Used to remove the existing registration for this chip, if any. */
+        val unregisterTransition: () -> Unit = {},
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
index 7402583..0fc7f82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
 import android.annotation.ElapsedRealtimeLong
-import android.os.SystemClock
 import android.text.format.DateUtils.formatElapsedTime
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -90,7 +89,7 @@
 fun rememberChronometerState(
     eventTimeMillis: Long,
     isCountDown: Boolean,
-    timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+    timeSource: TimeSource,
 ): ChronometerState {
     val state =
         remember(timeSource, eventTimeMillis, isCountDown) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
index 3168a22..cb1002a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
@@ -17,14 +17,14 @@
 package com.android.systemui.statusbar.data.repository
 
 import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.app.displaylib.PerDisplayInstanceProvider
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.SingleInstanceRepositoryImpl
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.ConfigurationStateImpl
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
-import com.android.systemui.display.data.repository.PerDisplayInstanceProvider
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
-import com.android.systemui.display.data.repository.SingleInstanceRepositoryImpl
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import dagger.Lazy
 import dagger.Module
@@ -39,7 +39,6 @@
     private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
     private val factory: ConfigurationStateImpl.Factory,
 ) : PerDisplayInstanceProvider<ConfigurationState> {
-
     override fun createInstance(displayId: Int): ConfigurationState? {
         val displayWindowProperties =
             displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 8be9e41..fcdcc3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -105,7 +105,7 @@
                 mBubblesOptional.get().collapseStack();
             }
         } else {
-            if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+            if (!row.getEntryLegacy().isBubble() && mBubblesOptional.isPresent()) {
                 mBubblesOptional.get().collapseStack();
             }
         }
@@ -130,7 +130,7 @@
             } else {
                 row.setBubbleClickListener(v ->
                         mNotificationActivityStarter.onNotificationBubbleIconClicked(
-                                row.getEntry()));
+                                row.getEntryLegacy()));
             }
             row.setOnClickListener(this);
             row.setOnDragSuccessListener(mOnDragSuccessListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 874a059..8163128 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -74,7 +74,6 @@
         const val ANIMATION_DURATION_TOP_ROUNDING = 100L
     }
 
-    private val notificationEntry = notification.entry
     private val notificationKey = notification.key
 
     override val isLaunching: Boolean = true
@@ -160,7 +159,7 @@
     private val headsUpNotificationRow: ExpandableNotificationRow?
         get() {
             val pipelineParent = if (NotificationBundleUi.isEnabled)
-                notification.entryAdapter?.parent else notificationEntry.parent
+                notification.entryAdapter?.parent else notification.entryLegacy.parent
             val summaryEntry = (pipelineParent as? GroupEntry)?.summary
             return when {
                 headsUpManager.isHeadsUpEntry(notificationKey) -> notification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 26c302b..b1a26af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -113,6 +113,10 @@
         return false
     }
 
+    override fun isPromotedOngoing(): Boolean {
+        return false
+    }
+
     override fun isFullScreenCapable(): Boolean {
         return false
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 3118ce5..4299825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -132,6 +132,11 @@
 
     boolean isAmbient();
 
+    /**
+     * Returns whether this row represents promoted ongoing notification.
+     */
+    boolean isPromotedOngoing();
+
     default boolean isFullScreenCapable() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index caa7abb..8f7f61f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -34,7 +34,7 @@
     }
 
     /**
-     * The SystemClock.uptimeMillis() when this object was created. In general, this means the
+     * The SystemClock.elapsedRealtime() when this object was created. In general, this means the
      * moment when NotificationManager notifies our listener about the existence of this entry.
      *
      * This value will not change if the notification is updated, although it will change if the
@@ -65,13 +65,4 @@
     @Nullable public PipelineEntry getPreviousParent() {
         return mPreviousAttachState.getParent();
     }
-
-    /**
-     * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
-     * fresh attach state (all entries will be null/default-initialized).
-     */
-    void beginNewAttachState() {
-        mPreviousAttachState.clone(mAttachState);
-        mAttachState.reset();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9795edf..b7fe39e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -522,7 +522,7 @@
     }
 
     private void onNotificationsInitialized() {
-        mInitializedTimestamp = mClock.uptimeMillis();
+        mInitializedTimestamp = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
     }
 
     private void postNotification(
@@ -532,7 +532,8 @@
 
         if (entry == null) {
             // A new notification!
-            entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+            entry = new NotificationEntry(sbn, ranking,
+                    UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
             mEventQueue.add(new InitEntryEvent(entry));
             mEventQueue.add(new BindEntryEvent(entry, sbn));
             mNotificationSet.put(sbn.getKey(), entry);
@@ -861,7 +862,7 @@
     // messages from system server.
     private void crashIfNotInitializing(RuntimeException exception) {
         final boolean isRecentlyInitialized = mInitializedTimestamp == 0
-                || mClock.uptimeMillis() - mInitializedTimestamp
+                || UseElapsedRealtimeForCreationTime.getCurrentTime(mClock) - mInitializedTimestamp
                         < INITIALIZATION_FORGIVENESS_WINDOW;
 
         if (isRecentlyInitialized) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
index 1f8d365..698fed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
@@ -89,9 +89,9 @@
                 return true
             }
 
-            // Using uptimeMillis since it's guaranteed to be monotonic, as we don't want a
+            // Using elapsedRealtime since it's guaranteed to be monotonic, as we don't want a
             // timezone/clock change to break us
-            val now = systemClock.uptimeMillis()
+            val now = UseElapsedRealtimeForCreationTime.getCurrentTime(systemClock)
 
             // Cannot purge the same entry from two threads simultaneously
             synchronized(key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d031d83..4558017 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -251,7 +251,7 @@
     /**
      * @param sbn the StatusBarNotification from system server
      * @param ranking also from system server
-     * @param creationTime SystemClock.uptimeMillis of when we were created
+     * @param creationTime SystemClock.elapsedRealtime of when we were created
      */
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
@@ -508,7 +508,7 @@
 
             ArrayList<NotificationEntry> children = new ArrayList<>();
             for (ExpandableNotificationRow child : rowChildren) {
-                children.add(child.getEntry());
+                children.add(child.getEntryLegacy());
             }
 
             return children;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 1168c64..345b6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -137,6 +137,10 @@
         return entry.ranking.isAmbient
     }
 
+    override fun isPromotedOngoing(): Boolean {
+        return entry.isPromotedOngoing
+    }
+
     override fun isFullScreenCapable(): Boolean {
         return entry.sbn.notification.fullScreenIntent != null
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index 872cd68..e9c4efc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -101,4 +101,13 @@
     public void setBucket(@PriorityBucket int bucket) {
         mBucket = bucket;
     }
+
+    /**
+     * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
+     * fresh attach state (all entries will be null/default-initialized).
+     */
+    void beginNewAttachState() {
+        mPreviousAttachState.clone(mAttachState);
+        mAttachState.reset();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 238ba8d..5cea821 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -289,6 +289,9 @@
 
         mIdToBundleEntry.clear();
         for (String id: mNotifBundler.getBundleIds()) {
+            if (BundleCoordinator.debugBundleUi) {
+                Log.i(TAG, "create BundleEntry with id: " + id);
+            }
             mIdToBundleEntry.put(id, new BundleEntry(id));
         }
     }
@@ -562,6 +565,11 @@
             entry.beginNewAttachState();
         }
 
+        for (BundleEntry be : mIdToBundleEntry.values()) {
+            be.beginNewAttachState();
+            // TODO(b/399736937) Clear bundle children
+            // BundleEntry has not representative summary so we do not need to clear it here.
+        }
         mNotifList.clear();
     }
 
@@ -570,7 +578,7 @@
             List<PipelineEntry> out,
             List<NotifFilter> filters) {
         Trace.beginSection("ShadeListBuilder.filterNotifs");
-        final long now = mSystemClock.uptimeMillis();
+        final long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock);
         for (PipelineEntry entry : entries) {
             if (entry instanceof GroupEntry) {
                 final GroupEntry groupEntry = (GroupEntry) entry;
@@ -614,7 +622,8 @@
 
                 GroupEntry group = mGroups.get(topLevelKey);
                 if (group == null) {
-                    group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
+                    group = new GroupEntry(topLevelKey,
+                            UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock));
                     mGroups.put(topLevelKey, group);
                 }
                 if (group.getParent() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
new file mode 100644
index 0000000..23f90f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+import android.app.Flags
+import com.android.systemui.util.time.SystemClock
+
+/** A helper class for replacing uptimeMillis with elapsedRealtime for entry creation times */
+public object UseElapsedRealtimeForCreationTime {
+    @JvmStatic
+    fun getCurrentTime(clock: SystemClock): Long {
+        if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+            return clock.elapsedRealtime()
+        }
+        return clock.uptimeMillis()
+    }
+
+    @JvmStatic
+    fun getCurrentTime(): Long {
+        if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+            return android.os.SystemClock.elapsedRealtime()
+        }
+        return android.os.SystemClock.uptimeMillis()
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2eec68b..fb7772e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -25,7 +25,7 @@
  * Represents a set of notification post events for a particular notification group.
  */
 public class EventBatch {
-    /** SystemClock.uptimeMillis() */
+    /** SystemClock.elapsedRealtime() */
     final long mCreatedTimestamp;
 
     /** SBN.getGroupKey -- same for all members */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 96b3542..944e313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
 import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
@@ -182,11 +183,12 @@
     private void maybeEmitBatch(StatusBarNotification sbn) {
         final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
         final EventBatch batch = mBatches.get(sbn.getGroupKey());
+        long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
         if (event != null) {
             mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
             emitBatch(requireNonNull(event.getBatch()));
         } else if (batch != null
-                && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+                && now - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
             mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
             emitBatch(batch);
         }
@@ -228,7 +230,8 @@
     private EventBatch getOrBuildBatch(final String groupKey) {
         EventBatch batch = mBatches.get(groupKey);
         if (batch == null) {
-            batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+            batch = new EventBatch(UseElapsedRealtimeForCreationTime.getCurrentTime(mClock),
+                    groupKey);
             mBatches.put(groupKey, batch);
         }
         return batch;
@@ -268,7 +271,8 @@
         }
         events.sort(mEventComparator);
 
-        long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+        long batchAge = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock)
+                - batch.mCreatedTimestamp;
         mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
 
         mHandler.onNotificationBatchPosted(events);
@@ -298,7 +302,7 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        long now = mClock.uptimeMillis();
+        long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
 
         int eventCount = 0;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index 8833ff1..4478d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -98,9 +98,14 @@
         object : NotifBundler("NotifBundler") {
 
             // Use list instead of set to keep fixed order
-            override val bundleIds: List<String> = SYSTEM_RESERVED_IDS
+            override val bundleIds: List<String> =
+                if (debugBundleUi) SYSTEM_RESERVED_IDS + "notify"
+                else SYSTEM_RESERVED_IDS
 
             override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+                if (debugBundleUi && entry?.key?.contains("notify") == true) {
+                    return "notify"
+                }
                 return entry?.representativeEntry?.channel?.id?.takeIf { it in this.bundleIds }
             }
         }
@@ -110,4 +115,9 @@
             pipeline.setNotifBundler(bundler)
         }
     }
+
+    companion object {
+        @kotlin.jvm.JvmField
+        var debugBundleUi: Boolean = true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index b54f21b..1be415d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -241,8 +241,7 @@
                 isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
                 mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
             }
-
-            return !isInflated(entry) || isMemberOfDelayedGroup;
+            return !isInflated(entry) || (isMemberOfDelayedGroup != null && isMemberOfDelayedGroup);
         }
 
         @Override
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 a0a8671..f43767d 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
@@ -22,7 +22,6 @@
 import com.android.internal.widget.MessagingMessage
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -147,9 +146,7 @@
         traceSection("updateNotifOnUiModeChanged") {
             mPipeline?.allNotifs?.forEach { entry ->
                 entry.row?.onUiModeChanged()
-                if (Flags.notificationUndoGutsOnConfigChanged()) {
-                    mGutsManager.closeAndUndoGuts()
-                }
+                mGutsManager.closeAndUndoGuts()
             }
         }
     }
@@ -158,16 +155,7 @@
         colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
         mPipeline?.allNotifs?.forEach { entry ->
             entry.onDensityOrFontScaleChanged()
-            if (Flags.notificationUndoGutsOnConfigChanged()) {
-                mGutsManager.closeAndUndoGuts()
-            } else {
-                // This property actually gets reset when the guts are re-inflated, so we're never
-                // actually calling onDensityOrFontScaleChanged below.
-                val exposedGuts = entry.areGutsExposed()
-                if (exposedGuts) {
-                    mGutsManager.onDensityOrFontScaleChanged(entry)
-                }
-            }
+            mGutsManager.closeAndUndoGuts()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index bdbdc53..0466c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,9 +39,9 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -113,6 +113,8 @@
     @VisibleForTesting
     protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
 
+    private final boolean mCheckLockScreenTransitionEnabled = Flags.checkLockscreenGoneTransition();
+
     @Inject
     public VisualStabilityCoordinator(
             @Background DelayableExecutor delayableExecutor,
@@ -182,7 +184,7 @@
                     this::onTrackingHeadsUpModeChanged);
         }
 
-        if (Flags.checkLockscreenGoneTransition()) {
+        if (mCheckLockScreenTransitionEnabled) {
             if (SceneContainerFlag.isEnabled()) {
                 mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
                                 Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone), null),
@@ -437,7 +439,7 @@
         boolean wasReorderingAllowed = mReorderingAllowed;
         // No need to run notification pipeline when the lockscreen is in fading animation.
         mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
-                || (Flags.checkLockscreenGoneTransition() && mLockscreenInGoneTransition));
+                || (mCheckLockScreenTransitionEnabled && mLockscreenInGoneTransition));
         mReorderingAllowed = isReorderingAllowed();
         if (wasPipelineRunAllowed != mPipelineRunAllowed
                 || wasReorderingAllowed != mReorderingAllowed) {
@@ -499,7 +501,7 @@
      * notification and we are reordering based on the user's change.
      *
      * @param entry notification entry that can change sections even if isReorderingAllowed is false
-     * @param now current time SystemClock.uptimeMillis
+     * @param now current time SystemClock.elapsedRealtime
      */
     public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) {
         final String entryKey = entry.getKey();
@@ -566,7 +568,7 @@
         pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
         pw.println("  notifPanelCollapsing: " + mNotifPanelCollapsing);
         pw.println("  launchingNotifActivity: " + mNotifPanelLaunchingActivity);
-        if (Flags.checkLockscreenGoneTransition()) {
+        if (mCheckLockScreenTransitionEnabled) {
             pw.println("  lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
         }
         pw.println("reorderingAllowed: " + mReorderingAllowed);
@@ -627,7 +629,7 @@
     }
 
     private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
-        if (!Flags.checkLockscreenGoneTransition()) {
+        if (!mCheckLockScreenTransitionEnabled) {
             return;
         }
         if (inGoneTransition == mLockscreenInGoneTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 07fa6ae..03b4076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -20,7 +20,6 @@
 
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 
-import android.os.SystemClock;
 import android.service.notification.NotificationStats;
 
 import androidx.annotation.NonNull;
@@ -30,6 +29,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -85,7 +85,7 @@
     public void onImportanceChanged(NotificationEntry entry) {
         mVisualStabilityCoordinator.temporarilyAllowSectionChanges(
                 entry,
-                SystemClock.uptimeMillis());
+                UseElapsedRealtimeForCreationTime.getCurrentTime());
     }
 
     @NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index 776c7d5..389bb31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -41,8 +41,8 @@
      *              this entry will not have any grouping nor sorting information.
      *              If this filter is registered via {@link NotifPipeline#addFinalizeFilter},
      *              this entry will have grouping and sorting information.
-     * @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
-     *            pipeline execution. This value will be the same for all pluggable calls made
+     * @param now A timestamp in SystemClock.elapsedRealtime that represents "now" for the purposes
+     *            of pipeline execution. This value will be the same for all pluggable calls made
      *            during this pipeline run, giving pluggables a stable concept of "now" to compare
      *            various entries against.
      * @return True if the notif should be removed from the list
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 8021d8f..a552ca5 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
@@ -234,21 +234,21 @@
     fun getChildCount(): Int = controller.getChildCount()
 
     fun addChildAt(child: ShadeNode, index: Int) {
-        traceSection("ShadeNode#addChildAt") {
+        traceSection({ "ShadeNode#${controller::class.simpleName}#addChildAt" }) {
             controller.addChildAt(child.controller, index)
             child.controller.onViewAdded()
         }
     }
 
     fun moveChildTo(child: ShadeNode, index: Int) {
-        traceSection("ShadeNode#moveChildTo") {
+        traceSection({ "ShadeNode#${controller::class.simpleName}#moveChildTo" }) {
             controller.moveChildTo(child.controller, index)
             child.controller.onViewMoved()
         }
     }
 
     fun removeChild(child: ShadeNode, isTransfer: Boolean) {
-        traceSection("ShadeNode#removeChild") {
+        traceSection({ "ShadeNode#${controller::class.simpleName}#removeChild" }) {
             controller.removeChild(child.controller, isTransfer)
             child.controller.onViewRemoved()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index cdbe0fd..8d1e611 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -81,7 +81,7 @@
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(emptySet())
         } else {
-            activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }
+            activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }.distinctUntilChanged()
         }
     }
 
@@ -90,9 +90,9 @@
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(emptySet())
         } else {
-            activeHeadsUpRows.map {
-                it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet()
-            }
+            activeHeadsUpRows
+                .map { it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet() }
+                .distinctUntilChanged() // TODO(b/402428276) stop sending duplicate updates instead
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index 147a5af..619d48f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -18,9 +18,9 @@
 
 import android.view.Display
 import androidx.lifecycle.lifecycleScope
+import com.android.app.displaylib.PerDisplayRepository
 import com.android.app.tracing.traceSection
 import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.display.data.repository.PerDisplayRepository
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ec8fbc0..5bdd769 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -19,7 +19,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -44,6 +43,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.UpdateSource;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -112,7 +112,7 @@
 
         @Override
         public void run() {
-            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+            mLastVisibilityReportUptimeMs = UseElapsedRealtimeForCreationTime.getCurrentTime();
 
             // 1. Loop over active entries:
             //   A. Keep list of visible notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 2a01a14..777392d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -19,19 +19,26 @@
 import android.app.Flags
 import android.app.Flags.notificationsRedesignTemplates
 import android.app.Notification
+import android.content.Context
 import android.graphics.PorterDuff
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View.GONE
+import android.view.View.MeasureSpec.AT_MOST
+import android.view.View.MeasureSpec.EXACTLY
+import android.view.View.MeasureSpec.UNSPECIFIED
+import android.view.View.MeasureSpec.makeMeasureSpec
 import android.view.View.VISIBLE
 import android.view.ViewGroup.MarginLayoutParams
 import android.view.ViewStub
 import android.widget.Chronometer
 import android.widget.DateTimeView
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.ProgressBar
 import android.widget.TextView
+import androidx.annotation.DimenRes
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Box
@@ -42,7 +49,9 @@
 import androidx.compose.runtime.key
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.isVisible
@@ -88,22 +97,12 @@
     }
 
     key(content.identity) {
-        val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
-        val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
-
-        val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
-
-        val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
-        val borderShape = RoundedCornerShape(borderRadius)
-
-        Box(modifier = modifier.padding(sidePaddingValues)) {
-            AODPromotedNotificationView(
-                layoutResource = layoutResource,
-                content = content,
-                audiblyAlertedIconVisible = audiblyAlertedIconVisible,
-                modifier = Modifier.border(borderStroke, borderShape),
-            )
-        }
+        AODPromotedNotificationView(
+            layoutResource = layoutResource,
+            content = content,
+            audiblyAlertedIconVisible = audiblyAlertedIconVisible,
+            modifier = modifier,
+        )
     }
 }
 
@@ -114,27 +113,91 @@
     audiblyAlertedIconVisible: Boolean,
     modifier: Modifier = Modifier,
 ) {
-    AndroidView(
-        factory = { context ->
-            val view =
-                traceSection("$TAG.inflate") {
-                    LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
-                }
+    val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+    val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
 
-            val updater =
-                traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
+    val boxModifier = modifier.padding(sidePaddingValues)
 
-            view.setTag(viewUpdaterTagId, updater)
+    val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
 
-            view
-        },
-        update = { view ->
-            val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+    val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+    val borderShape = RoundedCornerShape(borderRadius)
 
-            traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
-        },
-        modifier = modifier,
-    )
+    val maxHeight =
+        with(LocalDensity.current) {
+                scaledFontHeight(systemuiR.dimen.notification_max_height_for_promoted_ongoing)
+                    .toPx()
+            }
+            .toInt()
+
+    val viewModifier = Modifier.border(borderStroke, borderShape)
+
+    Box(modifier = boxModifier) {
+        AndroidView(
+            factory = { context ->
+                val notif =
+                    traceSection("$TAG.inflate") {
+                        LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+                    }
+                val updater =
+                    traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) }
+
+                val frame = FrameLayoutWithMaxHeight(maxHeight, context)
+                frame.addView(notif)
+                frame.setTag(viewUpdaterTagId, updater)
+
+                frame
+            },
+            update = { frame ->
+                val updater = frame.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+                traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
+                frame.maxHeight = maxHeight
+            },
+            modifier = viewModifier,
+        )
+    }
+}
+
+private class FrameLayoutWithMaxHeight(maxHeight: Int, context: Context) : FrameLayout(context) {
+    var maxHeight = maxHeight
+        set(value) {
+            if (field != value) {
+                field = value
+                requestLayout()
+            }
+        }
+
+    // This mirrors the logic in NotificationContentView.onMeasure.
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        if (childCount < 1) {
+            return
+        }
+
+        val child = getChildAt(0)
+        val childLayoutHeight = child.layoutParams.height
+        val childHeightSpec =
+            if (childLayoutHeight >= 0) {
+                makeMeasureSpec(maxHeight.coerceAtMost(childLayoutHeight), EXACTLY)
+            } else {
+                makeMeasureSpec(maxHeight, AT_MOST)
+            }
+        measureChildWithMargins(child, widthMeasureSpec, 0, childHeightSpec, 0)
+        val childMeasuredHeight = child.measuredHeight
+
+        val ownHeightMode = MeasureSpec.getMode(heightMeasureSpec)
+        val ownHeightSize = MeasureSpec.getSize(heightMeasureSpec)
+
+        val ownMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec)
+        val ownMeasuredHeight =
+            if (ownHeightMode != UNSPECIFIED) {
+                childMeasuredHeight.coerceAtMost(ownHeightSize)
+            } else {
+                childMeasuredHeight
+            }
+
+        setMeasuredDimension(ownMeasuredWidth, ownMeasuredHeight)
+    }
 }
 
 private val PromotedNotificationContentModel.layoutResource: Int?
@@ -521,6 +584,11 @@
     val brush = SolidColor(androidx.compose.ui.graphics.Color(colorInt))
 }
 
+@Composable
+private fun scaledFontHeight(@DimenRes dimenId: Int): Dp {
+    return dimensionResource(dimenId) * LocalDensity.current.fontScale.coerceAtLeast(1f)
+}
+
 private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
 
 private const val TAG = "AODPromotedNotification"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
new file mode 100644
index 0000000..1429535
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class which can receive both a demotion signal and a single handler of that signal */
+@SysUISingleton
+class PackageDemotionInteractor @Inject constructor() {
+    private var demotionSignalHandler: ((packageName: String, uid: Int) -> Unit)? = null
+
+    /**
+     * called after sending a the demotion signal to
+     * [android.app.INotificationManager.setCanBePromoted]
+     */
+    fun onPackageDemoted(packageName: String, uid: Int) {
+        demotionSignalHandler?.invoke(packageName, uid)
+    }
+
+    /** sets the [handler] that will be called when [onPackageDemoted] is called. */
+    fun setOnPackageDemotionHandler(handler: (packageName: String, uid: Int) -> Unit) {
+        demotionSignalHandler = handler
+    }
+}
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 6892226..e768673 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
@@ -40,6 +40,7 @@
 import com.android.app.animation.Interpolators;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.jank.InteractionJankMonitor.Configuration;
+import com.android.systemui.Flags;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.common.shared.colors.SurfaceEffectColors;
 import com.android.systemui.res.R;
@@ -101,7 +102,8 @@
     private ValueAnimator mBackgroundColorAnimator;
     private float mAppearAnimationFraction = -1.0f;
     private float mAppearAnimationTranslation;
-    private int mNormalColor;
+    protected int mNormalColor;
+    protected int mOpaqueColor;
     private boolean mIsBelowSpeedBump;
     private long mLastActionUpTime;
 
@@ -130,17 +132,13 @@
 
     protected void updateColors() {
         if (notificationRowTransparency()) {
-            if (mIsBlurSupported) {
-                mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
-            } else {
-                mNormalColor = mContext.getColor(
-                        com.android.internal.R.color.materialColorSurfaceContainer);
-            }
+            mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+            mOpaqueColor = mContext.getColor(
+                    com.android.internal.R.color.materialColorSurfaceContainer);
         } else {
             mNormalColor = mContext.getColor(
                     com.android.internal.R.color.materialColorSurfaceContainerHigh);
         }
-        setBackgroundToNormalColor();
         mTintedRippleColor = mContext.getColor(
                 R.color.notification_ripple_tinted_color);
         mNormalRippleColor = mContext.getColor(
@@ -151,12 +149,6 @@
         mOverrideAmount = 0.0f;
     }
 
-    private void setBackgroundToNormalColor() {
-        if (mBackgroundNormal != null) {
-            mBackgroundNormal.setNormalColor(mNormalColor);
-        }
-    }
-
     /**
      * Reload background colors from resources and invalidate views.
      */
@@ -186,7 +178,6 @@
         mBackgroundNormal = findViewById(R.id.backgroundNormal);
         mFakeShadow = findViewById(R.id.fake_shadow);
         mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
-        setBackgroundToNormalColor();
         initBackground();
         updateBackgroundTint();
         updateOutlineAlpha();
@@ -352,7 +343,7 @@
     }
 
     protected boolean usesTransparentBackground() {
-        return mIsBlurSupported && notificationRowTransparency();
+        return false;
     }
 
     @Override
@@ -709,7 +700,11 @@
         if (withTint && mBgTint != NO_COLOR) {
             return mBgTint;
         } else {
-            return mNormalColor;
+            if (Flags.notificationRowTransparency()) {
+                return usesTransparentBackground() ? mNormalColor : mOpaqueColor;
+            } else {
+                return mNormalColor;
+            }
         }
     }
 
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 8da2f76..2c3676a 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
@@ -82,6 +82,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -421,9 +422,10 @@
                 }
                 onExpansionChanged(true /* userAction */, wasExpanded);
             } else {
-                final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
-                boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
-                mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+                final boolean wasExpanded =
+                        mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
+                boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(getEntryLegacy());
+                mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
                 if (shouldLogExpandClickMetric) {
                     mMetricsLogger.action(
                             MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
@@ -453,7 +455,7 @@
             if (NotificationBundleUi.isEnabled()) {
                 mOnExpandClickListener.onExpandClicked(this, mEntryAdapter, nowExpanded);
             } else {
-                mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+                mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
             }
             if (shouldLogExpandClickMetric) {
                 mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
@@ -558,7 +560,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return mKey;
         } else {
-            return mEntry.getKey();
+            return getEntryLegacy().getKey();
         }
     }
 
@@ -661,14 +663,14 @@
      */
     public boolean getIsNonblockable() {
         NotificationBundleUi.assertInLegacyMode();
-        if (mEntry == null) {
+        if (getEntryLegacy() == null) {
             return true;
         }
-        return !mEntry.isBlockable();
+        return !getEntryLegacy().isBlockable();
     }
 
     private boolean isConversation() {
-        return mPeopleNotificationIdentifier.getPeopleNotificationType(mEntry)
+        return mPeopleNotificationIdentifier.getPeopleNotificationType(getEntry())
                 != PeopleNotificationIdentifier.TYPE_NON_PERSON;
     }
 
@@ -679,7 +681,7 @@
             Trace.beginSection("ExpNotRow#onNotifUpdated (leaf)");
         }
         for (NotificationContentView l : mLayouts) {
-            l.onNotificationUpdated(mEntry);
+            l.onNotificationUpdated(getEntry());
         }
         mShowingPublicInitialized = false;
         if (mMenuRow != null) {
@@ -746,7 +748,7 @@
      */
     public void updateBubbleButton() {
         for (NotificationContentView l : mLayouts) {
-            l.updateBubbleButton(mEntry);
+            l.updateBubbleButton(getEntry());
         }
     }
 
@@ -777,7 +779,7 @@
                 return mEntryAdapter.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
                         getBackgroundColorWithoutTint());
             } else {
-                return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+                return getEntryLegacy().getContrastedColor(mContext, mIsMinimized && !isExpanded(),
                         getBackgroundColorWithoutTint());
             }
         }
@@ -885,7 +887,7 @@
         if (NotificationBundleUi.isEnabled()) {
             targetSdk = mEntryAdapter.getTargetSdk();
         } else {
-            targetSdk = mEntry.targetSdk;
+            targetSdk = getEntryLegacy().targetSdk;
         }
 
         boolean beforeN = targetSdk < Build.VERSION_CODES.N;
@@ -901,7 +903,7 @@
         if (NotificationBundleUi.isEnabled()) {
             summarization = mEntryAdapter.getSummarization();
         } else {
-            summarization = mEntry.getRanking().getSummarization();
+            summarization = getEntryLegacy().getRanking().getSummarization();
         }
 
         if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -945,7 +947,25 @@
         layout.setHeights(smallHeight, headsUpHeight, maxExpandedHeight);
     }
 
+    /**
+     * Check {@link NotificationBundleUi#isEnabled()}
+     * and use {@link #getEntryAdapter()} when true
+     * and {@link #getEntryLegacy()} when false.
+     */
     @NonNull
+    @Deprecated
+    public NotificationEntry getEntryLegacy() {
+        NotificationBundleUi.assertInLegacyMode();
+        return mEntry;
+    }
+
+    /**
+     * Check {@link NotificationBundleUi#isEnabled()}
+     * and use {@link #getEntryAdapter()} when true
+     * and {@link #getEntryLegacy()} when false.
+     */
+    @NonNull
+    @Deprecated
     public NotificationEntry getEntry() {
         return mEntry;
     }
@@ -979,7 +999,7 @@
         } else if (isAboveShelf() != wasAboveShelf) {
             mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
         }
-        updateColors();
+        updateBackgroundTint();
     }
 
     /**
@@ -1481,8 +1501,8 @@
     public void setBubbleClickListener(@Nullable OnClickListener l) {
         mBubbleClickListener = l;
         // ensure listener is passed to the content views
-        mPrivateLayout.updateBubbleButton(mEntry);
-        mPublicLayout.updateBubbleButton(mEntry);
+        mPrivateLayout.updateBubbleButton(getEntry());
+        mPublicLayout.updateBubbleButton(getEntry());
     }
 
     /**
@@ -1554,7 +1574,7 @@
             return initializationTime != -1
                     && SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
         } else {
-            return getEntry().hasFinishedInitialization();
+            return getEntryLegacy().hasFinishedInitialization();
         }
     }
 
@@ -1633,14 +1653,14 @@
         if (NotificationBundleUi.isEnabled()) {
             mEntryAdapter.prepareForInflation();
         } else {
-            mEntry.getSbn().clearPackageContext();
+            getEntryLegacy().getSbn().clearPackageContext();
         }
         // TODO: Move content inflation logic out of this call
         RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
         params.setNeedsReinflation(true);
 
         var rebindEndCallback = mRebindingTracker.trackRebinding(NotificationBundleUi.isEnabled()
-                ? mEntryAdapter.getKey() : mEntry.getKey());
+                ? mEntryAdapter.getKey() : getEntryLegacy().getKey());
         mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
         Trace.endSection();
     }
@@ -1678,21 +1698,36 @@
 
     @Override
     protected void setBackgroundTintColor(int color) {
+        if (notificationRowTransparency()) {
+            boolean isColorized = false;
+            if (NotificationBundleUi.isEnabled()) {
+                if (mEntryAdapter != null) {
+                    isColorized = mEntryAdapter.isColorized();
+                }
+            } else {
+                if (mEntry != null) {
+                    isColorized = mEntry.getSbn().getNotification().isColorized();
+                }
+            }
+            boolean isTransparent = usesTransparentBackground();
+            if (isColorized) {
+                // For colorized notifications, use a color that matches the tint color at 90% alpha
+                // when the row is transparent.
+                color = ColorUtils.setAlphaComponent(
+                        color, (int) (0xFF * (isTransparent ? 0.9f : 1)));
+            } else {
+                // For non-colorized notifications, use the semi-transparent normal color token
+                // when the row is transparent, and the opaque color token otherwise.
+                if (!isTransparent && mBgTint == NO_COLOR) {
+                    color = mOpaqueColor;
+                }
+            }
+        }
         super.setBackgroundTintColor(color);
         NotificationContentView view = getShowingLayout();
         if (view != null) {
             view.setBackgroundTintColor(color);
         }
-        if (notificationRowTransparency() && mBackgroundNormal != null) {
-            if (NotificationBundleUi.isEnabled() && mEntryAdapter != null) {
-                mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
-            } else {
-                if (mEntry != null) {
-                    mBackgroundNormal.setBgIsColorized(
-                            mEntry.getSbn().getNotification().isColorized());
-                }
-            }
-        }
     }
 
     public void closeRemoteInput() {
@@ -2310,7 +2345,9 @@
     }
 
     @VisibleForTesting
-    protected void setEntry(NotificationEntry entry) {
+    @Deprecated
+    protected void setEntryLegacy(NotificationEntry entry) {
+        NotificationBundleUi.assertInLegacyMode();
         mEntry = entry;
     }
 
@@ -2403,7 +2440,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return traceTag + "(" + getEntryAdapter().getStyle() + ")";
         } else {
-            return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+            return traceTag + "(" + getEntryLegacy().getNotificationStyle() + ")";
         }
     }
 
@@ -2908,7 +2945,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return getEntryAdapter().getIcons().getShelfIcon();
         } else {
-            return mEntry.getIcons().getShelfIcon();
+            return getEntryLegacy().getIcons().getShelfIcon();
         }
     }
 
@@ -3016,7 +3053,7 @@
                     mGroupExpansionManager.setGroupExpanded(mEntryAdapter, userExpanded);
                 }
             } else {
-                mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded);
+                mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), userExpanded);
             }
             onExpansionChanged(true /* userAction */, wasExpanded);
             return;
@@ -3113,7 +3150,7 @@
                     mChildrenContainer.setOnKeyguard(onKeyguard);
                 }
             }
-            updateColors();
+            updateBackgroundTint();
         }
     }
 
@@ -3159,7 +3196,7 @@
     public boolean canShowHeadsUp() {
         boolean canEntryHun = NotificationBundleUi.isEnabled()
                 ? mEntryAdapter.canPeek()
-                : mEntry.isStickyAndNotDemoted();
+                : getEntryLegacy().isStickyAndNotDemoted();
         if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
                 (!canEntryHun
                         || (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
@@ -3181,13 +3218,13 @@
         if (NotificationBundleUi.isEnabled()) {
             return mGroupExpansionManager.isGroupExpanded(mEntryAdapter);
         }
-        return mGroupExpansionManager.isGroupExpanded(mEntry);
+        return mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
     }
 
     private boolean isGroupRoot() {
         return NotificationBundleUi.isEnabled()
                 ? mGroupMembershipManager.isGroupRoot(mEntryAdapter)
-                : mGroupMembershipManager.isGroupSummary(mEntry);
+                : mGroupMembershipManager.isGroupSummary(getEntryLegacy());
     }
 
     private void onAttachedChildrenCountChanged() {
@@ -3209,7 +3246,8 @@
             if (NotificationBundleUi.isEnabled()) {
                 mPublicLayout.setNotificationWhen(mEntryAdapter.getWhen());
             } else {
-                mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+                mPublicLayout.setNotificationWhen(
+                        getEntryLegacy().getSbn().getNotification().getWhen());
             }
         }
         getShowingLayout().updateBackgroundColor(false /* animate */);
@@ -3243,12 +3281,19 @@
             return false;
         }
 
-        final NotificationEntry entry = mEntry;
-        if (entry == null) {
-            return false;
+        if (NotificationBundleUi.isEnabled()) {
+            final EntryAdapter entryAdapter = mEntryAdapter;
+            if (entryAdapter == null) {
+                return false;
+            }
+            return entryAdapter.isPromotedOngoing();
+        } else {
+            final NotificationEntry entry = mEntry;
+            if (entry == null) {
+                return false;
+            }
+            return entry.isPromotedOngoing();
         }
-
-        return entry.isPromotedOngoing();
     }
 
     private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
@@ -3537,7 +3582,8 @@
             return mEntryAdapter.isClearable()
                     && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
         } else {
-            return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+            return getEntryLegacy().isClearable()
+                    && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
         }
     }
 
@@ -3551,7 +3597,7 @@
             if (!NotificationBundleUi.isEnabled()) {
                 // this is only called if row.getParent() instanceof NotificationStackScrollLayout,
                 // so there is never a group to expand
-                mGroupExpansionManager.setGroupExpanded(mEntry, true);
+                mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), true);
             }
         }
         notifyHeightChanged(/* needsAnimation= */ false);
@@ -3784,7 +3830,7 @@
                 return true;
             }
         } else {
-            if (getEntry().getSbn().getNotification().isColorized()) {
+            if (getEntryLegacy().getSbn().getNotification().isColorized()) {
                 return true;
             }
         }
@@ -4255,7 +4301,7 @@
 
     public boolean isMediaRow() {
         NotificationBundleUi.assertInLegacyMode();
-        return mEntry.getSbn().getNotification().isMediaNotification();
+        return getEntryLegacy().getSbn().getNotification().isMediaNotification();
     }
 
     public void setAboveShelf(boolean aboveShelf) {
@@ -4377,11 +4423,7 @@
     public void dump(PrintWriter pwOriginal, String[] args) {
         IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
         // Skip super call; dump viewState ourselves
-        if (NotificationBundleUi.isEnabled()) {
-            pw.println("Notification: " + mEntryAdapter.getKey());
-        } else {
-            pw.println("Notification: " + mEntry.getKey());
-        }
+        pw.println("Notification: " + getKey());
         DumpUtilsKt.withIncreasedIndent(pw, () -> {
             pw.println(this);
             pw.print("visibility: " + getVisibility());
@@ -4618,7 +4660,7 @@
         if (NotificationBundleUi.isEnabled()) {
             mLaunchAnimationRunning = launchAnimationRunning;
         } else {
-            getEntry().setExpandAnimationRunning(launchAnimationRunning);
+            getEntryLegacy().setExpandAnimationRunning(launchAnimationRunning);
         }
     }
 
@@ -4627,12 +4669,16 @@
         if (NotificationBundleUi.isEnabled()) {
             return mLaunchAnimationRunning;
         } else {
-            return getEntry().isExpandAnimationRunning();
+            return getEntryLegacy().isExpandAnimationRunning();
         }
     }
 
     @Override
     protected boolean usesTransparentBackground() {
-        return super.usesTransparentBackground() && !mIsHeadsUp && !mOnKeyguard;
+        // Row background should be opaque when it's displayed as a heads-up notification or
+        // displayed on keyguard.
+        // TODO(b/388891313): Account for isBlurSupported when it is initialized and updated
+        // correctly.
+        return  notificationRowTransparency() && !mIsHeadsUp && !mOnKeyguard;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index ac55930..7c0ee668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -139,7 +139,7 @@
                         }
                         final int viewUserId = NotificationBundleUi.isEnabled()
                             ? mView.getEntryAdapter().getSbn().getUserId()
-                            : mView.getEntry().getSbn().getUserId();
+                            : mView.getEntryLegacy().getSbn().getUserId();
                         if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
                             mView.getPrivateLayout().setBubblesEnabledForUser(
                                     BUBBLES_SETTING_ENABLED_VALUE.equals(value));
@@ -395,7 +395,7 @@
                         mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
                     }
                 } else {
-                    mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+                    mView.getEntryLegacy().setInitializationTime(mClock.elapsedRealtime());
                     mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
                 }
                 mPluginManager.addPluginListener(mView,
@@ -429,7 +429,9 @@
     @Override
     @NonNull
     public String getNodeLabel() {
-        return NotificationBundleUi.isEnabled() ? mView.getLoggingKey() : logKey(mView.getEntry());
+        return NotificationBundleUi.isEnabled()
+                ? mView.getLoggingKey()
+                : logKey(mView.getEntryLegacy());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 9ae2eb1..20b826a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -109,7 +109,7 @@
 
         StatusBarNotification sn = NotificationBundleUi.isEnabled()
                 ? enr.getEntryAdapter().getSbn()
-                : enr.getEntry().getSbn();
+                : enr.getEntryLegacy().getSbn();
         Notification notification = sn.getNotification();
         final PendingIntent contentIntent = notification.contentIntent != null
                 ? notification.contentIntent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4914e10..e4997e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,9 +36,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dumpable;
+import com.android.systemui.common.shared.colors.SurfaceEffectColors;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
 import com.android.systemui.util.DrawableDumpKt;
@@ -52,7 +52,6 @@
 public class NotificationBackgroundView extends View implements Dumpable,
         ExpandableNotificationRow.DismissButtonTargetVisibilityListener {
 
-    private static final int MAX_ALPHA = 0xFF;
     private final boolean mDontModifyCorners;
     private Drawable mBackground;
     private int mClipTopAmount;
@@ -73,8 +72,6 @@
     private final ColorStateList mLightColoredStatefulColors;
     private final ColorStateList mDarkColoredStatefulColors;
     private int mNormalColor;
-    private boolean mBgIsColorized = false;
-    private boolean mForceOpaque = false;
     private final int convexR = 9;
     private final int concaveR = 22;
 
@@ -88,13 +85,15 @@
                 R.color.notification_state_color_light);
         mDarkColoredStatefulColors = getResources().getColorStateList(
                 R.color.notification_state_color_dark);
+        if (notificationRowTransparency()) {
+            mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+        } else  {
+            mNormalColor = mContext.getColor(
+                    com.android.internal.R.color.materialColorSurfaceContainerHigh);
+        }
         mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
     }
 
-    public void setNormalColor(int color) {
-        mNormalColor = color;
-    }
-
     @Override
     public void onTargetVisibilityChanged(boolean targetVisible) {
         if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
@@ -140,21 +139,6 @@
         }
     }
 
-    /**
-     * A way to tell whether the background has been colorized.
-     */
-    public boolean isColorized() {
-        return mBgIsColorized;
-    }
-
-    /**
-     * A way to inform this class whether the background has been colorized.
-     * We need to know this, in order to *not* override that color.
-     */
-    public void setBgIsColorized(boolean b) {
-        mBgIsColorized = b;
-    }
-
     private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) {
         // TODO(b/365585705): Adapt to RTL after the UX design is finalized.
 
@@ -311,28 +295,21 @@
         return ((LayerDrawable) mBackground).getDrawable(1);
     }
 
-    private void updateBaseLayerColor() {
-        // BG base layer being a drawable, there isn't a method like setColor() to color it.
-        // Instead, we set a color filter that essentially replaces every pixel of the drawable.
-        // For non-colorized notifications, this function specifies a new color token.
-        // For colorized notifications, this uses a color that matches the tint color at 90% alpha.
-        int color = isColorized()
-                ? ColorUtils.setAlphaComponent(mTintColor, (int) (MAX_ALPHA * 0.9f))
-                : mNormalColor;
-        getBaseBackgroundLayer().setColorFilter(
-                new PorterDuffColorFilter(
-                        color,
-                        PorterDuff.Mode.SRC)); // SRC operator discards the drawable's color+alpha
-    }
-
     public void setTint(int tintColor) {
         Drawable baseLayer = getBaseBackgroundLayer();
-        baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
-        baseLayer.setTint(tintColor);
-        mTintColor = tintColor;
         if (notificationRowTransparency()) {
-            updateBaseLayerColor();
+            // BG base layer being a drawable, there isn't a method like setColor() to color it.
+            // Instead, we set a color filter that essentially replaces every pixel of the drawable.
+            baseLayer.setColorFilter(
+                    new PorterDuffColorFilter(
+                            tintColor,
+                            // SRC operator discards the drawable's color+alpha
+                            PorterDuff.Mode.SRC));
+        } else {
+            baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
+            baseLayer.setTint(tintColor);
         }
+        mTintColor = tintColor;
         setStatefulColors();
         invalidate();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index ff4b835..d97e25f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -473,7 +473,7 @@
                     result.newPublicView = createSensitiveContentMessageNotification(
                             NotificationBundleUi.isEnabled()
                                     ? row.getEntryAdapter().getSbn().getNotification()
-                                    : row.getEntry().getSbn().getNotification(),
+                                    : row.getEntryLegacy().getSbn().getNotification(),
                             builder.getStyle(),
                             systemUiContext, packageContext).createContentView();
                 } else {
@@ -814,7 +814,7 @@
                     existingWrapper.onReinflated();
                 }
             } catch (Exception e) {
-                handleInflationError(runningInflations, e, row, callback, logger,
+                handleInflationError(runningInflations, e, row, entry, callback, logger,
                         "applying view synchronously");
                 // Add a running inflation to make sure we don't trigger callbacks.
                 // Safe to do because only happens in tests.
@@ -836,7 +836,7 @@
                 String invalidReason = isValidView(v, entry, row.getResources());
                 if (invalidReason != null) {
                     handleInflationError(runningInflations, new InflationException(invalidReason),
-                            row, callback, logger, "applied invalid view");
+                            row, entry, callback, logger, "applied invalid view");
                     runningInflations.remove(inflationId);
                     return;
                 }
@@ -873,7 +873,7 @@
                     onViewApplied(newView);
                 } catch (Exception anotherException) {
                     runningInflations.remove(inflationId);
-                    handleInflationError(runningInflations, e, row,
+                    handleInflationError(runningInflations, e, row, entry,
                             callback, logger, "applying view");
                 }
             }
@@ -969,13 +969,14 @@
 
     private static void handleInflationError(
             HashMap<Integer, CancellationSignal> runningInflations, Exception e,
-            ExpandableNotificationRow row, @Nullable InflationCallback callback,
+            ExpandableNotificationRow row, NotificationEntry entry,
+            @Nullable InflationCallback callback,
             NotificationRowContentBinderLogger logger, String logContext) {
         Assert.isMainThread();
         logger.logAsyncTaskException(row.getLoggingKey(), logContext, e);
         runningInflations.values().forEach(CancellationSignal::cancel);
         if (callback != null) {
-            callback.handleInflationException(row.getEntry(), e);
+            callback.handleInflationException(entry, e);
         }
     }
 
@@ -1443,7 +1444,7 @@
                     + Integer.toHexString(sbn.getId());
             Log.e(CentralSurfaces.TAG, "couldn't inflate view for notification " + ident, e);
             if (mCallback != null) {
-                mCallback.handleInflationException(mRow.getEntry(),
+                mCallback.handleInflationException(mEntry,
                         new InflationException("Couldn't inflate contentViews" + e));
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b3357d0..26d318b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -269,6 +269,7 @@
         mNotificationMaxHeight = maxHeight;
     }
 
+    // This logic is mirrored in FrameLayoutWithMaxHeight.onMeasure in AODPromotedNotification.kt.
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -600,7 +601,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return mContainingNotification.getEntryAdapter().getSbn();
         } else {
-            return mContainingNotification.getEntry().getSbn();
+            return mContainingNotification.getEntryLegacy().getSbn();
         }
     }
 
@@ -1592,10 +1593,13 @@
             return;
         }
         ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
-        View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
-        ViewGroup actionListMarginTarget = layout.findViewById(
-                com.android.internal.R.id.notification_action_list_margin_target);
-        if (bubbleButton == null || actionContainer == null) {
+        // With the new design, the actions_container should always be visible to act as padding
+        // when there are no actions. We're making its child visible/invisible instead.
+        View actionsContainerForVisibilityChange = layout.findViewById(
+                notificationsRedesignTemplates()
+                        ? com.android.internal.R.id.actions_container_layout
+                        : com.android.internal.R.id.actions_container);
+        if (bubbleButton == null || actionsContainerForVisibilityChange == null) {
             return;
         }
 
@@ -1613,17 +1617,14 @@
             bubbleButton.setImageDrawable(d);
             bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
             bubbleButton.setVisibility(VISIBLE);
-            actionContainer.setVisibility(VISIBLE);
-            // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
-            if (actionListMarginTarget != null) {
-                removeBottomMargin(actionListMarginTarget);
-            }
-            if (notificationsRedesignTemplates()) {
-                // Similar treatment for smart reply margin
-                LinearLayout smartReplyContainer = layout.findViewById(
-                        com.android.internal.R.id.smart_reply_container);
-                if (smartReplyContainer != null) {
-                    removeBottomMargin(smartReplyContainer);
+            actionsContainerForVisibilityChange.setVisibility(VISIBLE);
+            if (!notificationsRedesignTemplates()) {
+                // Set notification_action_list_margin_target's bottom margin to 0 when showing
+                // bubble
+                ViewGroup actionListMarginTarget = layout.findViewById(
+                        com.android.internal.R.id.notification_action_list_margin_target);
+                if (actionListMarginTarget != null) {
+                    removeBottomMargin(actionListMarginTarget);
                 }
             }
         } else  {
@@ -1664,8 +1665,13 @@
             return;
         }
         ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button);
-        View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
-        if (snoozeButton == null || actionContainer == null) {
+        // With the new design, the actions_container should always be visible to act as padding
+        // when there are no actions. We're making its child visible/invisible instead.
+        View actionsContainerForVisibilityChange = layout.findViewById(
+                notificationsRedesignTemplates()
+                        ? com.android.internal.R.id.actions_container_layout
+                        : com.android.internal.R.id.actions_container);
+        if (snoozeButton == null || actionsContainerForVisibilityChange == null) {
             return;
         }
         // Notification.Builder can 'disable' the snooze button to prevent it from being shown here
@@ -1691,7 +1697,7 @@
         snoozeButton.setOnClickListener(
                 mContainingNotification.getSnoozeClickListener(snoozeMenuItem));
         snoozeButton.setVisibility(VISIBLE);
-        actionContainer.setVisibility(VISIBLE);
+        actionsContainerForVisibilityChange.setVisibility(VISIBLE);
     }
 
     private void applySmartReplyView() {
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 9a75253..cdb78d9 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
@@ -47,7 +47,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +70,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -100,6 +100,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final HighPriorityProvider mHighPriorityProvider;
     private final ChannelEditorDialogController mChannelEditorDialogController;
+    private final PackageDemotionInteractor mPackageDemotionInteractor;
     private final OnUserInteractionCallback mOnUserInteractionCallback;
 
     // Dependencies:
@@ -155,6 +156,7 @@
             LauncherApps launcherApps,
             ShortcutManager shortcutManager,
             ChannelEditorDialogController channelEditorDialogController,
+            PackageDemotionInteractor packageDemotionInteractor,
             UserContextProvider contextTracker,
             AssistantFeedbackController assistantFeedbackController,
             Optional<BubblesManager> bubblesManagerOptional,
@@ -184,6 +186,7 @@
         mShortcutManager = shortcutManager;
         mContextTracker = contextTracker;
         mChannelEditorDialogController = channelEditorDialogController;
+        mPackageDemotionInteractor = packageDemotionInteractor;
         mAssistantFeedbackController = assistantFeedbackController;
         mBubblesManagerOptional = bubblesManagerOptional;
         mUiEventLogger = uiEventLogger;
@@ -231,15 +234,6 @@
         }
     }
 
-    public void onDensityOrFontScaleChanged(NotificationEntry entry) {
-        if (!Flags.notificationUndoGutsOnConfigChanged()) {
-            Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
-                    + " notificationUndoGutsOnConfigChanged is off");
-        }
-        setExposedGuts(entry.getGuts());
-        bindGuts(entry.getRow());
-    }
-
     /**
      * Sends an intent to open the notification settings for a particular package and optional
      * channel.
@@ -291,11 +285,6 @@
         mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
     }
 
-    private boolean bindGuts(final ExpandableNotificationRow row) {
-        row.ensureGutsInflated();
-        return bindGuts(row, mGutsMenuItem);
-    }
-
     @VisibleForTesting
     protected boolean bindGuts(final ExpandableNotificationRow row,
             NotificationMenuRowPlugin.MenuItem item) {
@@ -429,6 +418,7 @@
                 mIconStyleProvider,
                 mOnUserInteractionCallback,
                 mChannelEditorDialogController,
+                mPackageDemotionInteractor,
                 packageName,
                 row.getEntry().getChannel(),
                 row.getEntry(),
@@ -440,6 +430,7 @@
                 NotificationBundleUi.isEnabled()
                         ? !row.getEntry().isBlockable()
                         : row.getIsNonblockable(),
+                row.canViewBeDismissed(),
                 mHighPriorityProvider.isHighPriority(row.getEntry()),
                 mAssistantFeedbackController,
                 mMetricsLogger,
@@ -605,6 +596,7 @@
         return mNotificationGutsExposed;
     }
 
+    @VisibleForTesting
     public void setExposedGuts(NotificationGuts guts) {
         mNotificationGutsExposed = guts;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 6611225..b6f4ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -74,6 +74,7 @@
 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.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 
@@ -120,6 +121,7 @@
     private boolean mIsAutomaticChosen;
     private boolean mIsSingleDefaultChannel;
     private boolean mIsNonblockable;
+    private boolean mIsDismissable;
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
@@ -160,6 +162,7 @@
         mPressedApply = true;
         mGutsContainer.closeControls(v, /* save= */ true);
     };
+    private OnClickListener mOnCloseClickListener;
 
     public NotificationInfo(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -193,6 +196,7 @@
             NotificationIconStyleProvider iconStyleProvider,
             OnUserInteractionCallback onUserInteractionCallback,
             ChannelEditorDialogController channelEditorDialogController,
+            PackageDemotionInteractor packageDemotionInteractor,
             String pkg,
             NotificationChannel notificationChannel,
             NotificationEntry entry,
@@ -202,6 +206,7 @@
             UiEventLogger uiEventLogger,
             boolean isDeviceProvisioned,
             boolean isNonblockable,
+            boolean isDismissable,
             boolean wasShownHighPriority,
             AssistantFeedbackController assistantFeedbackController,
             MetricsLogger metricsLogger,
@@ -226,11 +231,13 @@
         mStartingChannelImportance = mSingleNotificationChannel.getImportance();
         mWasShownHighPriority = wasShownHighPriority;
         mIsNonblockable = isNonblockable;
+        mIsDismissable = isDismissable;
         mAppUid = mSbn.getUid();
         mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
         mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled();
         mUiEventLogger = uiEventLogger;
+        mOnCloseClickListener = onCloseClick;
 
         mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class)
                 && mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid());
@@ -277,6 +284,11 @@
         turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
                 ? VISIBLE : GONE);
 
+        View dismissButton = findViewById(R.id.inline_dismiss);
+        dismissButton.setOnClickListener(mOnCloseClickListener);
+        dismissButton.setVisibility(dismissButton.hasOnClickListeners() && mIsDismissable
+                ? VISIBLE : GONE);
+
         View done = findViewById(R.id.done);
         done.setOnClickListener(mOnDismissSettings);
         done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 2f94d32..ae52db8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -541,7 +541,7 @@
             val ident: String = (sbn.packageName + "/0x" + Integer.toHexString(sbn.id))
             Log.e(TAG, "couldn't inflate view for notification $ident", e)
             callback?.handleInflationException(
-                if (NotificationBundleUi.isEnabled) entry else row.entry,
+                if (NotificationBundleUi.isEnabled) entry else row.entryLegacy,
                 InflationException("Couldn't inflate contentViews$e"),
             )
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 83897f5..cec0ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,7 +51,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Flags;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.res.R;
@@ -477,7 +476,7 @@
 
     @Override
     public boolean handleCloseControls(boolean save, boolean force) {
-        if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+        if (!save) {
             // Undo changes and let the guts handle closing the view
             mSelectedOption = null;
             showSnoozeOptions(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 6ff711d..01ee788 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -31,6 +31,7 @@
 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.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 
@@ -42,6 +43,7 @@
 public class PromotedNotificationInfo extends NotificationInfo {
     private static final String TAG = "PromotedNotifInfoGuts";
     private INotificationManager mNotificationManager;
+    private PackageDemotionInteractor mPackageDemotionInteractor;
     private NotificationGuts mGutsContainer;
 
     public PromotedNotificationInfo(Context context, AttributeSet attrs) {
@@ -56,6 +58,7 @@
             NotificationIconStyleProvider iconStyleProvider,
             OnUserInteractionCallback onUserInteractionCallback,
             ChannelEditorDialogController channelEditorDialogController,
+            PackageDemotionInteractor packageDemotionInteractor,
             String pkg,
             NotificationChannel notificationChannel,
             NotificationEntry entry,
@@ -65,16 +68,19 @@
             UiEventLogger uiEventLogger,
             boolean isDeviceProvisioned,
             boolean isNonblockable,
+            boolean isDismissable,
             boolean wasShownHighPriority,
             AssistantFeedbackController assistantFeedbackController,
             MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
         super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider,
-                onUserInteractionCallback, channelEditorDialogController, pkg, notificationChannel,
+                onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor,
+                pkg, notificationChannel,
                 entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger,
-                isDeviceProvisioned, isNonblockable, wasShownHighPriority,
+                isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority,
                 assistantFeedbackController, metricsLogger, onCloseClick);
 
         mNotificationManager = iNotificationManager;
+        mPackageDemotionInteractor = packageDemotionInteractor;
 
         bindDemote(entry.getSbn(), pkg);
     }
@@ -94,8 +100,8 @@
     private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
         return ((View v) -> {
             try {
-                // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
                 mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+                mPackageDemotionInteractor.onPackageDemoted(packageName, sbn.getUid());
                 mGutsContainer.closeControls(v, true);
             } catch (RemoteException e) {
                 Log.e(TAG, "Couldn't revoke live update permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index 4082a5b..2c5b9f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -61,7 +61,7 @@
         row: ExpandableNotificationRow,
         context: Context,
     ): NotificationIconProvider {
-        val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else  row.entry.sbn
+        val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entryLegacy.sbn
         if (sbn == null) {
             return object : NotificationIconProvider {
                 override fun shouldShowAppIcon(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index f492b25..e266dad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -50,7 +50,7 @@
         resolveViews();
         updateImageTag(NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn()
-                : row.getEntry().getSbn());
+                : row.getEntryLegacy().getSbn());
     }
 
     private void resolveViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index dec674c..71bb9a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -47,7 +47,7 @@
         // the transformation types and we need to have our values set by then.
         resolveViews(NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn()
-                : row.getEntry().getSbn());
+                : row.getEntryLegacy().getSbn());
         super.onContentUpdated(row);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 585051a..e6dadcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -225,7 +225,7 @@
         super.onContentUpdated(row);
         mIsLowPriority = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().isAmbient()
-                : row.getEntry().isAmbient();
+                : row.getEntryLegacy().isAmbient();
         mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
 
@@ -236,7 +236,7 @@
         updateCropToPaddingForImageViews();
         Notification n = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn().getNotification()
-                : row.getEntry().getSbn().getNotification();
+                : row.getEntryLegacy().getSbn().getNotification();
         mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
 
         // We need to reset all views that are no longer transforming in case a view was previously
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 99db1db..19321dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -327,7 +327,7 @@
         // the transformation types and we need to have our values set by then.
         resolveTemplateViews(NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn()
-                : row.getEntry().getSbn());
+                : row.getEntryLegacy().getSbn());
         super.onContentUpdated(row);
         // With the modern templates, a large icon visually overlaps the header, so we can't
         // hide the header, we must show it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 64babb2..35e286c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -83,7 +83,7 @@
             if (NotificationBundleUi.isEnabled()
                     ? row.getEntryAdapter().getSbn().getNotification().isStyle(
                     Notification.DecoratedCustomViewStyle.class)
-                    : row.getEntry().getSbn().getNotification().isStyle(
+                    : row.getEntryLegacy().getSbn().getNotification().isStyle(
                     Notification.DecoratedCustomViewStyle.class)) {
                 return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
             }
@@ -141,7 +141,7 @@
         // Apps targeting Q should fix their dark mode bugs.
         int targetSdk = NotificationBundleUi.isEnabled()
                 ? mRow.getEntryAdapter().getTargetSdk()
-                : mRow.getEntry().targetSdk;
+                : mRow.getEntryLegacy().targetSdk;
         if (targetSdk >= Build.VERSION_CODES.Q) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 315d37e..f9d8c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -444,7 +444,7 @@
         mIsConversation = isConversation;
         StatusBarNotification notification = NotificationBundleUi.isEnabled()
                 ? mContainingNotification.getEntryAdapter().getSbn()
-                : mContainingNotification.getEntry().getSbn();
+                : mContainingNotification.getEntryLegacy().getSbn();
         if (notification == null) {
             return;
         }
@@ -615,7 +615,7 @@
         RemoteViews header;
         StatusBarNotification notification = NotificationBundleUi.isEnabled()
                 ? mContainingNotification.getEntryAdapter().getSbn()
-                : mContainingNotification.getEntry().getSbn();
+                : mContainingNotification.getEntryLegacy().getSbn();
         if (notification == null) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 96f0e6f..b5562ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -167,7 +167,7 @@
             view === promoHeaderView -> BUCKET_PROMO
             view is ExpandableNotificationRow ->
                 if (NotificationBundleUi.isEnabled) view.entryAdapter?.sectionBucket
-                else view.entry.bucket
+                else view.entryLegacy.bucket
             else -> null
         }
 
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 e6bb1b9..9fea750 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
@@ -64,6 +64,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.MathUtils;
+import android.util.Pair;
 import android.view.DisplayCutout;
 import android.view.InputDevice;
 import android.view.LayoutInflater;
@@ -140,6 +141,7 @@
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsProxy;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.ColorUtilKt;
 import com.android.systemui.util.DumpUtilsKt;
@@ -1037,10 +1039,10 @@
                 }
                 int bucket = NotificationBundleUi.isEnabled()
                         ? row.getEntryAdapter().getSectionBucket()
-                        : row.getEntry().getBucket();
+                        : row.getEntryLegacy().getBucket();
                 boolean isAmbient = NotificationBundleUi.isEnabled()
                         ? row.getEntryAdapter().isAmbient()
-                        : row.getEntry().isAmbient();
+                        : row.getEntryLegacy().isAmbient();
                 currentIndex++;
                 boolean beforeSpeedBump;
                 if (mHighPriorityBeforeSpeedBump) {
@@ -1845,7 +1847,7 @@
         } else {
             if (row.isChildInGroup()) {
                 final NotificationEntry groupSummary =
-                        mGroupMembershipManager.getGroupSummary(row.getEntry());
+                        mGroupMembershipManager.getGroupSummary(row.getEntryLegacy());
                 if (groupSummary != null) {
                     row = groupSummary.getRow();
                 }
@@ -1998,16 +2000,16 @@
             if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow row) {
-                    NotificationEntry entry = row.getEntry();
                     boolean isEntrySummaryForTopHun;
                     if (NotificationBundleUi.isEnabled()) {
                         isEntrySummaryForTopHun = Objects.equals(
                                 ((ExpandableNotificationRow) slidingChild).getNotificationParent(),
                                 mTopHeadsUpRow);
                     } else {
+                        NotificationEntry entry = row.getEntryLegacy();
                         isEntrySummaryForTopHun = mTopHeadsUpRow != null &&
-                                mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
-                                == entry;
+                                mGroupMembershipManager.getGroupSummary(
+                                        mTopHeadsUpRow.getEntryLegacy()) == entry;
                     }
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
                             && mTopHeadsUpRow != row
@@ -3007,7 +3009,7 @@
             ExpandableNotificationRow childRow = (ExpandableNotificationRow) child;
             return NotificationBundleUi.isEnabled()
                     ? mGroupMembershipManager.isChildInGroup(childRow.getEntryAdapter())
-                    : mGroupMembershipManager.isChildInGroup(childRow.getEntry());
+                    : mGroupMembershipManager.isChildInGroup(childRow.getEntryLegacy());
         }
         return false;
     }
@@ -3852,8 +3854,6 @@
                         // existing overScroll, we have to scroll the view
                         customOverScrollBy((int) scrollAmount, getOwnScrollY(),
                                 range, getHeight() / 2);
-                        // If we're scrolling, leavebehinds should be dismissed
-                        mController.checkSnoozeLeavebehind();
                     }
                 }
                 break;
@@ -6002,7 +6002,6 @@
      *                 LockscreenShadeTransitionController resets fraction to 0
      *                 where it remains until the next lockscreen-to-shade transition.
      */
-    @Override
     public void setFractionToShade(float fraction) {
         mAmbientState.setFractionToShade(fraction);
         updateContentHeight();  // Recompute stack height with different section gap.
@@ -6474,7 +6473,7 @@
             @SelectedRows int selection) {
         int bucket = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSectionBucket()
-                : row.getEntry().getBucket();
+                : row.getEntryLegacy().getBucket();
         switch (selection) {
             case ROWS_ALL:
                 return true;
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 f7f8acf..f3d8ee2 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
@@ -648,11 +648,11 @@
                 public void onChildSnappedBack(View animView, float targetLeft) {
                     mView.onSwipeEnd();
                     if (animView instanceof ExpandableNotificationRow row) {
-                        if (row.isPinned() && !canChildBeDismissed(row)
-                                && NotificationBundleUi.isEnabled()
+                        boolean cannotFullScreen = NotificationBundleUi.isEnabled()
                                 ? !row.getEntryAdapter().isFullScreenCapable()
-                                : (row.getEntry().getSbn().getNotification().fullScreenIntent
-                                        == null)) {
+                                : (row.getEntryLegacy().getSbn().getNotification().fullScreenIntent
+                                        == null);
+                        if (row.isPinned() && !canChildBeDismissed(row) && cannotFullScreen) {
                             mHeadsUpManager.removeNotification(
                                     row.getKey(),
                                     /* removeImmediately= */ true,
@@ -1735,7 +1735,6 @@
      *                 they remain until the next lockscreen-to-shade transition.
      */
     public void setTransitionToFullShadeAmount(float fraction) {
-        SceneContainerFlag.assertInLegacyMode();
         mView.setFractionToShade(fraction);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index fcb63df..e5071d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -413,7 +413,7 @@
                         (currentNotification as? ExpandableNotificationRow)?.entryAdapter
                     counter.incrementForBucket(entryAdapter?.sectionBucket)
                 } else {
-                    val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+                    val entry = (currentNotification as? ExpandableNotificationRow)?.entryLegacy
                     counter.incrementForBucket(entry?.bucket)
                 }
             }
@@ -470,7 +470,7 @@
             calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
         val canPeek = view is ExpandableNotificationRow &&
                 if (NotificationBundleUi.isEnabled) view.entryAdapter?.canPeek() == true
-                else view.entry.isStickyAndNotDemoted
+                else view.entryLegacy.isStickyAndNotDemoted
 
         var size =
             if (onLockscreen) {
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 2821822..da14423 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
@@ -927,7 +927,7 @@
                             childState.headsUpIsVisible, row.showingPulsing(),
                             ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
                                     ? row.getEntryAdapter().canPeek()
-                                    : row.getEntry().isStickyAndNotDemoted())) {
+                                    : row.getEntryLegacy().isStickyAndNotDemoted())) {
                         // the height of this child before clamping it to the top
                         float unmodifiedChildHeight = childState.height;
                         clampHunToTop(
@@ -984,7 +984,7 @@
                             childState.headsUpIsVisible, row.showingPulsing(),
                             ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
                                     ? row.getEntryAdapter().canPeek()
-                                    : row.getEntry().isStickyAndNotDemoted())) {
+                                    : row.getEntryLegacy().isStickyAndNotDemoted())) {
                         // Ensure that the heads up is always visible even when scrolled off.
                         // NSSL y starts at top of screen in non-split-shade, but below the qs
                         // offset
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index ac89f3a..9c855e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -107,9 +107,6 @@
     /** sets the current expand fraction */
     fun setExpandFraction(expandFraction: Float)
 
-    /** Sets the fraction of the LockScreen -> Shade transition. */
-    fun setFractionToShade(fraction: Float)
-
     /** sets the current QS expand fraction */
     fun setQsExpandFraction(expandFraction: Float)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 40739b3..653344a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -95,11 +95,6 @@
                     view.setExpandFraction(it.coerceIn(0f, 1f))
                 }
             }
-            launch {
-                viewModel.lockScreenToShadeTransitionProgress.collectTraced {
-                    view.setFractionToShade(it.coerceIn(0f, 1f))
-                }
-            }
             launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
             launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) }
             launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 940b2e5..c1aa5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -207,44 +207,6 @@
     val qsExpandFraction: Flow<Float> =
         shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
 
-    /**
-     * Fraction of the LockScreen -> Shade transition. 0..1 while the transition in progress, and
-     * snaps back to 0 when it is Idle.
-     */
-    val lockScreenToShadeTransitionProgress: Flow<Float> =
-        combine(
-                shadeInteractor.shadeExpansion,
-                shadeModeInteractor.shadeMode,
-                sceneInteractor.transitionState,
-            ) { shadeExpansion, _, transitionState ->
-                when (transitionState) {
-                    is Idle -> 0f
-                    is ChangeScene ->
-                        if (
-                            transitionState.isTransitioning(
-                                from = Scenes.Lockscreen,
-                                to = Scenes.Shade,
-                            )
-                        ) {
-                            shadeExpansion
-                        } else {
-                            0f
-                        }
-
-                    is Transition.OverlayTransition ->
-                        if (
-                            transitionState.currentScene == Scenes.Lockscreen &&
-                                transitionState.isTransitioning(to = Overlays.NotificationsShade)
-                        ) {
-                            shadeExpansion
-                        } else {
-                            0f
-                        }
-                }
-            }
-            .distinctUntilChanged()
-            .dumpWhileCollecting("lockScreenToShadeTransitionProgress")
-
     val isOccluded: Flow<Boolean> =
         bouncerInteractor.bouncerExpansion
             .map { it == 1f }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index bc53314..afe7971 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -60,7 +60,7 @@
                         if (animationsEnabled) {
                             added.forEach { key ->
                                 val row = obtainView(key)
-                                val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+                                val hasStatusBarChip = statusBarChips.contains(row.key)
                                 parentView.generateHeadsUpAnimation(
                                     row,
                                     /* isHeadsUp = */ true,
@@ -69,7 +69,7 @@
                             }
                             removed.forEach { key ->
                                 val row = obtainView(key)
-                                val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+                                val hasStatusBarChip = statusBarChips.contains(row.key)
                                 if (!parentView.isBeingDragged()) {
                                     parentView.generateHeadsUpAnimation(
                                         row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74a42ef..f3d7202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,6 +29,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.content.Context;
 import android.graphics.Color;
 import android.os.Handler;
 import android.util.Log;
@@ -226,6 +227,8 @@
 
     private ScrimState mState = ScrimState.UNINITIALIZED;
 
+    private Context mContext;
+
     private ScrimView mScrimInFront;
     private ScrimView mNotificationsScrim;
     private ScrimView mScrimBehind;
@@ -365,7 +368,9 @@
             @Main CoroutineDispatcher mainDispatcher,
             LargeScreenShadeInterpolator largeScreenShadeInterpolator,
             BlurConfig blurConfig,
+            @Main Context context,
             Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor) {
+        mContext = context;
         mScrimStateListener = lightBarController::setScrimState;
         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
         mBlurConfig = blurConfig;
@@ -1627,16 +1632,16 @@
 
     private void updateThemeColors() {
         if (mScrimBehind == null) return;
-        int background = mScrimBehind.getContext().getColor(
+        int background = mContext.getColor(
                 com.android.internal.R.color.materialColorSurfaceDim);
-        int accent = mScrimBehind.getContext().getColor(
+        int accent = mContext.getColor(
                 com.android.internal.R.color.materialColorPrimary);
         mColors.setMainColor(background);
         mColors.setSecondaryColor(accent);
         final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
         mColors.setSupportsDarkText(isBackgroundLight);
 
-        int surface = mScrimBehind.getContext().getColor(
+        int surface = mContext.getColor(
                 com.android.internal.R.color.materialColorSurface);
         for (ScrimState state : ScrimState.values()) {
             state.setSurfaceColor(surface);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 05a46cd..8389aab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -219,7 +219,7 @@
                     if (NotificationBundleUi.isEnabled()) {
                         mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
                     } else {
-                        mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+                        mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
                     }
                 } else if (!row.isChildInGroup()) {
                     final boolean expandNotification;
@@ -241,7 +241,7 @@
                     if (NotificationBundleUi.isEnabled()) {
                         mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
                     } else {
-                        mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+                        mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index ba66651..2f9cff4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -196,9 +196,8 @@
 
                                 setContent {
                                     PlatformTheme {
-                                        val chipsVisibilityModel by
+                                        val chipsVisibilityModel =
                                             statusBarViewModel.ongoingActivityChips
-                                                .collectAsStateWithLifecycle()
                                         if (chipsVisibilityModel.areChipsAllowed) {
                                             OngoingActivityChips(
                                                 chips = chipsVisibilityModel.chips,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index c717b18..540baba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -21,6 +21,8 @@
 import android.view.Display
 import android.view.View
 import androidx.compose.runtime.getValue
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.TrackGroupUtils.trackGroup
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -130,7 +132,7 @@
     val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
 
     /** All supported activity chips, whether they are currently active or not. */
-    val ongoingActivityChips: StateFlow<ChipsVisibilityModel>
+    val ongoingActivityChips: ChipsVisibilityModel
 
     /**
      * The multiple ongoing activity chips that should be shown on the left-hand side of the status
@@ -386,11 +388,9 @@
         }
 
     override val isHomeStatusBarAllowed =
-        isHomeStatusBarAllowedCompat.stateIn(
-            bgScope,
-            SharingStarted.WhileSubscribed(),
-            initialValue = false,
-        )
+        isHomeStatusBarAllowedCompat
+            .traceEach(trackGroup(TRACK_GROUP, "isHomeStatusBarAllowed"), logcat = true)
+            .stateIn(bgScope, SharingStarted.WhileSubscribed(), initialValue = false)
 
     private val shouldHomeStatusBarBeVisible =
         combine(
@@ -461,24 +461,29 @@
             isHomeStatusBarAllowed && !isSecureCameraActive && !hideStartSideContentForHeadsUp
         }
 
-    override val ongoingActivityChips =
+    private val chipsVisibilityModel: Flow<ChipsVisibilityModel> =
         combine(ongoingActivityChipsViewModel.chips, canShowOngoingActivityChips) { chips, canShow
                 ->
                 ChipsVisibilityModel(chips, areChipsAllowed = canShow)
             }
-            .stateIn(
-                bgScope,
-                SharingStarted.WhileSubscribed(),
-                initialValue =
-                    ChipsVisibilityModel(
-                        chips = MultipleOngoingActivityChipsModel(),
-                        areChipsAllowed = false,
-                    ),
-            )
+            .traceEach(trackGroup(TRACK_GROUP, "chips"), logcat = true) {
+                "Chips[allowed=${it.areChipsAllowed} numChips=${it.chips.active.size}]"
+            }
+
+    override val ongoingActivityChips: ChipsVisibilityModel by
+        hydrator.hydratedStateOf(
+            traceName = "ongoingActivityChips",
+            initialValue =
+                ChipsVisibilityModel(
+                    chips = MultipleOngoingActivityChipsModel(),
+                    areChipsAllowed = false,
+                ),
+            source = chipsVisibilityModel,
+        )
 
     private val hasOngoingActivityChips =
         if (StatusBarChipsModernization.isEnabled) {
-            ongoingActivityChips.map { it.chips.active.any { chip -> !chip.isHidden } }
+            chipsVisibilityModel.map { it.chips.active.any { chip -> !chip.isHidden } }
         } else if (StatusBarNotifChips.isEnabled) {
             ongoingActivityChipsLegacy.map { it.primary is OngoingActivityChipModel.Active }
         } else {
@@ -607,6 +612,8 @@
         private const val COL_PREFIX_NOTIF_CONTAINER = "notifContainer"
         private const val COL_PREFIX_SYSTEM_INFO = "systemInfo"
 
+        private const val TRACK_GROUP = "StatusBar"
+
         fun tableLogBufferName(displayId: Int) = "HomeStatusBarViewModel[$displayId]"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index b13e01b..fa022b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -56,8 +57,8 @@
     private int mDeviceState = -1;
     @Nullable
     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
-    private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
-            mDeviceStateRotationLockSettingsListener;
+    private DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+            mDeviceStateAutoRotateSettingListener;
 
     @Inject
     public DeviceStateRotationLockSettingController(
@@ -83,17 +84,17 @@
             // is no user action.
             mDeviceStateCallback = this::updateDeviceState;
             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
-            mDeviceStateRotationLockSettingsListener = () ->
+            mDeviceStateAutoRotateSettingListener = () ->
                     readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
             mDeviceStateRotationLockSettingsManager.registerListener(
-                    mDeviceStateRotationLockSettingsListener);
+                    mDeviceStateAutoRotateSettingListener);
         } else {
             if (mDeviceStateCallback != null) {
                 mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
             }
-            if (mDeviceStateRotationLockSettingsListener != null) {
+            if (mDeviceStateAutoRotateSettingListener != null) {
                 mDeviceStateRotationLockSettingsManager.unregisterListener(
-                        mDeviceStateRotationLockSettingsListener);
+                        mDeviceStateAutoRotateSettingListener);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
index 47e27bc..1cc7a31 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -50,6 +50,7 @@
                 onScreenTurningOnToOnDrawnMs,
                 onDrawnToOnScreenTurnedOnMs,
                 trackingResult,
+                screenWakelockstatus
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 66de522..5800d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -344,6 +344,8 @@
         val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN,
         val trackingResult: Int =
             SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__UNKNOWN_RESULT,
+        val screenWakelockstatus: Int =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_UNKNOWN,
     )
 
     enum class TrackingResult {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1bc3096..9475bdb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2746,7 +2746,7 @@
                     () -> mJavaAdapter,
                     () -> mSceneInteractor,
                     () -> mCommunalSceneInteractor,
-                    mKeyguardServiceShowLockscreenInteractor);
+                    () -> mKeyguardServiceShowLockscreenInteractor);
             setAlternateBouncerVisibility(false);
             setPrimaryBouncerVisibility(false);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 5c893da..6a4f3da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -44,6 +44,7 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -77,6 +78,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.Optional;
+import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -158,6 +160,31 @@
      * is false are removed.[
      */
     private void initController() {
+        IntentCreator fakeIntentCreator = new IntentCreator() {
+            @Override
+            public Intent getTextEditorIntent(Context context) {
+                return new Intent();
+            }
+
+            @Override
+            public Intent getShareIntent(ClipData clipData, Context context) {
+                Intent intent = Intent.createChooser(new Intent(Intent.ACTION_SEND), null);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                return intent;
+            }
+
+            @Override
+            public void getImageEditIntentAsync(Uri uri, Context context,
+                    Consumer<Intent> outputConsumer) {
+                outputConsumer.accept(new Intent(Intent.ACTION_EDIT));
+            }
+
+            @Override
+            public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+                return new Intent();
+            }
+        };
+
         mOverlayController = new ClipboardOverlayController(
                 mContext,
                 mClipboardOverlayView,
@@ -171,7 +198,7 @@
                 mClipboardTransitionExecutor,
                 mClipboardIndicationProvider,
                 mUiEventLogger,
-                new ActionIntentCreator());
+                fakeIntentCreator);
         verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
         mCallbacks = mOverlayCallbacksCaptor.getValue();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index fb70846..061f798 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -134,7 +134,6 @@
     @Mock private RingerModeTracker mRingerModeTracker;
     @Mock private RingerModeLiveData mRingerModeLiveData;
     @Mock private PackageManager mPackageManager;
-    @Mock private Handler mHandler;
     @Mock private UserContextProvider mUserContextProvider;
     @Mock private VibratorHelper mVibratorHelper;
     @Mock private ShadeController mShadeController;
@@ -148,6 +147,7 @@
     private TestableLooper mTestableLooper;
     private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     private GlobalActionsInteractor mInteractor;
+    private Handler mHandler;
 
     @Before
     public void setUp() throws Exception {
@@ -166,6 +166,7 @@
         mGlobalSettings = new FakeGlobalSettings();
         mSecureSettings = new FakeSettings();
         mInteractor = mKosmos.getGlobalActionsInteractor();
+        mHandler = new Handler(mTestableLooper.getLooper());
 
         mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
                 mWindowManagerFuncs,
@@ -771,6 +772,31 @@
         mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
     }
 
+    @Test
+    public void userSwitching_dismissDialog() {
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mResources)
+                .getStringArray(com.android.internal.R.array.config_globalActionsList);
+
+        mGlobalActionsDialogLite.showOrHideDialog(false, true, null, Display.DEFAULT_DISPLAY);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mGlobalActionsDialogLite.mDialog.isShowing()).isTrue();
+
+        ArgumentCaptor<UserTracker.Callback> captor =
+                ArgumentCaptor.forClass(UserTracker.Callback.class);
+
+        verify(mUserTracker).addCallback(captor.capture(), any());
+
+        captor.getValue().onBeforeUserSwitching(100);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mGlobalActionsDialogLite.mDialog).isNull();
+    }
+
     private UserInfo mockCurrentUser(int flags) {
         return new UserInfo(10, "A User", flags);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index f394c80..8f1d07b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -855,6 +855,20 @@
 
     @Test
     fun contentDescriptionUpdated() {
+        var elapsedTimeDesc: CharSequence? = null
+        var durationDesc: CharSequence? = null
+        val listener =
+            object : SeekBarViewModel.ContentDescriptionListener {
+                override fun onContentDescriptionChanged(
+                    elapsedTimeDescription: CharSequence,
+                    durationDescription: CharSequence,
+                ) {
+                    elapsedTimeDesc = elapsedTimeDescription
+                    durationDesc = durationDescription
+                }
+            }
+        viewModel.setContentDescriptionListener(listener)
+
         // When there is a duration and position
         val duration = (1.5 * 60 * 60 * 1000).toLong()
         val metadata =
@@ -875,9 +889,7 @@
         viewModel.updateController(mockController)
         fakeExecutor.runNextReady()
 
-        // Then the content description is set
-        val result = viewModel.progress.value!!
-
+        // Then the content description listener gets an update
         val expectedProgress =
             MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
                 .formatMeasures(Measure(3, MeasureUnit.SECOND))
@@ -888,7 +900,7 @@
                     Measure(30, MeasureUnit.MINUTE),
                     Measure(0, MeasureUnit.SECOND),
                 )
-        assertThat(result.durationDescription).isEqualTo(expectedDuration)
-        assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+        assertThat(elapsedTimeDesc).isEqualTo(expectedProgress)
+        assertThat(durationDesc).isEqualTo(expectedDuration)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 92b26ea..7e42ec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -30,6 +30,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -55,19 +56,21 @@
         listState: EditTileListState,
         onSetTiles: (List<TileSpec>) -> Unit,
     ) {
-        DefaultEditTileGrid(
-            listState = listState,
-            otherTiles = listOf(),
-            columns = 4,
-            largeTilesSpan = 4,
-            modifier = Modifier.fillMaxSize(),
-            onAddTile = {},
-            onRemoveTile = {},
-            onSetTiles = onSetTiles,
-            onResize = { _, _ -> },
-            onStopEditing = {},
-            onReset = null,
-        )
+        PlatformTheme {
+            DefaultEditTileGrid(
+                listState = listState,
+                otherTiles = listOf(),
+                columns = 4,
+                largeTilesSpan = 4,
+                modifier = Modifier.fillMaxSize(),
+                onAddTile = {},
+                onRemoveTile = {},
+                onSetTiles = onSetTiles,
+                onResize = { _, _ -> },
+                onStopEditing = {},
+                onReset = null,
+            )
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index e76be82c..9d4a425 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -33,6 +33,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -56,19 +57,22 @@
         var tiles by remember { mutableStateOf(TestEditTiles) }
         val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent }
         val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2)
-        DefaultEditTileGrid(
-            listState = listState,
-            otherTiles = otherTiles,
-            columns = 4,
-            largeTilesSpan = 4,
-            modifier = Modifier.fillMaxSize(),
-            onAddTile = { tiles = tiles.add(it) },
-            onRemoveTile = { tiles = tiles.remove(it) },
-            onSetTiles = {},
-            onResize = { _, _ -> },
-            onStopEditing = {},
-            onReset = null,
-        )
+
+        PlatformTheme {
+            DefaultEditTileGrid(
+                listState = listState,
+                otherTiles = otherTiles,
+                columns = 4,
+                largeTilesSpan = 4,
+                modifier = Modifier.fillMaxSize(),
+                onAddTile = { tiles = tiles.add(it) },
+                onRemoveTile = { tiles = tiles.remove(it) },
+                onSetTiles = {},
+                onResize = { _, _ -> },
+                onStopEditing = {},
+                onReset = null,
+            )
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 021be32..5e76000 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -61,19 +62,21 @@
         listState: EditTileListState,
         onResize: (TileSpec, Boolean) -> Unit,
     ) {
-        DefaultEditTileGrid(
-            listState = listState,
-            otherTiles = listOf(),
-            columns = 4,
-            largeTilesSpan = 4,
-            modifier = Modifier.fillMaxSize(),
-            onAddTile = {},
-            onRemoveTile = {},
-            onSetTiles = {},
-            onResize = onResize,
-            onStopEditing = {},
-            onReset = null,
-        )
+        PlatformTheme {
+            DefaultEditTileGrid(
+                listState = listState,
+                otherTiles = listOf(),
+                columns = 4,
+                largeTilesSpan = 4,
+                modifier = Modifier.fillMaxSize(),
+                onAddTile = {},
+                onRemoveTile = {},
+                onSetTiles = {},
+                onResize = onResize,
+                onStopEditing = {},
+                onReset = null,
+            )
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index cf54df8..997cf41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -22,6 +22,7 @@
 import android.platform.test.annotations.EnableFlags
 import android.view.Display
 import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.deviceStateManager
 import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
@@ -37,6 +38,7 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 import org.junit.Before
 import org.junit.Test
+import org.mockito.Mockito.times
 import org.mockito.kotlin.any
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -59,6 +61,7 @@
             fakeRearDisplayStateInteractor,
             kosmos.rearDisplayInnerDialogDelegateFactory,
             kosmos.testScope,
+            kosmos.keyguardUpdateMonitor,
         )
 
     @Before
@@ -96,6 +99,26 @@
             }
         }
 
+    @Test
+    @EnableFlags(FLAG_DEVICE_STATE_RDM_V2)
+    fun testDialogResumesAfterKeyguardGone() =
+        kosmos.runTest {
+            impl.use {
+                it.start()
+                fakeRearDisplayStateInteractor.emitRearDisplay()
+
+                verify(mockDialog).show()
+
+                it.keyguardCallback.onKeyguardVisibilityChanged(true)
+                // Do not need to check that the dialog is dismissed, since SystemUIDialog
+                // implementation handles that. We just toggle keyguard here so that the flow
+                // emits.
+
+                it.keyguardCallback.onKeyguardVisibilityChanged(false)
+                verify(mockDialog, times(2)).show()
+            }
+        }
+
     private class FakeRearDisplayStateInteractor(private val kosmos: Kosmos) :
         RearDisplayStateInteractor {
         private val stateFlow = MutableSharedFlow<RearDisplayStateInteractor.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
index e0118b1..9b03833 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
@@ -22,6 +22,7 @@
 import android.content.pm.ResolveInfo
 import android.os.PowerManager
 import android.os.UserManager
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableContext
 import android.testing.TestableLooper
 import android.view.Display
@@ -29,17 +30,23 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.app.AssistUtils
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
 import com.android.systemui.model.sysUiState
+import com.android.systemui.model.sysUiStateFactory
 import com.android.systemui.navigationbar.NavigationBarController
 import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.views.NavigationBar
 import com.android.systemui.process.ProcessWrapper
 import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
 import com.android.systemui.settings.FakeDisplayTracker
@@ -56,18 +63,20 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.wm.shell.back.BackAnimation
 import com.android.wm.shell.sysui.ShellInterface
 import com.google.common.util.concurrent.MoreExecutors
 import java.util.Optional
 import java.util.concurrent.Executor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.any
@@ -81,6 +90,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -96,8 +106,10 @@
     private val displayTracker = FakeDisplayTracker(mContext)
     private val fakeSystemClock = FakeSystemClock()
     private val sysUiState = kosmos.sysUiState
+    private val sysUiStateFactory = kosmos.sysUiStateFactory
     private val wakefulnessLifecycle =
         WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
+    private val sysuiStatePerDisplayRepository = kosmos.fakeSysUIStatePerDisplayRepository
 
     @Mock private lateinit var launcherProxy: ILauncherProxy.Stub
     @Mock private lateinit var packageManager: PackageManager
@@ -149,6 +161,8 @@
 
         // return isSystemUser as true by default.
         `when`(processWrapper.isSystemUser).thenReturn(true)
+        sysuiStatePerDisplayRepository.add(Display.DEFAULT_DISPLAY, sysUiState)
+        runBlocking { kosmos.displayRepository.apply { addDisplay(0) } }
         subject = createLauncherProxyService(context)
     }
 
@@ -249,6 +263,48 @@
         verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
 
+    @Test
+    fun notifySysUiStateFlagsForAllDisplays_triggersUpdateInAllDisplays() =
+        kosmos.testScope.runTest {
+            kosmos.displayRepository.apply {
+                addDisplay(0)
+                addDisplay(1)
+                addDisplay(2)
+            }
+            kosmos.fakeSysUIStatePerDisplayRepository.apply {
+                add(1, sysUiStateFactory.create(1))
+                add(2, sysUiStateFactory.create(2))
+            }
+            clearInvocations(launcherProxy)
+            subject.notifySysUiStateFlagsForAllDisplays()
+
+            verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(0))
+            verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(1))
+            verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(2))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun updateSystemUiStateFlags_updatesAllNavBars() =
+        kosmos.testScope.runTest {
+            kosmos.displayRepository.apply {
+                addDisplay(0)
+                addDisplay(1)
+            }
+            kosmos.fakeSysUIStatePerDisplayRepository.apply {
+                add(1, sysUiStateFactory.create(1))
+            }
+            val navBar0 = mock<NavigationBar>()
+            val navBar1 = mock<NavigationBar>()
+            whenever(navBarController.getNavigationBar(eq(0))).thenReturn(navBar0)
+            whenever(navBarController.getNavigationBar(eq(1))).thenReturn(navBar1)
+
+            subject.updateSystemUiStateFlags()
+
+            verify(navBar0).updateSystemUiStateFlags()
+            verify(navBar1).updateSystemUiStateFlags()
+        }
+
     private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
         return LauncherProxyService(
             ctx,
@@ -260,7 +316,7 @@
             screenPinningRequest,
             navModeController,
             statusBarWinController,
-            sysUiState,
+            kosmos.fakeSysUIStatePerDisplayRepository,
             mock(),
             mock(),
             userTracker,
@@ -276,6 +332,7 @@
             broadcastDispatcher,
             backAnimation,
             processWrapper,
+            kosmos.displayRepository,
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
index c231be1..826c547 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
@@ -23,6 +23,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.UserHandle;
+import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -37,36 +38,34 @@
 @RunWith(AndroidJUnit4.class)
 public class RingtonePlayerTest extends SysuiTestCase {
 
-    private AudioManager mAudioManager;
-
     private static final String TAG = "RingtonePlayerTest";
 
-    @Before
-    public void setup() throws Exception {
-        mAudioManager = getContext().getSystemService(AudioManager.class);
-    }
-
     @Test
     public void testRingtonePlayerUriUserCheck() {
-        android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
-        final AudioAttributes aa = new AudioAttributes.Builder()
-                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
-        // get a UserId that doesn't belong to mine
-        final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
-        // build a URI that I shouldn't have access to
-        final Uri uri = new Uri.Builder()
-                .scheme("content").authority(otherUserId + "@media")
-                .appendPath("external").appendPath("downloads")
-                .appendPath("bogusPathThatDoesNotMatter.mp3")
-                .build();
-        if (android.media.audio.Flags.ringtoneUserUriCheck()) {
-            assertThrows(SecurityException.class, () ->
-                    irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
-            );
+        // temporarily skipping this test
+        Log.i(TAG, "skipping testRingtonePlayerUriUserCheck");
+        return;
 
-            assertThrows(SecurityException.class, () ->
-                    irp.getTitle(uri));
-        }
+        // TODO change how IRingtonePlayer is created
+//        android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
+//        final AudioAttributes aa = new AudioAttributes.Builder()
+//                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+//        // get a UserId that doesn't belong to mine
+//        final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
+//        // build a URI that I shouldn't have access to
+//        final Uri uri = new Uri.Builder()
+//                .scheme("content").authority(otherUserId + "@media")
+//                .appendPath("external").appendPath("downloads")
+//                .appendPath("bogusPathThatDoesNotMatter.mp3")
+//                .build();
+//        if (android.media.audio.Flags.ringtoneUserUriCheck()) {
+//            assertThrows(SecurityException.class, () ->
+//                    irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
+//            );
+//
+//            assertThrows(SecurityException.class, () ->
+//                    irp.getTitle(uri));
+//        }
     }
 
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 3937d3d46..ff17a36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -97,7 +96,6 @@
     fun themeChangePropagatesToEntry() {
         configurationListener.onThemeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
     }
 
@@ -105,7 +103,6 @@
     fun densityChangePropagatesToEntry() {
         configurationListener.onDensityOrFontScaleChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
     }
 
@@ -129,7 +126,6 @@
         verify(entry).row
         verify(row).onUiModeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
         clearInvocations(entry, row)
 
@@ -160,7 +156,6 @@
         verify(entry).row
         verify(row).onUiModeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
         clearInvocations(entry, row)
 
@@ -196,14 +191,7 @@
         verify(entry).row
         verify(row).onUiModeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
         clearInvocations(entry, row)
     }
-
-    private fun checkGutsExposedCalled() {
-        if (!Flags.notificationUndoGutsOnConfigChanged()) {
-            verify(entry).areGutsExposed()
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 84f39be..2ea4e7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -61,6 +61,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.CachingIconView;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
@@ -71,12 +72,18 @@
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -173,6 +180,7 @@
     }
 
     @Test
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void testUpdateBackgroundColors_isRecursive() throws Exception {
         ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
         group.setTintColor(Color.RED);
@@ -597,14 +605,14 @@
     public void testGetIsNonblockable() throws Exception {
         ExpandableNotificationRow row =
                 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
-        row.setEntry(null);
+        row.setEntryLegacy(null);
 
         assertTrue(row.getIsNonblockable());
 
         NotificationEntry entry = mock(NotificationEntry.class);
 
         Mockito.doReturn(false, true).when(entry).isBlockable();
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
         assertTrue(row.getIsNonblockable());
         assertFalse(row.getIsNonblockable());
     }
@@ -939,12 +947,14 @@
 
     @Test
     @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.isPromotedOngoing()).thenReturn(true);
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
         row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
 
@@ -954,12 +964,14 @@
 
     @Test
     @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.isPromotedOngoing()).thenReturn(true);
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(false);
 
         // THEN
@@ -968,12 +980,14 @@
 
     @Test
     @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.isPromotedOngoing()).thenReturn(true);
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
 
         // THEN
@@ -982,13 +996,15 @@
 
     @Test
     @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationIgnoreLockscreenConstraints_expanded()
             throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.isPromotedOngoing()).thenReturn(true);
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
         row.setIgnoreLockscreenConstraints(true);
 
@@ -996,15 +1012,35 @@
         assertThat(row.isExpanded()).isTrue();
     }
 
+    private static void setRowPromotedOngoing(ExpandableNotificationRow row) {
+        final NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        if (NotificationBundleUi.isEnabled()) {
+            final EntryAdapter entryAdapter = new NotificationEntryAdapter(
+                    mock(NotificationActivityStarter.class),
+                    mock(MetricsLogger.class),
+                    mock(PeopleNotificationIdentifier.class),
+                    mock(NotificationIconStyleProvider.class),
+                    mock(VisualStabilityCoordinator.class),
+                    mock(NotificationActionClickManager.class),
+                    entry);
+            row.setEntryAdapter(entryAdapter);
+        } else {
+            row.setEntryLegacy(entry);
+        }
+    }
+
     @Test
     @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
             throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.isPromotedOngoing()).thenReturn(true);
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
         row.setSaveSpaceOnLockscreen(true);
 
@@ -1014,13 +1050,15 @@
 
     @Test
     @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationNotSaveSpaceOnLockScreen_expanded()
             throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.isPromotedOngoing()).thenReturn(true);
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
         row.setSaveSpaceOnLockscreen(false);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index c874bc6..cf8278e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -19,6 +19,7 @@
 import android.annotation.DimenRes
 import android.content.res.Resources
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
 import android.service.notification.StatusBarNotification
 import android.testing.TestableLooper
 import android.testing.ViewUtils
@@ -93,13 +94,12 @@
                             /* attrs= */ null,
                             UserHandle.CURRENT
                         ).apply {
-                            entry = mockEntry
                             entryAdapter = mockEntryAdapter
                         }
                     }
                     false -> {
                         ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
-                            entry = mockEntry
+                            entryLegacy = mockEntry
                         }
                     }
                 }
@@ -402,6 +402,7 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun setExpandedChild_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
         // Bubble button should not be shown for the given NotificationEntry
@@ -429,6 +430,7 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun setExpandedChild_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
         // Bubble button should be shown for the given NotificationEntry
@@ -458,6 +460,7 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun onNotificationUpdated_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
         val mockNotificationEntry = createMockNotificationEntry()
@@ -486,6 +489,7 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun onNotificationUpdated_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
         val mockNotificationEntry = createMockNotificationEntry()
@@ -514,7 +518,7 @@
         // Given: controller says bubbles are enabled for the user
         view.setBubblesEnabledForUser(true)
 
-        // Then: bottom margin of actionListMarginTarget should not change, still be 20
+        // Then: bottom margin of actionListMarginTarget should be changed to 0
         assertEquals(0, getMarginBottom(actionListMarginTarget))
     }
 
@@ -628,8 +632,7 @@
             whenever(sbnMock.user).thenReturn(userMock)
         }
 
-    private fun createMockNotificationEntryAdapter() =
-        mock<EntryAdapter>()
+    private fun createMockNotificationEntryAdapter() = mock<EntryAdapter>()
 
     private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
         val outerLayout = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 0c0ef9d..10de866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -67,6 +67,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
 import com.android.systemui.statusbar.notification.row.icon.appIconProvider
 import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -146,6 +147,7 @@
     @Mock private lateinit var notificationManager: INotificationManager
     @Mock private lateinit var shortcutManager: ShortcutManager
     @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+    @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
     @Mock private lateinit var contextTracker: UserContextProvider
     @Mock private lateinit var bubblesManager: BubblesManager
@@ -185,6 +187,7 @@
                 launcherApps,
                 shortcutManager,
                 channelEditorDialogController,
+                packageDemotionInteractor,
                 contextTracker,
                 assistantFeedbackController,
                 Optional.of(bubblesManager),
@@ -297,45 +300,6 @@
     }
 
     @Test
-    fun testChangeDensityOrFontScale() {
-        val guts = spy(NotificationGuts(mContext))
-        whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
-            handler.post((invocation.arguments[0] as Runnable))
-            null
-        }
-
-        // Test doesn't support animation since the guts view is not attached.
-        doNothing()
-            .whenever(guts)
-            .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-        val realRow = createTestNotificationRow()
-        val menuItem = createTestMenuItem(realRow)
-        val row = spy(realRow)
-        whenever(row!!.windowToken).thenReturn(Binder())
-        whenever(row.guts).thenReturn(guts)
-        doNothing().whenever(row).ensureGutsInflated()
-        val realEntry = realRow!!.entry
-        val entry = spy(realEntry)
-        whenever(entry.row).thenReturn(row)
-        whenever(entry.getGuts()).thenReturn(guts)
-        Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
-        executor.runAllReady()
-        verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-
-        // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
-        verify(row).setGutsView(any())
-        row.onDensityOrFontScaleChanged()
-        gutsManager.onDensityOrFontScaleChanged(entry)
-        executor.runAllReady()
-        gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
-        verify(guts)
-            .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>())
-
-        // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
-        verify(row, times(2)).setGutsView(any())
-    }
-
-    @Test
     fun testAppOpsSettingsIntent_camera() {
         val ops = ArraySet<Int>()
         ops.add(AppOpsManager.OP_CAMERA)
@@ -427,6 +391,7 @@
             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
             .setImportance(NotificationManager.IMPORTANCE_HIGH)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
         val statusBarNotification = entry.sbn
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -438,6 +403,7 @@
                 eq(iconStyleProvider),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                eq(packageDemotionInteractor),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -447,7 +413,8 @@
                 any<UiEventLogger>(),
                 eq(true),
                 eq(false),
-                eq(true), /* wasShownHighPriority */
+                eq(true),
+                eq(true),
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
                 any<View.OnClickListener>(),
@@ -462,6 +429,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -473,6 +441,7 @@
                 eq(iconStyleProvider),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                eq(packageDemotionInteractor),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -482,7 +451,8 @@
                 any<UiEventLogger>(),
                 eq(true),
                 eq(false),
-                eq(false), /* wasShownHighPriority */
+                eq(true), /* wasShownHighPriority */
+                eq(false),
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
                 any<View.OnClickListener>(),
@@ -497,6 +467,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -508,6 +479,7 @@
                 eq(iconStyleProvider),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                eq(packageDemotionInteractor),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -517,7 +489,8 @@
                 any<UiEventLogger>(),
                 eq(true),
                 eq(false),
-                eq(false), /* wasShownHighPriority */
+                eq(true), /* wasShownHighPriority */
+                eq(false),
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
                 any<View.OnClickListener>(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index ffb861d..063b546 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -296,6 +296,7 @@
                 mKosmos.getTestDispatcher(),
                 mLinearLargeScreenShadeInterpolator,
                 new BlurConfig(0.0f, 0.0f),
+                mContext,
                 mKosmos::getWindowRootViewBlurInteractor);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
@@ -1247,6 +1248,7 @@
                 mKosmos.getTestDispatcher(),
                 mLinearLargeScreenShadeInterpolator,
                 new BlurConfig(0.0f, 0.0f),
+                mContext,
                 mKosmos::getWindowRootViewBlurInteractor);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index 5f34420..422b20e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -45,7 +45,7 @@
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -92,7 +92,7 @@
 
         engine.onSurfaceRedrawNeeded(surfaceHolder)
 
-        verifyZeroInteractions(canvas)
+        verifyNoMoreInteractions(canvas)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 663a853..122e6a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.display.data.repository
 
 import android.view.Display
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.util.mockito.mock
 import dagger.Binds
@@ -41,16 +42,15 @@
 }
 
 /** Creates a mock [DisplayRepository.PendingDisplay]. */
-fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
-    mock<DisplayRepository.PendingDisplay> { whenever(this.id).thenReturn(id) }
+fun createPendingDisplay(id: Int = 0): PendingDisplay =
+    mock<PendingDisplay> { whenever(this.id).thenReturn(id) }
 
 @SysUISingleton
 /** Fake [DisplayRepository] implementation for testing. */
 class FakeDisplayRepository @Inject constructor() : DisplayRepository {
     private val flow = MutableStateFlow<Set<Display>>(emptySet())
     private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
-    private val pendingDisplayFlow =
-        MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
+    private val pendingDisplayFlow = MutableSharedFlow<PendingDisplay?>(replay = 1)
     private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
     private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
     private val displayIdsWithSystemDecorationsFlow = MutableStateFlow<Set<Int>>(emptySet())
@@ -101,7 +101,7 @@
     suspend fun emit(value: Set<Display>) = flow.emit(value)
 
     /** Emits [value] as [pendingDisplay] flow value. */
-    suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
+    suspend fun emit(value: PendingDisplay?) = pendingDisplayFlow.emit(value)
 
     override val displays: StateFlow<Set<Display>>
         get() = flow
@@ -109,7 +109,7 @@
     override val displayIds: StateFlow<Set<Int>>
         get() = displayIdFlow
 
-    override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
+    override val pendingDisplay: Flow<PendingDisplay?>
         get() = pendingDisplayFlow
 
     private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
index aa23aa3..4b516e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.display.data.repository
 
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -66,6 +68,7 @@
 val Kosmos.fakePerDisplayInstanceProviderWithTeardown by
     Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() }
 
+val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) }
 val Kosmos.fakePerDisplayInstanceRepository by
     Kosmos.Fixture {
         PerDisplayInstanceRepositoryImpl(
@@ -73,6 +76,6 @@
             instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
             testScope.backgroundScope,
             displayRepository,
-            dumpManager,
+            perDisplayDumpHelper,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
similarity index 66%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
index 3cec5a9..361d21d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
 
-val Kosmos.keyguardServiceShowLockscreenInteractor by
-    Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+val Kosmos.keyguardServiceShowLockscreenRepository by
+    Kosmos.Fixture { KeyguardServiceShowLockscreenRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
new file mode 100644
index 0000000..447aa12
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardServiceShowLockscreenRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardServiceShowLockscreenInteractor by
+    Kosmos.Fixture {
+        KeyguardServiceShowLockscreenInteractor(
+            backgroundScope = testScope,
+            selectedUserInteractor = selectedUserInteractor,
+            repository = keyguardServiceShowLockscreenRepository,
+            userTracker = userTracker,
+            wmLockscreenVisibilityInteractor = { windowManagerLockscreenVisibilityInteractor },
+            keyguardEnabledInteractor = keyguardEnabledInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
index f7caeb6..dd868e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
@@ -18,10 +18,12 @@
 
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
 
 val Kosmos.keyguardShowWhileAwakeInteractor by
     Kosmos.Fixture {
         KeyguardShowWhileAwakeInteractor(
+            backgroundScope = testScope,
             biometricSettingsRepository = biometricSettingsRepository,
             keyguardEnabledInteractor = keyguardEnabledInteractor,
             keyguardServiceShowLockscreenInteractor = keyguardServiceShowLockscreenInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 43fa718..c89fb70 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.util.settings.fakeSettings
 import com.android.systemui.util.time.systemClock
 
-val Kosmos.keyguardWakeDirectlyToGoneInteractor by
+val Kosmos.keyguardWakeDirectlyToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor by
     Kosmos.Fixture {
         KeyguardWakeDirectlyToGoneInteractor(
             applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
index 11bd4c7..54261c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
@@ -38,7 +38,9 @@
     }
 }
 
-val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture { FakePerDisplayRepository<SysUiState>() }
+val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture {
+    FakePerDisplayRepository<SysUiState>().apply { add(Display.DEFAULT_DISPLAY, sysUiState) }
+}
 
 val Kosmos.sysuiStateInteractor by Fixture {
     SysUIStateDisplaysInteractor(fakeSysUIStatePerDisplayRepository, displayRepository)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 4efcada..215df9d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -58,7 +58,7 @@
         return this;
     }
 
-    /** Sets the creation time. */
+    /** Sets the creation time. Should be SystemClock.elapsedRealtime */
     public GroupEntryBuilder setCreationTime(long creationTime) {
         mCreationTime = creationTime;
         return this;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
similarity index 66%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
index 3cec5a9..38b5994 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
 
-val Kosmos.keyguardServiceShowLockscreenInteractor by
-    Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+val Kosmos.packageDemotionInteractor by Kosmos.Fixture { PackageDemotionInteractor() }
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 1148539..083d2aa 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -180,9 +180,11 @@
     // AUTO-GENERATED-END
   ],
   "ravenwood-postsubmit": [
-    {
-      "name": "SystemUiRavenTests",
-      "host": true
-    }
+    // We haven't maintained SystemUiRavenTests, and as a result, it's been demoted already.
+    // Disable it until we fix the issues: b/319647875
+    // {
+    //   "name": "SystemUiRavenTests",
+    //   "host": true
+    // }
   ]
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6c26c1f..703e37f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -531,7 +531,7 @@
             AccessibilitySecurityPolicy securityPolicy,
             SystemActionPerformer systemActionPerformer,
             AccessibilityWindowManager a11yWindowManager,
-            AccessibilityDisplayListener a11yDisplayListener,
+            AccessibilityDisplayListener.DisplayManagerWrapper displayManagerWrapper,
             MagnificationController magnificationController,
             @Nullable AccessibilityInputFilter inputFilter,
             ProxyManager proxyManager,
@@ -550,7 +550,8 @@
         mSecurityPolicy = securityPolicy;
         mSystemActionPerformer = systemActionPerformer;
         mA11yWindowManager = a11yWindowManager;
-        mA11yDisplayListener = a11yDisplayListener;
+        mA11yDisplayListener = new AccessibilityDisplayListener(displayManagerWrapper,
+                new MainHandler(Looper.getMainLooper()));
         mMagnificationController = magnificationController;
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
@@ -596,7 +597,8 @@
                 this, LocalServices.getService(PackageManagerInternal.class));
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
-        mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+        mA11yDisplayListener = new AccessibilityDisplayListener(
+                new AccessibilityDisplayListener.DisplayManagerWrapper(mContext), mMainHandler);
         mMagnificationController = new MagnificationController(
                 this,
                 mLock,
@@ -5457,11 +5459,11 @@
      * A Utility class to handle display state.
      */
     public class AccessibilityDisplayListener implements DisplayManager.DisplayListener {
-        private final DisplayManager mDisplayManager;
+        private final DisplayManagerWrapper mDisplayManager;
         private final ArrayList<Display> mDisplaysList = new ArrayList<>();
         private int mSystemUiUid = 0;
 
-        AccessibilityDisplayListener(Context context, Handler handler) {
+        AccessibilityDisplayListener(DisplayManagerWrapper displayManager, Handler handler) {
             // Avoid concerns about one thread adding displays while another thread removes
             // them by ensuring the looper is the main looper and the DisplayListener
             // callbacks are always executed on the one main thread.
@@ -5474,7 +5476,7 @@
                 Slog.e(LOG_TAG, errorMessage);
             }
 
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+            mDisplayManager = displayManager;
             mDisplayManager.registerDisplayListener(this, handler);
             initializeDisplayList();
 
@@ -5626,6 +5628,34 @@
             }
             return true;
         }
+
+        /** Wrapper of DisplayManager for testing. */
+        @VisibleForTesting
+        static class DisplayManagerWrapper {
+            private final DisplayManager mDm;
+
+            DisplayManagerWrapper(Context context) {
+                mDm = context.getSystemService(DisplayManager.class);
+            }
+
+            /**
+             * @see DisplayManager#registerDisplayListener(DisplayManager.DisplayListener, Handler)
+             */
+            public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+                    @Nullable Handler handler) {
+                mDm.registerDisplayListener(listener, handler);
+            }
+
+            /** @see DisplayManager#getDisplays() */
+            public Display[] getDisplays() {
+                return mDm.getDisplays();
+            }
+
+            /** @see DisplayManager#getDisplay(int) */
+            public Display getDisplay(int displayId) {
+                return mDm.getDisplay(displayId);
+            }
+        }
     }
 
     /** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 805d7f8..94cef41 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -165,6 +165,9 @@
             if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
                 if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) {
                     AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
+                    if (commDevice == null) {
+                        return;
+                    }
                     mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
                     if (mHearingDevice != null) {
                         showNotificationIfNeeded();
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 23166a8..84158cf 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -24,6 +24,7 @@
 import static android.view.accessibility.AccessibilityManager.AUTOCLICK_REVERT_TO_LEFT_CLICK_DEFAULT;
 
 import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
@@ -45,6 +46,7 @@
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
 import android.view.WindowManager;
 
 import androidx.annotation.VisibleForTesting;
@@ -799,7 +801,7 @@
 
             final long now = SystemClock.uptimeMillis();
 
-            int actionButton;
+            int actionButton = BUTTON_PRIMARY;
             if (mHoveredState) {
                 // Always triggers left-click when the cursor hovers over the autoclick type
                 // panel, to always allow users to change a different click type. Otherwise, if
@@ -807,15 +809,32 @@
                 // select other click types.
                 actionButton = BUTTON_PRIMARY;
             } else {
-                actionButton = mActiveClickType == AUTOCLICK_TYPE_RIGHT_CLICK
-                        ? BUTTON_SECONDARY
-                        : BUTTON_PRIMARY;
+                switch (mActiveClickType) {
+                    case AUTOCLICK_TYPE_LEFT_CLICK:
+                        actionButton = BUTTON_PRIMARY;
+                        break;
+                    case AUTOCLICK_TYPE_RIGHT_CLICK:
+                        actionButton = BUTTON_SECONDARY;
+                        break;
+                    case AUTOCLICK_TYPE_DOUBLE_CLICK:
+                        actionButton = BUTTON_PRIMARY;
+                        long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+                        sendMotionEvent(actionButton, now);
+                        sendMotionEvent(actionButton, now + doubleTapMinimumTimeout);
+                        return;
+                    default:
+                        break;
+                }
             }
 
+            sendMotionEvent(actionButton, now);
+        }
+
+        private void sendMotionEvent(int actionButton, long eventTime) {
             MotionEvent downEvent =
                     MotionEvent.obtain(
-                            /* downTime= */ now,
-                            /* eventTime= */ now,
+                            /* downTime= */ eventTime,
+                            /* eventTime= */ eventTime,
                             MotionEvent.ACTION_DOWN,
                             /* pointerCount= */ 1,
                             mTempPointerProperties,
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 88b7910..6e098d01 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -21,15 +21,18 @@
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.sensor.IVirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.content.AttributionSource;
+import android.hardware.SensorAdditionalInfo;
 import android.hardware.SensorDirectChannel;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SharedMemory;
+import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -140,7 +143,7 @@
         final IBinder sensorToken =
                 new Binder("android.hardware.sensor.VirtualSensor:" + config.getName());
         VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(),
-                virtualDevice, sensorToken);
+                config.getFlags(), virtualDevice, sensorToken);
         synchronized (mLock) {
             mSensorDescriptors.put(sensorToken, sensorDescriptor);
             mVirtualSensors.put(handle, sensor);
@@ -164,6 +167,37 @@
         }
     }
 
+    boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+            @NonNull VirtualSensorAdditionalInfo info) {
+        Objects.requireNonNull(token);
+        Objects.requireNonNull(info);
+        synchronized (mLock) {
+            final SensorDescriptor sensorDescriptor = mSensorDescriptors.get(token);
+            long timestamp = SystemClock.elapsedRealtimeNanos();
+            if (sensorDescriptor == null) {
+                throw new IllegalArgumentException("Could not send sensor event for given token");
+            }
+            if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+                    sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_BEGIN,
+                    /* serial= */ 0, timestamp++, /* values= */ null)) {
+                return false;
+            }
+            for (int i = 0; i < info.getValues().size(); ++i) {
+                if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+                        sensorDescriptor.getHandle(), info.getType(), /* serial= */ i,
+                        timestamp++, info.getValues().get(i))) {
+                    return false;
+                }
+            }
+            if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+                    sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_END,
+                    /* serial= */ 0, timestamp, /* values= */ null)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Nullable
     VirtualSensor getSensorByHandle(int handle) {
         synchronized (mLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 28efdfc..0023b6d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -55,6 +55,7 @@
 import android.companion.virtual.audio.IAudioRoutingCallback;
 import android.companion.virtual.camera.VirtualCameraConfig;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.companion.virtualdevice.flags.Flags;
 import android.compat.annotation.ChangeId;
@@ -1294,6 +1295,18 @@
     }
 
     @Override // Binder call
+    public boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+            @NonNull VirtualSensorAdditionalInfo info) {
+        checkCallerIsDeviceOwner();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mSensorController.sendSensorAdditionalInfo(token, info);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override // Binder call
     public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
             IntentFilter filter) {
         checkCallerIsDeviceOwner();
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 7eb7072..a1d39fa 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -313,7 +313,8 @@
             android.Manifest.permission.CREATE_USERS,
             android.Manifest.permission.QUERY_USERS
     })
-    private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
+    private Intent getContextualSearchIntent(int entrypoint, int userId, String callingPackage,
+            CallbackToken mToken) {
         final Intent launchIntent = getResolvedLaunchIntent(userId);
         if (launchIntent == null) {
             if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
@@ -332,6 +333,9 @@
             launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING,
                     mAudioManager.isMusicActive());
         }
+        if (Flags.selfInvocation()) {
+            launchIntent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
+        }
         boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
         final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
         final List<IBinder> activityTokens = new ArrayList<>(records.size());
@@ -500,6 +504,7 @@
         }
 
         private void startContextualSearchInternal(int entrypoint) {
+            final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid());
             final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
             mAssistDataRequester.cancel();
             // Creates a new CallbackToken at mToken and an expiration handler.
@@ -508,7 +513,8 @@
             // server has READ_FRAME_BUFFER permission to get the screenshot and because only
             // the system server can invoke non-exported activities.
             Binder.withCleanCallingIdentity(() -> {
-                Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken);
+                Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId,
+                            callingPackage, mToken);
                 if (launchIntent != null) {
                     int result = invokeContextualSearchIntent(launchIntent, callingUserId);
                     if (DEBUG) {
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 4b6d6bc..fd2d835 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -61,7 +61,7 @@
 per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
 
 # Miscellaneous
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com
 per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
 
 # Activity Security
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index fb33cb1..72de8d5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1059,7 +1059,7 @@
         if (Flags.enableAllSqliteAppopsAccesses()) {
             mHistoricalRegistry = new HistoricalRegistrySql(context);
         } else {
-            mHistoricalRegistry = new HistoricalRegistry(this, context);
+            mHistoricalRegistry = new LegacyHistoricalRegistry(this, context);
         }
     }
 
@@ -7011,7 +7011,8 @@
             mHistoricalRegistry = new HistoricalRegistrySql(
                     (HistoricalRegistrySql) mHistoricalRegistry);
         } else {
-            mHistoricalRegistry = new HistoricalRegistry((HistoricalRegistry) mHistoricalRegistry);
+            mHistoricalRegistry = new LegacyHistoricalRegistry(
+                    (LegacyHistoricalRegistry) mHistoricalRegistry);
         }
 
         mHistoricalRegistry.systemReady(mContext.getContentResolver());
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index c53e4bd..3a8d583 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -24,6 +24,7 @@
 import android.database.DefaultDatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteRawStatement;
 import android.os.Environment;
@@ -174,11 +175,16 @@
         if (DEBUG) {
             Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
         }
-        SQLiteDatabase db = getWritableDatabase();
-        if (bindArgs == null) {
-            db.execSQL(sql);
-        } else {
-            db.execSQL(sql, bindArgs);
+        try {
+            SQLiteDatabase db = getWritableDatabase();
+            if (bindArgs == null) {
+                db.execSQL(sql);
+            } else {
+                db.execSQL(sql, bindArgs);
+            }
+        } catch (SQLiteFullException exception) {
+            Slog.e(LOG_TAG, "Couldn't exec sql command, disk is full. Discrete ops "
+                    + "db file size (bytes) : " + getDatabaseFile().length(), exception);
         }
     }
 
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
index 70b7016..3867cbe 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -82,9 +82,8 @@
  * INITIALIZATION: We can initialize persistence only after the system is ready
  * as we need to check the optional configuration override from the settings
  * database which is not initialized at the time the app ops service is created. This class
- * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
- * outside calls are going through {@link HistoricalRegistry}.
- *
+ * relies on {@link LegacyHistoricalRegistry} for controlling that no calls are allowed until then.
+ * All outside calls are going through {@link LegacyHistoricalRegistry}.
  */
 abstract class DiscreteOpsRegistry {
     private static final String TAG = DiscreteOpsRegistry.class.getSimpleName();
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index 20706b6..771df19 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -81,7 +81,7 @@
  * THREADING AND LOCKING:
  * For in-memory transactions this class relies on {@link DiscreteOpsXmlRegistry#mInMemoryLock}.
  * It is assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
- * {@link HistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
+ * {@link LegacyHistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
  * {@link DiscreteOpsRegistry#recordDiscreteAccess} must only be called while holding this lock.
  * {@link DiscreteOpsXmlRegistry#mOnDiskLock} is used when disk transactions are performed.
  * It is very important to release {@link DiscreteOpsXmlRegistry#mInMemoryLock} as soon as
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
similarity index 99%
rename from services/core/java/com/android/server/appop/HistoricalRegistry.java
rename to services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
index a8128dd..f4f5775 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
@@ -128,11 +128,11 @@
  */
 // TODO (bug:122218838): Make sure we handle start of epoch time
 // TODO (bug:122218838): Validate changed time is handled correctly
-final class HistoricalRegistry implements HistoricalRegistryInterface {
+final class LegacyHistoricalRegistry implements HistoricalRegistryInterface {
     private static final boolean DEBUG = false;
     private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE;
 
-    private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName();
+    private static final String LOG_TAG = LegacyHistoricalRegistry.class.getSimpleName();
 
     private static final String PARAMETER_DELIMITER = ",";
     private static final String PARAMETER_ASSIGNMENT = "=";
@@ -200,7 +200,7 @@
 
     private final Context mContext;
 
-    HistoricalRegistry(@NonNull Object lock, Context context) {
+    LegacyHistoricalRegistry(@NonNull Object lock, Context context) {
         mInMemoryLock = lock;
         mContext = context;
         if (Flags.enableSqliteAppopsAccesses()) {
@@ -210,7 +210,7 @@
         }
     }
 
-    HistoricalRegistry(@NonNull HistoricalRegistry other) {
+    LegacyHistoricalRegistry(@NonNull LegacyHistoricalRegistry other) {
         this(other.mInMemoryLock, other.mContext);
         mMode = other.mMode;
         mBaseSnapshotInterval = other.mBaseSnapshotInterval;
@@ -313,9 +313,9 @@
                 final int mode = AppOpsManager.parseHistoricalMode(modeValue);
                 final long baseSnapshotInterval = Long.parseLong(baseSnapshotIntervalValue);
                 final int intervalCompressionMultiplier = Integer.parseInt(intervalMultiplierValue);
-                setHistoryParameters(mode, baseSnapshotInterval,intervalCompressionMultiplier);
+                setHistoryParameters(mode, baseSnapshotInterval, intervalCompressionMultiplier);
                 return;
-            } catch (NumberFormatException ignored) {}
+            } catch (NumberFormatException ignored) { }
         }
         Slog.w(LOG_TAG, "Bad value for" + Settings.Global.APPOP_HISTORY_PARAMETERS
                 + "=" + setting + " resetting!");
@@ -805,7 +805,7 @@
 
     private void schedulePersistHistoricalOpsMLocked(@NonNull HistoricalOps ops) {
         final Message message = PooledLambda.obtainMessage(
-                HistoricalRegistry::persistPendingHistory, HistoricalRegistry.this);
+                LegacyHistoricalRegistry::persistPendingHistory, LegacyHistoricalRegistry.this);
         message.what = MSG_WRITE_PENDING_HISTORY;
         IoThread.getHandler().sendMessage(message);
         mPendingWrites.offerFirst(ops);
@@ -813,7 +813,7 @@
 
     private static void makeRelativeToEpochStart(@NonNull HistoricalOps ops, long nowMillis) {
         ops.setBeginAndEndTime(nowMillis - ops.getEndTimeMillis(),
-                nowMillis- ops.getBeginTimeMillis());
+                nowMillis - ops.getBeginTimeMillis());
     }
 
     private void pruneFutureOps(@NonNull List<HistoricalOps> ops) {
@@ -979,7 +979,7 @@
                     final HistoricalOps readOp = readOps.get(i);
                     currentOps.merge(readOp);
                 }
-             }
+            }
         }
 
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid,
@@ -1125,7 +1125,7 @@
                 if (existingOpCount > 0) {
                     // Compute elapsed time
                     final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
-                        .getEndTimeMillis();
+                            .getEndTimeMillis();
                     for (int i = 0; i < existingOpCount; i++) {
                         final HistoricalOps existingOp = existingOps.get(i);
                         existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 11a5c6d..7f03b27 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -15123,6 +15123,61 @@
 
     /**
      * @hide
+     * Returns the current audio output device delay value of key in milliseconds.
+     *
+     * In aidl implementation, the param of getParameters is "key" and reply delay of all supported
+     * devices which be composited as "key=device,address,delay|device,address,delay|...".
+     * e.g.
+     * param: additional_output_device_delay=
+     * reply: additional_output_device_delay=2,,0|4,,0|8,,0|128,,0|256,,0|512,,0|1024,,0|8192,,0|
+     * 16384,,0|262144,,0|262145,,0|524288,,0|67108864,,0|134217728,,0|536870912,,0|536870913,,0|
+     * 536870914,,0
+     *
+     * In hidl implementation, the param of getParameters is "key=device,address" and reply a
+     * specific device delay which be composited as "key=device,address,delay".
+     * e.g.
+     * param: additional_output_device_delay=2,
+     * reply: additional_output_device_delay=2,,0
+     *
+     * @param key
+     * @param deviceType
+     * @param address
+     * @return the delay value of key. This is a non-negative number.
+     *     {@code 0} is returned if unsupported.
+     */
+    private long getDelayByKeyDevice(@NonNull String key, @NonNull AudioDeviceAttributes device) {
+        long delayMillis = 0;
+
+        try {
+            if (AudioHalVersionInfo.AUDIO_HAL_TYPE_AIDL == getHalVersion().getHalType()) {
+                final String reply = AudioSystem.getParameters(key);
+                final String keyDeviceAddressPrefix =
+                    Integer.toUnsignedString(device.getInternalType()) + "," + device.getAddress() +
+                    ",";
+                int start = reply.indexOf(keyDeviceAddressPrefix);
+                int end = -1;
+                if (start != -1) {
+                    start += keyDeviceAddressPrefix.length();
+                    end = reply.indexOf("|", start);
+                    delayMillis = Long.parseLong(
+                            end == -1 ? reply.substring(start) : reply.substring(start, end));
+                }
+            } else {
+                final String reply = AudioSystem.getParameters(
+                    key + "=" + device.getInternalType() + "," + device.getAddress());
+                if (reply.contains(key)) {
+                    delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+                }
+            }
+        } catch (NullPointerException e) {
+            Log.w(TAG, "NullPointerException when getting delay for device " + device, e);
+        }
+
+        return delayMillis;
+    }
+
+    /**
+     * @hide
      * Returns the current additional audio output device delay in milliseconds.
      *
      * @param deviceType
@@ -15138,17 +15193,8 @@
         device = retrieveBluetoothAddress(device);
 
         final String key = "additional_output_device_delay";
-        final String reply = AudioSystem.getParameters(
-                key + "=" + device.getInternalType() + "," + device.getAddress());
-        long delayMillis = 0;
-        if (reply.contains(key)) {
-            try {
-                delayMillis = Long.parseLong(reply.substring(key.length() + 1));
-            } catch (NullPointerException e) {
-                delayMillis = 0;
-            }
-        }
-        return delayMillis;
+
+        return getDelayByKeyDevice(key, device);
     }
 
     /**
@@ -15170,17 +15216,8 @@
         device = retrieveBluetoothAddress(device);
 
         final String key = "max_additional_output_device_delay";
-        final String reply = AudioSystem.getParameters(
-                key + "=" + device.getInternalType() + "," + device.getAddress());
-        long delayMillis = 0;
-        if (reply.contains(key)) {
-            try {
-                delayMillis = Long.parseLong(reply.substring(key.length() + 1));
-            } catch (NullPointerException e) {
-                delayMillis = 0;
-            }
-        }
-        return delayMillis;
+
+        return getDelayByKeyDevice(key, device);
     }
 
     @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ac0892b..aa98590 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1113,7 +1113,11 @@
             }
             // Remove always-on VPN if it's not supported.
             if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
-                setAlwaysOnPackage(null, false, null);
+                // Do not remove the always-on setting due to the restricted ability in safe mode.
+                // The always-on VPN can then start after the device reboots to normal mode.
+                if (!mContext.getPackageManager().isSafeMode()) {
+                    setAlwaysOnPackage(null, false, null);
+                }
                 return false;
             }
             // Skip if the service is already established. This isn't bulletproof: it's not bound
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 3aaf4f6..7450dff 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -122,8 +122,8 @@
     public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
 
     /**
-     * Flag: This flag identifies secondary displays that should show system decorations, such as
-     * navigation bar, home activity or wallpaper.
+     * Flag: This flag identifies secondary displays that should always show system decorations,
+     * such as navigation bar, home activity or wallpaper.
      * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
      * @hide
      */
@@ -191,6 +191,19 @@
     public static final int FLAG_STEAL_TOP_FOCUS_DISABLED = 1 << 19;
 
     /**
+     * Flag: Indicates that the display is allowed to switch the content mode between
+     * projected/extended and mirroring. This allows the display to dynamically add or remove the
+     * home and system decorations.
+     *
+     * Note that this flag should not be enabled with any of {@link #FLAG_PRIVATE},
+     * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}, or {@link #FLAG_OWN_CONTENT_ONLY} at the
+     * same time; otherwise it will be ignored.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 20;
+
+    /**
      * Touch attachment: Display does not receive touch.
      */
     public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index f73b66c..ce8d8a6 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import android.util.IndentingPrintWriter;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -97,4 +99,14 @@
         }
         return displayIds;
     }
+
+    /** Dumps information about the DisplayGroup. */
+    void dumpLocked(IndentingPrintWriter ipw) {
+        final int numDisplays = mDisplays.size();
+        for (int i = 0; i < numDisplays; i++) {
+            LogicalDisplay logicalDisplay = mDisplays.get(i);
+            ipw.println("Display " + logicalDisplay.getDisplayIdLocked() + " "
+                    + logicalDisplay.getPrimaryDisplayDeviceLocked());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7b714ad..2cad7ed 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -766,7 +766,7 @@
                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
                     }
                 } else {
-                    if (!res.getBoolean(R.bool.config_localDisplaysMirrorContent)) {
+                    if (shouldOwnContentOnly()) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
                     }
 
@@ -780,6 +780,15 @@
                     }
                 }
 
+                if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+                    // Public display with FLAG_OWN_CONTENT_ONLY disabled is allowed to switch the
+                    // content mode.
+                    if (mIsFirstDisplay
+                            || (!isDisplayPrivate(physicalAddress) && !shouldOwnContentOnly())) {
+                        mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+                    }
+                }
+
                 if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
                 }
@@ -822,6 +831,7 @@
                                 R.string.display_manager_hdmi_display_name);
                     }
                 }
+
                 mInfo.frameRateOverrides = mFrameRateOverrides;
 
                 // The display is trusted since it is created by system.
@@ -1467,6 +1477,11 @@
             return false;
         }
 
+        private boolean shouldOwnContentOnly() {
+            final Resources res = getOverlayContext().getResources();
+            return !res.getBoolean(R.bool.config_localDisplaysMirrorContent);
+        }
+
         private boolean isDisplayStealTopFocusDisabled(DisplayAddress.Physical physicalAddress) {
             if (physicalAddress == null) {
                 return false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b2b9ef1..0e6870f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -489,6 +489,11 @@
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_STEAL_TOP_FOCUS_DISABLED;
             }
+            // Rear display should not be allowed to use the content mode switch.
+            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0
+                    && mDevicePosition != Layout.Display.POSITION_REAR) {
+                mBaseDisplayInfo.flags |= Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+            }
             Rect maskingInsets = getMaskingInsets(deviceInfo);
             int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
             int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
@@ -1155,6 +1160,7 @@
         pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
         pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
         pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
+        pw.println("mDisplayGroupId=" + mDisplayGroupId);
         pw.println("mDisplayGroupName=" + mDisplayGroupName);
         pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
         pw.println("mLeadDisplayId=" + mLeadDisplayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index f4daf87..4a4c616 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -478,6 +478,21 @@
             ipw.decreaseIndent();
             ipw.println();
         }
+
+        final int displayGroupCount = mDisplayGroups.size();
+        ipw.println();
+        ipw.println("Display Groups: size=" + displayGroupCount);
+        for (int i = 0; i < displayGroupCount; i++) {
+            int groupId = mDisplayGroups.keyAt(i);
+            DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
+            ipw.println("Group " + groupId + ":");
+            ipw.increaseIndent();
+            displayGroup.dumpLocked(ipw);
+            ipw.decreaseIndent();
+            ipw.println();
+        }
+
+
         mDeviceStateToLayoutMap.dumpLocked(ipw);
     }
 
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index b5a9b19..60b7fca 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -76,6 +76,7 @@
  * <li><code>secure</code>: creates a secure display</li>
  * <li><code>own_content_only</code>: only shows this display's own content</li>
  * <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>fixed_content_mode</code>: not allowed to switch content mode</li>
  * <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
  * <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
  * <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
@@ -117,6 +118,18 @@
     private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
             "should_show_system_decorations";
 
+    /**
+     * When this flag is set, the overlay display is not allowed to switch content mode.
+     * Note that it is the opposite of {@link  DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH},
+     * because we want overlay displays (such as those used for connected display simulation in
+     * development) to have {@link  DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH} enabled by
+     * default without explicitly specifying it.
+     *
+     * @see DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH
+     */
+    private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE =
+            "fixed_content_mode";
+
     // Gravity flags to decide where the overlay should be shown.
     private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
     private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
@@ -384,6 +397,17 @@
                 if (mFlags.mShouldShowSystemDecorations) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
                 }
+                if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+                    if (!mFlags.mFixedContentMode
+                            && !mFlags.mOwnContentOnly
+                            && !mFlags.mShouldShowSystemDecorations) {
+                        // For overlay displays, if FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS and
+                        // FLAG_OWN_CONTENT_ONLY are both disabled,
+                        // then FLAG_ALLOWS_CONTENT_MODE_SWITCH should be enabled by default,
+                        // unless OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE is set.
+                        mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+                    }
+                }
                 mInfo.type = Display.TYPE_OVERLAY;
                 mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
                 mInfo.state = mState;
@@ -628,16 +652,21 @@
         /** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
         final boolean mShouldShowSystemDecorations;
 
+        /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */
+        final boolean mFixedContentMode;
+
         final int mGravity;
 
         OverlayFlags(
                 boolean secure,
                 boolean ownContentOnly,
                 boolean shouldShowSystemDecorations,
+                boolean fixedContentMode,
                 int gravity) {
             mSecure = secure;
             mOwnContentOnly = ownContentOnly;
             mShouldShowSystemDecorations = shouldShowSystemDecorations;
+            mFixedContentMode = fixedContentMode;
             mGravity = gravity;
         }
 
@@ -647,12 +676,14 @@
                         false /* secure */,
                         false /* ownContentOnly */,
                         false /* shouldShowSystemDecorations */,
+                        false /* fixedContentMode */,
                         Gravity.NO_GRAVITY);
             }
 
             boolean secure = false;
             boolean ownContentOnly = false;
             boolean shouldShowSystemDecorations = false;
+            boolean fixedContentMode = false;
             int gravity = Gravity.NO_GRAVITY;
             for (String flag: flagString.split(FLAG_SPLITTER)) {
                 if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
@@ -661,11 +692,14 @@
                     ownContentOnly = true;
                 } else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
                     shouldShowSystemDecorations = true;
+                } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) {
+                    fixedContentMode = true;
                 } else {
                     gravity = parseOverlayGravity(flag);
                 }
             }
-            return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
+            return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations,
+                    fixedContentMode, gravity);
         }
 
         @Override
@@ -674,6 +708,7 @@
                     .append("secure=").append(mSecure)
                     .append(", ownContentOnly=").append(mOwnContentOnly)
                     .append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+                    .append(", fixedContentMode=").append(mFixedContentMode)
                     .append(", gravity").append(Gravity.toString(mGravity))
                     .append("}")
                     .toString();
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 902eefa..89679c7 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -666,6 +666,12 @@
                 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
                 // The display is trusted since it is created by system.
                 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
+                if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+                    // The wifi display is allowed to switch content mode since FLAG_PRIVATE,
+                    // FLAG_OWN_CONTENT_ONLY, and FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS are not
+                    // enabled in WifiDisplayDevice#getDisplayDeviceInfoLocked().
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+                }
                 mInfo.displayShape =
                         DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false);
             }
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 7cc178d..f5228df 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@
             Flags::committedStateSeparateEvent
     );
 
+    private final FlagState mSeparateTimeouts = new FlagState(
+            Flags.FLAG_SEPARATE_TIMEOUTS,
+            Flags::separateTimeouts
+    );
+
     private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
             Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
             Flags::delayImplicitRrRegistrationUntilRrAccessed
@@ -608,6 +613,14 @@
     }
 
     /**
+     * @return {@code true} if the flag for having a separate timeouts for power groups
+     * is enabled
+     */
+    public boolean isSeparateTimeoutsEnabled() {
+        return mSeparateTimeouts.isEnabled();
+    }
+
+    /**
      * @return {@code true} if the flag for only explicit subscription for RR changes is enabled
      */
     public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
@@ -671,6 +684,7 @@
         pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
         pw.println(" " + mRefreshRateEventForForegroundApps);
         pw.println(" " + mCommittedStateSeparateEvent);
+        pw.println(" " + mSeparateTimeouts);
         pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
     }
 
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index a0064a9..007646f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -509,6 +509,14 @@
     }
 }
 
+
+flag {
+    name: "separate_timeouts"
+    namespace: "lse_desktop_experience"
+    description: "Allow separate timeouts for different power groups"
+    bug: "402356291"
+}
+
 flag {
     name: "delay_implicit_rr_registration_until_rr_accessed"
     namespace: "display_manager"
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2375775..fde9165 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5669,11 +5669,6 @@
         LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
     }
 
-    // TODO(b/352228316): Remove it once IMMIProxy is removed.
-    InputMethodManagerInternal getLocalService(){
-        return mInputMethodManagerInternal;
-    }
-
     private final class LocalServiceImpl extends InputMethodManagerInternal {
 
         @ImfLockFree
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 6a72cc7..7d9f2c2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -24,6 +24,7 @@
 import android.hardware.location.GeofenceHardwareImpl;
 import android.location.FusedBatchOptions;
 import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
@@ -35,6 +36,8 @@
 import android.location.IGpsGeofenceHardware;
 import android.location.Location;
 import android.location.LocationManager;
+import android.location.flags.Flags;
+import android.location.provider.IGnssAssistanceCallback;
 import android.location.util.identity.CallerIdentity;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -47,12 +50,13 @@
 import com.android.server.FgThread;
 import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.Injector;
+import com.android.server.location.provider.proxy.ProxyGnssAssistanceProvider;
 
 import java.io.FileDescriptor;
 import java.util.List;
 
 /** Manages Gnss providers and related Gnss functions for LocationManagerService. */
-public class GnssManagerService {
+public class GnssManagerService implements GnssNative.GnssAssistanceCallbacks {
 
     public static final String TAG = "GnssManager";
     public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
@@ -75,6 +79,8 @@
 
     private final GnssMetrics mGnssMetrics;
 
+    private @Nullable ProxyGnssAssistanceProvider mProxyGnssAssistanceProvider = null;
+
     public GnssManagerService(Context context, Injector injector, GnssNative gnssNative) {
         mContext = context.createAttributionContext(ATTRIBUTION_ID);
         mGnssNative = gnssNative;
@@ -100,6 +106,16 @@
     /** Called when system is ready. */
     public void onSystemReady() {
         mGnssLocationProvider.onSystemReady();
+
+        if (Flags.gnssAssistanceInterfaceJni()) {
+            mProxyGnssAssistanceProvider =
+                    ProxyGnssAssistanceProvider.createAndRegister(mContext);
+            if (mProxyGnssAssistanceProvider == null) {
+                Log.e(TAG, "no gnss assistance provider found");
+            } else {
+                mGnssNative.setGnssAssistanceCallbacks(this);
+            }
+        }
     }
 
     /** Retrieve the GnssLocationProvider. */
@@ -323,6 +339,29 @@
         }
     }
 
+    @Override
+    public void onRequestGnssAssistanceInject() {
+        if (!Flags.gnssAssistanceInterfaceJni()) {
+            return;
+        }
+        if (mProxyGnssAssistanceProvider == null) {
+            Log.e(TAG, "ProxyGnssAssistanceProvider is null");
+            return;
+        }
+        mProxyGnssAssistanceProvider.request(new IGnssAssistanceCallback.Stub() {
+            @Override
+            public void onError() {
+                Log.e(TAG, "GnssAssistanceCallback.onError");
+            }
+
+            @Override
+            public void onResult(GnssAssistance gnssAssistance) {
+                Log.d(TAG, "GnssAssistanceCallback.onResult");
+                mGnssNative.injectGnssAssistance(gnssAssistance);
+            }
+        });
+    }
+
     private class GnssCapabilitiesHalModule implements GnssNative.BaseCallbacks {
 
         GnssCapabilitiesHalModule(GnssNative gnssNative) {
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index c79a21a..7b4c563 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementsEvent;
@@ -30,6 +31,7 @@
 import android.location.GnssSignalType;
 import android.location.GnssStatus;
 import android.location.Location;
+import android.location.flags.Flags;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -275,6 +277,12 @@
         void onRequestPsdsDownload(int psdsType);
     }
 
+    /** Callbacks for HAL requesting GNSS assistance. */
+    public interface GnssAssistanceCallbacks {
+        /** On request GnssAssistance injection. */
+        void onRequestGnssAssistanceInject();
+    }
+
     /** Callbacks for AGPS functionality. */
     public interface AGpsCallbacks {
 
@@ -400,6 +408,7 @@
     private TimeCallbacks mTimeCallbacks;
     private LocationRequestCallbacks mLocationRequestCallbacks;
     private PsdsCallbacks mPsdsCallbacks;
+    private @Nullable GnssAssistanceCallbacks mGnssAssistanceCallbacks;
     private AGpsCallbacks mAGpsCallbacks;
     private NotificationCallbacks mNotificationCallbacks;
 
@@ -504,6 +513,16 @@
         mNotificationCallbacks = Objects.requireNonNull(callbacks);
     }
 
+    /** Sets GnssAssistanceCallbacks. */
+    public void setGnssAssistanceCallbacks(GnssAssistanceCallbacks callbacks) {
+        if (!Flags.gnssAssistanceInterfaceJni()) {
+            return;
+        }
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mGnssAssistanceCallbacks == null);
+        mGnssAssistanceCallbacks = Objects.requireNonNull(callbacks);
+    }
+
     /**
      * Registers with the HAL and allows callbacks to begin. Once registered with the native HAL,
      * no more callbacks can be added or set. Must only be called once.
@@ -1053,6 +1072,17 @@
         mGnssHal.injectNiSuplMessageData(data, length, slotIndex);
     }
 
+    /**
+     * Injects GNSS assistance data into the GNSS HAL.
+     */
+    public void injectGnssAssistance(GnssAssistance assistance) {
+        if (!Flags.gnssAssistanceInterfaceJni()) {
+            return;
+        }
+        Preconditions.checkState(mRegistered);
+        mGnssHal.injectGnssAssistance(assistance);
+    }
+
     @NativeEntryPoint
     void reportGnssServiceDied() {
         // Not necessary to clear (and restore) binder identity since it runs on another thread.
@@ -1269,6 +1299,15 @@
     }
 
     @NativeEntryPoint
+    void gnssAssistanceInjectRequest() {
+        if (!Flags.gnssAssistanceInterfaceJni() || mGnssAssistanceCallbacks == null) {
+            return;
+        }
+        Binder.withCleanCallingIdentity(
+                () -> mGnssAssistanceCallbacks.onRequestGnssAssistanceInject());
+    }
+
+    @NativeEntryPoint
     void reportGeofenceTransition(int geofenceId, Location location, int transition,
             long transitionTimestamp) {
         Binder.withCleanCallingIdentity(
@@ -1569,6 +1608,10 @@
         protected void injectNiSuplMessageData(byte[] data, int length, int slotIndex) {
             native_inject_ni_supl_message_data(data, length, slotIndex);
         }
+
+        protected void injectGnssAssistance(GnssAssistance gnssAssistance) {
+            native_inject_gnss_assistance(gnssAssistance);
+        }
     }
 
     // basic APIs
@@ -1718,4 +1761,7 @@
     private static native boolean native_supports_psds();
 
     private static native void native_inject_psds_data(byte[] data, int length, int psdsType);
+
+    // GNSS Assistance APIs
+    private static native void native_inject_gnss_assistance(GnssAssistance gnssAssistance);
 }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index dd52cce..3f2c222 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -46,7 +46,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 public class ConditionProviders extends ManagedServices {
 
@@ -203,14 +202,7 @@
 
     @Override
     protected void loadDefaultsFromConfig() {
-        for (String dndPackage : getDefaultDndAccessPackages(mContext)) {
-            addDefaultComponentOrPackage(dndPackage);
-        }
-    }
-
-    static List<String> getDefaultDndAccessPackages(Context context) {
-        ArrayList<String> packages = new ArrayList<>();
-        String defaultDndAccess = context.getResources().getString(
+        String defaultDndAccess = mContext.getResources().getString(
                 R.string.config_defaultDndAccessPackages);
         if (defaultDndAccess != null) {
             String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
@@ -218,10 +210,9 @@
                 if (TextUtils.isEmpty(dnds[i])) {
                     continue;
                 }
-                packages.add(dnds[i]);
+                addDefaultComponentOrPackage(dnds[i]);
             }
         }
-        return packages;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index dc7129cd..ea7ee4a 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
       "name": "CtsNotificationTestCases_notification"
     },
     {
-      "name": "FrameworksUiServicesTests_notification"
+      "name": "FrameworksUiServicesNotificationTests"
+    },
+    {
+      "name": "FrameworksUiServicesZenTests"
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java b/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
deleted file mode 100644
index d65954d..0000000
--- a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.notification;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.service.notification.SystemZenRules;
-import android.service.notification.ZenModeConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-class ZenConfigTrimmer {
-
-    private static final String TAG = "ZenConfigTrimmer";
-    private static final int MAXIMUM_PARCELED_SIZE = 150_000; // bytes
-
-    private final HashSet<String> mTrustedPackages;
-
-    ZenConfigTrimmer(Context context) {
-        mTrustedPackages = new HashSet<>();
-        mTrustedPackages.add(SystemZenRules.PACKAGE_ANDROID);
-        mTrustedPackages.addAll(ConditionProviders.getDefaultDndAccessPackages(context));
-    }
-
-    void trimToMaximumSize(ZenModeConfig config) {
-        Map<String, PackageRules> rulesPerPackage = new HashMap<>();
-        for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
-            PackageRules pkgRules = rulesPerPackage.computeIfAbsent(rule.pkg, PackageRules::new);
-            pkgRules.mRules.add(rule);
-        }
-
-        int totalSize = 0;
-        for (PackageRules pkgRules : rulesPerPackage.values()) {
-            totalSize += pkgRules.dataSize();
-        }
-
-        if (totalSize > MAXIMUM_PARCELED_SIZE) {
-            List<PackageRules> deletionCandidates = new ArrayList<>();
-            for (PackageRules pkgRules : rulesPerPackage.values()) {
-                if (!mTrustedPackages.contains(pkgRules.mPkg)) {
-                    deletionCandidates.add(pkgRules);
-                }
-            }
-            deletionCandidates.sort(Comparator.comparingInt(PackageRules::dataSize).reversed());
-
-            evictPackagesFromConfig(config, deletionCandidates, totalSize);
-        }
-    }
-
-    private static void evictPackagesFromConfig(ZenModeConfig config,
-            List<PackageRules> deletionCandidates, int currentSize) {
-        while (currentSize > MAXIMUM_PARCELED_SIZE && !deletionCandidates.isEmpty()) {
-            PackageRules rulesToDelete = deletionCandidates.removeFirst();
-            Slog.w(TAG, String.format("Evicting %s zen rules from package '%s' (%s bytes)",
-                    rulesToDelete.mRules.size(), rulesToDelete.mPkg, rulesToDelete.dataSize()));
-
-            for (ZenModeConfig.ZenRule rule : rulesToDelete.mRules) {
-                config.automaticRules.remove(rule.id);
-            }
-
-            currentSize -= rulesToDelete.dataSize();
-        }
-    }
-
-    private static class PackageRules {
-        private final String mPkg;
-        private final List<ZenModeConfig.ZenRule> mRules;
-        private int mParceledSize = -1;
-
-        PackageRules(String pkg) {
-            mPkg = pkg;
-            mRules = new ArrayList<>();
-        }
-
-        private int dataSize() {
-            if (mParceledSize >= 0) {
-                return mParceledSize;
-            }
-            Parcel parcel = Parcel.obtain();
-            try {
-                parcel.writeParcelableList(mRules, 0);
-                mParceledSize = parcel.dataSize();
-                return mParceledSize;
-            } finally {
-                parcel.recycle();
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8b09c2a..889df51 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -48,7 +48,6 @@
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.notification.Flags.preventZenDeviceEffectsWhileDriving;
-import static com.android.server.notification.Flags.limitZenConfigSize;
 
 import static java.util.Objects.requireNonNull;
 
@@ -193,7 +192,6 @@
     private final ConditionProviders.Config mServiceConfig;
     private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
     private final ZenModeEventLogger mZenModeEventLogger;
-    private final ZenConfigTrimmer mConfigTrimmer;
 
     @VisibleForTesting protected int mZenMode;
     @VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
@@ -228,7 +226,6 @@
         mClock = clock;
         addCallback(mMetrics);
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mConfigTrimmer = new ZenConfigTrimmer(mContext);
 
         mDefaultConfig = Flags.modesUi()
                 ? ZenModeConfig.getDefaultConfig()
@@ -2064,20 +2061,20 @@
                 Log.w(TAG, "Invalid config in setConfigLocked; " + config);
                 return false;
             }
-            if (limitZenConfigSize() && (origin == ORIGIN_APP || origin == ORIGIN_USER_IN_APP)) {
-                mConfigTrimmer.trimToMaximumSize(config);
-            }
-
             if (config.user != mUser) {
                 // simply store away for background users
-                mConfigs.put(config.user, config);
+                synchronized (mConfigLock) {
+                    mConfigs.put(config.user, config);
+                }
                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
                 return true;
             }
             // handle CPS backed conditions - danger! may modify config
             mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
 
-            mConfigs.put(config.user, config);
+            synchronized (mConfigLock) {
+                mConfigs.put(config.user, config);
+            }
             if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
             ZenLog.traceConfig(origin, reason, triggeringComponent, mConfig, config, callingUid);
 
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 346d65a..76cd5c8 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -212,16 +212,6 @@
 }
 
 flag {
-  name: "limit_zen_config_size"
-  namespace: "systemui"
-  description: "Enforce a maximum (serialized) size for the Zen configuration"
-  bug: "387498139"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "managed_services_concurrent_multiuser"
   namespace: "systemui"
   description: "Enables ManagedServices to support Concurrent multi user environment"
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 1fda478..92e8eb9 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -133,6 +133,38 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "file_patterns": [
         "core/java/.*Install.*",
@@ -288,6 +320,38 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "file_patterns": [
         "core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 092ec8e..233b577 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1078,7 +1078,7 @@
         mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
         mUserDataPreparer = userDataPreparer;
         mUserTypes = UserTypeFactory.getUserTypes();
-        invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
+        invalidateOwnerNameIfNecessary(getContextResources(), true /* forceUpdate */);
         synchronized (mPackagesLock) {
             mUsersDir = new File(dataDir, USER_INFO_DIR);
             mUsersDir.mkdirs();
@@ -1184,6 +1184,15 @@
                 && android.multiuser.Flags.enablePrivateSpaceFeatures();
     }
 
+    private Resources getSystemResources() {
+        return android.multiuser.Flags.useUnifiedResources()
+                ? getContextResources() : Resources.getSystem();
+    }
+
+    private Resources getContextResources() {
+        return mContext.getResources();
+    }
+
     /**
      * This method retrieves the  {@link UserManagerInternal} only for the purpose of
      * PackageManagerService construction.
@@ -1223,7 +1232,7 @@
             // Avoid marking pre-created users for removal.
             return;
         }
-        if (ui.lastLoggedInTime == 0 && ui.isGuest() && Resources.getSystem().getBoolean(
+        if (ui.lastLoggedInTime == 0 && ui.isGuest() && getSystemResources().getBoolean(
                 com.android.internal.R.bool.config_guestUserAutoCreated)) {
             // Avoid marking auto-created but not-yet-logged-in guest user for removal. Because a
             // new one will be created anyway, and this one doesn't have any personal data in it yet
@@ -1402,7 +1411,7 @@
         }
 
         if (isHeadlessSystemUserMode()) {
-            final int bootStrategy = mContext.getResources()
+            final int bootStrategy = getContextResources()
                     .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
             switch (bootStrategy) {
                 case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
@@ -2983,7 +2992,7 @@
     boolean isUserSwitcherEnabled(@UserIdInt int userId) {
         boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.USER_SWITCHER_ENABLED,
-                Resources.getSystem().getBoolean(com.android.internal
+                getSystemResources().getBoolean(com.android.internal
                         .R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
 
         return UserManager.supportsMultipleUsers()
@@ -4672,7 +4681,7 @@
             UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
             if ("Primary".equals(userData.info.name)) {
                 userData.info.name =
-                        mContext.getResources().getString(com.android.internal.R.string.owner_name);
+                        getContextResources().getString(com.android.internal.R.string.owner_name);
                 userIdsToWrite.add(userData.info.id);
             }
             userVersion = 1;
@@ -5002,7 +5011,7 @@
 
         final Bundle restrictions = new Bundle();
         try {
-            final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
+            final String[] defaultFirstUserRestrictions = getContextResources().getStringArray(
                     com.android.internal.R.array.config_defaultFirstUserRestrictions);
             for (String userRestriction : defaultFirstUserRestrictions) {
                 if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
@@ -6178,7 +6187,7 @@
             // If the user switch hasn't been explicitly toggled on or off by the user, turn it on.
             if (android.provider.Settings.Global.getString(mContext.getContentResolver(),
                     android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) {
-                if (Resources.getSystem().getBoolean(
+                if (getSystemResources().getBoolean(
                         com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) {
                     android.provider.Settings.Global.putInt(mContext.getContentResolver(),
                             android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
@@ -7490,7 +7499,6 @@
         final long now = System.currentTimeMillis();
         final long nowRealtime = SystemClock.elapsedRealtime();
         final StringBuilder sb = new StringBuilder();
-        final Resources resources = Resources.getSystem();
 
         if (args != null && args.length > 0) {
             switch (args[0]) {
@@ -7573,13 +7581,14 @@
         pw.println();
         int effectiveMaxSupportedUsers = UserManager.getMaxSupportedUsers();
         pw.print("  Max users: " + effectiveMaxSupportedUsers);
-        int defaultMaxSupportedUsers = resources.getInteger(R.integer.config_multiuserMaximumUsers);
+        int defaultMaxSupportedUsers = getSystemResources()
+                .getInteger(R.integer.config_multiuserMaximumUsers);
         if (effectiveMaxSupportedUsers != defaultMaxSupportedUsers) {
             pw.print(" (built-in value: " + defaultMaxSupportedUsers + ")");
         }
         pw.println(" (limit reached: " + isUserLimitReached() + ")");
         pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
-        pw.println("  All guests ephemeral: " + resources.getBoolean(
+        pw.println("  All guests ephemeral: " + getSystemResources().getBoolean(
                 com.android.internal.R.bool.config_guestUserEphemeral));
         pw.println("  Force ephemeral users: " + mForceEphemeralUsers);
         final boolean isHeadlessSystemUserMode = isHeadlessSystemUserMode();
@@ -7594,7 +7603,7 @@
             }
         }
         if (isHeadlessSystemUserMode) {
-            pw.println("  Can switch to headless system user: " + resources
+            pw.println("  Can switch to headless system user: " + getSystemResources()
                     .getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser));
         }
         pw.println("  User version: " + mUserVersion);
@@ -8536,8 +8545,7 @@
      * or downgraded to non-admin status.
      */
     public boolean isMainUserPermanentAdmin() {
-        return Resources.getSystem()
-                .getBoolean(R.bool.config_isMainUserPermanentAdmin);
+        return getSystemResources().getBoolean(R.bool.config_isMainUserPermanentAdmin);
     }
 
     /**
@@ -8546,8 +8554,7 @@
      * it is not a full user.
      */
     public boolean canSwitchToHeadlessSystemUser() {
-        return Resources.getSystem()
-                .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
+        return getSystemResources().getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 102dc07..417b5c7 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
@@ -36,8 +37,8 @@
 import android.os.BatteryStatsInternal;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IWakeLockCallback;
 import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
@@ -67,6 +68,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.input.InputManagerInternal;
@@ -147,7 +149,8 @@
     @Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
     private final TrustManager mTrustManager;
     private final Vibrator mVibrator;
-    private final WakeLockLog mWakeLockLog;
+    @NonNull private final WakeLockLog mPartialWakeLockLog;
+    @NonNull private final WakeLockLog mFullWakeLockLog;
     private final DisplayManagerInternal mDisplayManagerInternal;
 
     private final NotifierHandler mHandler;
@@ -250,7 +253,9 @@
         mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
 
-        mWakeLockLog = mInjector.getWakeLockLog(context);
+        mFullWakeLockLog = mInjector.getWakeLockLog(context);
+        mPartialWakeLockLog = mInjector.getWakeLockLog(context);
+
         // Initialize interactive state for battery stats.
         try {
             mBatteryStats.noteInteractive(true);
@@ -324,7 +329,8 @@
                     // Ignore
                 }
             }
-            mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
+            getWakeLockLog(flags).onWakeLockAcquired(tag,
+                    getUidForWakeLockLog(ownerUid, workSource), flags, /*eventTime=*/ -1);
         }
         mWakefulnessSessionObserver.onWakeLockAcquired(flags);
     }
@@ -473,7 +479,8 @@
                     // Ignore
                 }
             }
-            mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
+            getWakeLockLog(flags).onWakeLockReleased(tag,
+                    getUidForWakeLockLog(ownerUid, workSource), /*eventTime=*/ -1);
         }
         mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
     }
@@ -960,11 +967,18 @@
      * @param pw The stream to print to.
      */
     public void dump(PrintWriter pw) {
-        if (mWakeLockLog != null) {
-            mWakeLockLog.dump(pw);
-        }
+        pw.println("Notifier:");
 
-        mWakefulnessSessionObserver.dump(pw);
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("Partial Wakelock Log:");
+        mPartialWakeLockLog.dump(ipw);
+
+        ipw.println("");
+        ipw.println("Full Wakelock Log:");
+        mFullWakeLockLog.dump(ipw);
+
+        ipw.println("");
+        mWakefulnessSessionObserver.dump(ipw);
     }
 
     private void updatePendingBroadcastLocked() {
@@ -1232,7 +1246,9 @@
                 // Do Nothing
             }
         }
-        mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+
+        getWakeLockLog(flags).onWakeLockAcquired(tag, getUidForWakeLockLog(ownerUid, workSource),
+                flags, currentTime);
     }
 
     @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1253,7 +1269,8 @@
                 // Ignore
             }
         }
-        mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
+        getWakeLockLog(flags).onWakeLockReleased(tag, getUidForWakeLockLog(ownerUid, workSource),
+                currentTime);
     }
 
     @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1419,6 +1436,15 @@
         }
     }
 
+    private @NonNull WakeLockLog getWakeLockLog(int flags) {
+        return PowerManagerService.isScreenLock(flags) ? mFullWakeLockLog : mPartialWakeLockLog;
+    }
+
+    private int getUidForWakeLockLog(int ownerUid, WorkSource workSource) {
+        int attributionUid = workSource != null ? workSource.getAttributionUid() : -1;
+        return attributionUid != -1 ? attributionUid : ownerUid;
+    }
+
     private final class NotifierHandler extends Handler {
 
         public NotifierHandler(Looper looper) {
@@ -1501,7 +1527,7 @@
         /**
          * Gets the WakeLockLog object
          */
-        WakeLockLog getWakeLockLog(Context context);
+        @NonNull WakeLockLog getWakeLockLog(Context context);
 
         /**
          * Gets the AppOpsManager system service
@@ -1522,7 +1548,7 @@
         }
 
         @Override
-        public WakeLockLog getWakeLockLog(Context context) {
+        public @NonNull WakeLockLog getWakeLockLog(Context context) {
             return new WakeLockLog(context);
         }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dd454cd..3eac4b5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1723,9 +1723,16 @@
         }
     }
 
-    @SuppressWarnings("deprecation")
     private static boolean isScreenLock(final WakeLock wakeLock) {
-        switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+        return isScreenLock(wakeLock.mFlags);
+    }
+
+    /**
+     * Returns if a wakelock flag corresponds to a screen wake lock.
+     */
+    @SuppressWarnings("deprecation")
+    public static boolean isScreenLock(int flags) {
+        switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.FULL_WAKE_LOCK:
             case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
             case PowerManager.SCREEN_DIM_WAKE_LOCK:
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index eda222e..7f152d6 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -81,11 +81,12 @@
     private static final int TYPE_ACQUIRE = 0x1;
     private static final int TYPE_RELEASE = 0x2;
     private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
-    private static final int LOG_SIZE = 1024 * 10;
+    private static final int LOG_SIZE = 1024 * 3;
     private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;
 
-    private static final int TAG_DATABASE_SIZE = 128;
+    private static final int TAG_DATABASE_SIZE = 64;
     private static final int TAG_DATABASE_SIZE_MAX = 128;
+    private static final int TAG_DATABASE_STARTING_SIZE = 16;
 
     private static final int LEVEL_SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK = 0;
     private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
@@ -182,7 +183,7 @@
      * @param pw The {@code PrintWriter} to write to.
      */
     public void dump(PrintWriter pw) {
-        dump(pw, false);
+        dump(pw, /* includeTagDb= */ true);
     }
 
     @VisibleForTesting
@@ -1161,15 +1162,16 @@
      */
     static class TagDatabase {
         private final int mInvalidIndex;
-        private final TagData[] mArray;
+        private final int mMaxArraySize;
+        private TagData[] mArray;
         private Callback mCallback;
 
         TagDatabase(Injector injector) {
-            int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);
-
-            // Largest possible index used as "INVALID", hence the (size - 1) sizing.
-            mArray = new TagData[size - 1];
-            mInvalidIndex = size - 1;
+            // Largest possible index used as "INVALID", hence the (size - 1) sizing
+            mMaxArraySize = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX - 1);
+            int startingSize = Math.min(mMaxArraySize, injector.getTagDatabaseStartingSize());
+            mArray = new TagData[startingSize];
+            mInvalidIndex = mMaxArraySize;
         }
 
         @Override
@@ -1195,8 +1197,10 @@
             sb.append(", entries: ").append(entries);
             sb.append(", Bytes used: ").append(byteEstimate);
             if (DEBUG) {
-                sb.append(", Avg tag size: ").append(tagSize / tags);
-                sb.append("\n    ").append(Arrays.toString(mArray));
+                sb.append(", Avg tag size: ").append(tags == 0 ? 0 : (tagSize / tags));
+                for (int i = 0; i < mArray.length; i++) {
+                    sb.append("\n  [").append(i).append("] ").append(mArray[i]);
+                }
             }
             return sb.toString();
         }
@@ -1284,6 +1288,18 @@
                 return null;
             }
 
+            // We don't have a spot available, see if we can still increase the array size
+            if (firstAvailable == -1) {
+                if (mArray.length < mMaxArraySize) {
+                    int oldSize = mArray.length;
+                    int newSize = Math.min(oldSize * 2, mMaxArraySize);
+                    TagData[] newArray = new TagData[newSize];
+                    System.arraycopy(mArray, 0, newArray, 0, oldSize);
+                    mArray = newArray;
+                    firstAvailable = oldSize;
+                }
+            }
+
             // If we need to remove an index, report to listeners that we are removing an index.
             boolean useOldest = firstAvailable == -1;
             if (useOldest && mCallback != null) {
@@ -1402,6 +1418,10 @@
             return TAG_DATABASE_SIZE;
         }
 
+        public int getTagDatabaseStartingSize() {
+            return TAG_DATABASE_STARTING_SIZE;
+        }
+
         public int getLogSize() {
             return LOG_SIZE;
         }
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index ebc50fd..52d4555 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -67,6 +67,10 @@
             new FlagState(Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN,
                     Flags::wakelockAttributionViaWorkchain);
 
+    private final FlagState mDisableFrozenProcessWakelocks =
+            new FlagState(Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS,
+                    Flags::disableFrozenProcessWakelocks);
+
     /** Returns whether early-screen-timeout-detector is enabled on not. */
     public boolean isEarlyScreenTimeoutDetectorEnabled() {
         return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -121,6 +125,13 @@
     }
 
     /**
+     * @return Whether the feature to disable the frozen process wakelocks is enabled
+     */
+    public boolean isDisableFrozenProcessWakelocksEnabled() {
+        return mDisableFrozenProcessWakelocks.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
@@ -132,6 +143,7 @@
         pw.println(" " + mFrameworkWakelockInfo);
         pw.println(" " + mMoveWscLoggingToNotifier);
         pw.println(" " + mWakelockAttributionViaWorkchain);
+        pw.println(" " + mDisableFrozenProcessWakelocks);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index fefe195..ad8ec03 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -70,3 +70,10 @@
     description: "Feature flag to move logging of WakelockStateChanged atoms from BatteryStatsImpl to Notifier."
     bug: "352602149"
 }
+
+flag {
+    name: "disable_frozen_process_wakelocks"
+    namespace: "power"
+    description: "Feature flag to disable/enable wakelocks of a process when it is frozen/unfrozen"
+    bug: "291115867"
+}
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 93fd276..6872ca9 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -23,7 +23,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -32,14 +34,21 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.AdvancedProtectionManager;
+import android.security.advancedprotection.AdvancedProtectionManager.FeatureId;
+import android.security.advancedprotection.AdvancedProtectionManager.SupportDialogType;
 import android.security.advancedprotection.IAdvancedProtectionCallback;
 import android.security.advancedprotection.IAdvancedProtectionService;
+import android.security.advancedprotection.AdvancedProtectionProtoEnums;
 import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -51,7 +60,9 @@
 import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
 import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook;
 
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -61,6 +72,15 @@
     private static final int MODE_CHANGED = 0;
     private static final int CALLBACK_ADDED = 1;
 
+    // Shared preferences keys
+    private static final String PREFERENCE = "advanced_protection_preference";
+    private static final String ENABLED_CHANGE_TIME = "enabled_change_time";
+    private static final String LAST_DIALOG_FEATURE_ID = "last_dialog_feature_id";
+    private static final String LAST_DIALOG_TYPE = "last_dialog_type";
+    private static final String LAST_DIALOG_HOURS_SINCE_ENABLED = "last_dialog_hours_since_enabled";
+    private static final String LAST_DIALOG_LEARN_MORE_CLICKED = "last_dialog_learn_more_clicked";
+    private static final long MILLIS_PER_HOUR = 60 * 60 * 1000;
+
     private final Context mContext;
     private final Handler mHandler;
     private final AdvancedProtectionStore mStore;
@@ -72,6 +92,10 @@
     // For tracking only - not called on state change
     private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
 
+    // Used to store logging data
+    private SharedPreferences mSharedPreferences;
+    private boolean mEmitLogs = true;
+
     private AdvancedProtectionService(@NonNull Context context) {
         super(PermissionEnforcer.fromContext(context));
         mContext = context;
@@ -126,6 +150,8 @@
         if (provider != null) {
             mProviders.add(provider);
         }
+
+        mEmitLogs = false;
     }
 
     @Override
@@ -178,7 +204,7 @@
                 if (enabled != isAdvancedProtectionEnabledInternal()) {
                     mStore.store(enabled);
                     sendModeChanged(enabled);
-                    Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
+                    logAdvancedProtectionEnabled(enabled);
                 }
             }
         } finally {
@@ -188,6 +214,91 @@
 
     @Override
     @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+    public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+            boolean learnMoreClicked) {
+        logDialogShown_enforcePermission();
+
+        if (!mEmitLogs) {
+            return;
+        }
+
+        int hoursSinceEnabled = hoursSinceLastChange();
+        FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_SUPPORT_DIALOG_DISPLAYED,
+                /*feature_id*/ featureIdToLogEnum(featureId),
+                /*dialogue_type*/ dialogueTypeToLogEnum(type),
+                /*learn_more_clicked*/ learnMoreClicked,
+                /*hours_since_last_change*/ hoursSinceEnabled);
+
+        getSharedPreferences().edit()
+                .putInt(LAST_DIALOG_FEATURE_ID, featureId)
+                .putInt(LAST_DIALOG_TYPE, type)
+                .putBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, learnMoreClicked)
+                .putInt(LAST_DIALOG_HOURS_SINCE_ENABLED, hoursSinceEnabled)
+                .apply();
+    }
+
+    private int featureIdToLogEnum(@FeatureId int featureId) {
+        switch (featureId) {
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_CELLULAR_2G;
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_USB;
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_WEP;
+            case AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_ENABLE_MTE;
+            default:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_UNKNOWN;
+        }
+    }
+
+    private int dialogueTypeToLogEnum(@SupportDialogType int type) {
+        switch (type) {
+            case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+            case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_BLOCKED_INTERACTION;
+            case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_DISABLED_SETTING;
+            default:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+        }
+    }
+
+    private void logAdvancedProtectionEnabled(boolean enabled) {
+        if (!mEmitLogs) {
+            return;
+        }
+
+        Slog.i(TAG, "Advanced protection has been " + (enabled ? "enabled" : "disabled"));
+        SharedPreferences prefs = getSharedPreferences();
+        FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_STATE_CHANGED,
+                /*enabled*/ enabled,
+                /*hours_since_enabled*/ hoursSinceLastChange(),
+                /*last_dialog_feature_id*/ featureIdToLogEnum(
+                    prefs.getInt(LAST_DIALOG_FEATURE_ID, -1)),
+                /*_type*/ dialogueTypeToLogEnum(prefs.getInt(LAST_DIALOG_TYPE, -1)),
+                /*_learn_more_clicked*/ prefs.getBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, false),
+                /*_hours_since_enabled*/ prefs.getInt(LAST_DIALOG_HOURS_SINCE_ENABLED, -1));
+        prefs.edit()
+                .putLong(ENABLED_CHANGE_TIME, System.currentTimeMillis())
+                .apply();
+    }
+
+    private int hoursSinceLastChange() {
+        int hoursSinceEnabled = -1;
+        long lastChangeTimeMillis = getSharedPreferences().getLong(ENABLED_CHANGE_TIME, -1);
+        if (lastChangeTimeMillis != -1) {
+            hoursSinceEnabled = (int)
+                    ((System.currentTimeMillis() - lastChangeTimeMillis) / MILLIS_PER_HOUR);
+        }
+        return hoursSinceEnabled;
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
     public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
         getAdvancedProtectionFeatures_enforcePermission();
         List<AdvancedProtectionFeature> features = new ArrayList<>();
@@ -213,6 +324,30 @@
                 .exec(this, in, out, err, args, callback, resultReceiver);
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+        writer.println("AdvancedProtectionService");
+        writer.println("  isAdvancedProtectionEnabled: " + isAdvancedProtectionEnabledInternal());
+        writer.println("  mHooks.size(): " + mHooks.size());
+        writer.println("  mCallbacks.size(): " + mCallbacks.size());
+        writer.println("  mProviders.size(): " + mProviders.size());
+
+        writer.println("Hooks: ");
+        mHooks.stream().forEach(hook -> {
+            writer.println("    " + hook.getClass().getSimpleName() +
+                                   " available: " + hook.isAvailable());
+        });
+        writer.println("  Providers: ");
+        mProviders.stream().forEach(provider -> {
+            writer.println("    " + provider.getClass().getSimpleName());
+            provider.getFeatures().stream().forEach(feature -> {
+                writer.println("      " + feature.getClass().getSimpleName());
+            });
+        });
+        writer.println("  mSharedPreferences: " + getSharedPreferences().getAll());
+    }
+
     void sendModeChanged(boolean enabled) {
         Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1)
                 .sendToTarget();
@@ -224,6 +359,22 @@
                 .sendToTarget();
     }
 
+    private SharedPreferences getSharedPreferences() {
+        if (mSharedPreferences == null) {
+            initSharedPreferences();
+        }
+        return mSharedPreferences;
+    }
+
+    private synchronized void initSharedPreferences() {
+        if (mSharedPreferences == null) {
+            Context deviceContext = mContext.createDeviceProtectedStorageContext();
+            File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREFERENCE);
+            mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs,
+                    Context.MODE_PRIVATE);
+        }
+    }
+
     public static final class Lifecycle extends SystemService {
         private final AdvancedProtectionService mService;
 
diff --git a/services/core/java/com/android/server/sensors/OWNERS b/services/core/java/com/android/server/sensors/OWNERS
new file mode 100644
index 0000000..6b22473
--- /dev/null
+++ b/services/core/java/com/android/server/sensors/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/services/sensorservice/OWNERS
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 7ff4ade..9636cc6 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,7 @@
 package com.android.server.sensors;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.SensorDirectChannel;
 import android.os.ParcelFileDescriptor;
 
@@ -71,7 +72,7 @@
     /**
      * Sends an event for the runtime sensor with the given handle to the framework.
      *
-     * Only relevant for sending runtime sensor events. @see #createRuntimeSensor.
+     * <p>Only relevant for sending runtime sensor events. @see #createRuntimeSensor.</p>
      *
      * @param handle The sensor handle.
      * @param type The type of the sensor.
@@ -83,6 +84,21 @@
             @NonNull float[] values);
 
     /**
+     * Sends an additional info event for the runtime sensor with the given handle to the framework.
+     *
+     * <p>Only relevant for runtime sensors. @see #createRuntimeSensor.</p>
+     *
+     * @param handle The sensor handle.
+     * @param type The type of payload data.
+     * @param serial The sequence number of this frame for this type.
+     * @param timestampNanos Timestamp of the event.
+     * @param values The payload data represented in float values.
+     * @return Whether the event injection was successful.
+     */
+    public abstract boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+            long timestampNanos, @Nullable float[] values);
+
+    /**
      * Listener for proximity sensor state changes.
      */
     public interface ProximityActiveListener {
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 3de1910..0d31b22 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -19,6 +19,7 @@
 import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.util.ArrayMap;
 
@@ -62,6 +63,9 @@
     private static native void unregisterRuntimeSensorNative(long ptr, int handle);
     private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
             long timestampNanos, float[] values);
+    private static native boolean sendRuntimeSensorAdditionalInfoNative(long ptr, int handle,
+            int type, int serial, long timestampNanos, float[] values);
+
 
     public SensorService(Context ctx) {
         super(ctx);
@@ -129,6 +133,18 @@
         }
 
         @Override
+        public boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+                long timestampNanos, @Nullable float[] values) {
+            synchronized (mLock) {
+                if (!mRuntimeSensorHandles.contains(handle)) {
+                    return false;
+                }
+                return sendRuntimeSensorAdditionalInfoNative(mPtr, handle, type, serial,
+                        timestampNanos, values);
+            }
+        }
+
+        @Override
         public void addProximityActiveListener(@NonNull Executor executor,
                 @NonNull ProximityActiveListener listener) {
             Objects.requireNonNull(executor, "executor must not be null");
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 6e640d8..424439d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -856,10 +856,14 @@
             BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options);
             wallpaperImageSize.set(options.outWidth, options.outHeight);
         }
+        boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+                == View.LAYOUT_DIRECTION_RTL;
+        Rect croppedImageBound = getCrop(displaySize, mDefaultDisplayInfo, wallpaperImageSize,
+                getRelativeCropHints(wallpaperData), isRtl);
 
-        double maxDisplayToImageRatio = Math.max((double) displaySize.x / wallpaperImageSize.x,
-                (double) displaySize.y / wallpaperImageSize.y);
-        if (maxDisplayToImageRatio > 1.5) {
+        double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(),
+                (double) displaySize.y / croppedImageBound.height());
+        if (maxDisplayToImageRatio > 1.3) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index ce3ad88..d935432 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -73,9 +73,12 @@
         final Rect stableBounds = new Rect();
         task.getDisplayArea().getStableRect(stableBounds);
 
-        // If the options bounds size is flexible, update size with calculated desired size.
+        final boolean hasFullscreenOverride = activity != null
+                && activity.mAppCompatController.getAspectRatioOverrides().hasFullscreenOverride();
+        // If the options bounds size is flexible and no fullscreen override has been applied,
+        // update size with calculated desired size.
         final boolean updateOptionBoundsSize = options != null
-                && options.getFlexibleLaunchSize();
+                && options.getFlexibleLaunchSize() && !hasFullscreenOverride;
         // If cascading is also enabled, the position of the options bounds must be respected
         // during the size update.
         final boolean shouldRespectOptionPosition =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 76a39d9..59a0429 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -41,6 +41,7 @@
 import static android.util.TypedValue.COMPLEX_UNIT_MASK;
 import static android.util.TypedValue.COMPLEX_UNIT_SHIFT;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -2161,7 +2162,7 @@
     /** Re-show the previously hidden windows if all seamless rotated windows are done. */
     void finishAsyncRotationIfPossible() {
         final AsyncRotationController controller = mAsyncRotationController;
-        if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
+        if (controller != null) {
             controller.completeAll();
             mAsyncRotationController = null;
         }
@@ -2238,11 +2239,7 @@
      */
     private void applyRotation(final int oldRotation, final int rotation) {
         mDisplayRotation.applyCurrentRotation(rotation);
-        final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null;
-        final boolean rotateSeamlessly =
-                mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
-        final Transaction transaction =
-                shellTransitions ? getSyncTransaction() : getPendingTransaction();
+
         // We need to update our screen size information to match the new rotation. If the rotation
         // has actually changed then this method will return true and, according to the comment at
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -2250,25 +2247,13 @@
         // #computeScreenConfiguration() later.
         updateDisplayAndOrientation(null /* outConfig */);
 
-        if (!shellTransitions) {
-            forAllWindows(w -> {
-                w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
-            }, true /* traverseTopToBottom */);
-            mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
-            if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
-                // Make sure DisplayRotation#isRotatingSeamlessly() will return false.
-                mDisplayRotation.cancelSeamlessRotation();
-            }
-        }
+        // Before setDisplayProjection is applied by the start transaction of transition,
+        // set the transform hint to avoid using surface in old rotation.
+        setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
+        // The sync transaction should already contains setDisplayProjection, so unset the
+        // hint to restore the natural state when the transaction is applied.
+        getSyncTransaction().unsetFixedTransformHint(mSurfaceControl);
 
-        if (shellTransitions) {
-            // Before setDisplayProjection is applied by the start transaction of transition,
-            // set the transform hint to avoid using surface in old rotation.
-            setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
-            // The sync transaction should already contains setDisplayProjection, so unset the
-            // hint to restore the natural state when the transaction is applied.
-            transaction.unsetFixedTransformHint(mSurfaceControl);
-        }
         scheduleAnimation();
 
         mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
@@ -2870,8 +2855,7 @@
         // If the transition finished callback cannot match the token for some reason, make sure the
         // rotated state is cleared if it is already invisible.
         if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
-                && !mFixedRotationLaunchingApp.isVisible()
-                && !mDisplayRotation.isRotatingSeamlessly()) {
+                && !mFixedRotationLaunchingApp.isVisible()) {
             clearFixedRotationLaunchingApp();
         }
         // If there won't be a transition to notify the launch is done, then it should be ready to
@@ -3283,26 +3267,30 @@
                 /* inTopology= */ shouldShowContent);
     }
 
-     /**
-      * Whether the display is allowed to switch the content mode between extended and mirroring.
-      * If the content mode is extended, the display will start home activity and show system
-      * decorations, such as wallpapaer, status bar and navigation bar.
-      * If the content mode is mirroring, the display will not show home activity or system
-      * decorations.
-      * The content mode is switched when {@link Display#canHostTasks()} changes.
-      *
-      * Note that we only allow displays that are able to show system decorations to use the content
-      * mode switch; however, not all displays that are able to show system decorations are allowed
-      * to use the content mode switch.
-      */
-     boolean allowContentModeSwitch() {
+    /**
+     * Whether the display is allowed to switch the content mode between extended and mirroring.
+     * If the content mode is extended, the display will start home activity and show system
+     * decorations, such as wallpapaer, status bar and navigation bar.
+     * If the content mode is mirroring, the display will not show home activity or system
+     * decorations.
+     * The content mode is switched when {@link Display#canHostTasks()} changes.
+     *
+     * Note that we only allow displays that are able to show system decorations to use the content
+     * mode switch; however, not all displays that are able to show system decorations are allowed
+     * to use the content mode switch.
+     */
+    boolean allowContentModeSwitch() {
+        if ((mDisplay.getFlags() & FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0) {
+            return false;
+        }
+
         // The default display should always show system decorations.
         if (isDefaultDisplay) {
             return false;
         }
 
-        // Private display should never show system decorations.
-        if (isPrivate()) {
+        // Private or untrusted display should never show system decorations.
+        if (isPrivate() || !isTrusted()) {
             return false;
         }
 
@@ -3310,14 +3298,9 @@
             return false;
         }
 
-        // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
-        if ((mDisplay.getFlags() & Display.FLAG_REAR) != 0) {
-            return false;
-        }
-
-        // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
-        // Virtual displays cannot add or remove system decorations during their lifecycle.
-        if (mDisplay.getType() == Display.TYPE_VIRTUAL) {
+        // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should always show system
+        // decorations, and should not switch the content mode.
+        if ((mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
             return false;
         }
 
@@ -5072,7 +5055,7 @@
         }
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
-        if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
+        if (!inTransition()) {
             mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                     mLastHasContent,
                     mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 9cf792d..9edbb70 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -168,19 +168,6 @@
     private int mDeferredRotationPauseCount;
 
     /**
-     * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
-     * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
-     * so we need to track when this hits zero so we can apply deferred orientation updates.
-     */
-    private int mSeamlessRotationCount;
-
-    /**
-     * True in the interval from starting seamless rotation until the last rotated window draws in
-     * the new orientation.
-     */
-    private boolean mRotatingSeamlessly;
-
-    /**
      * Behavior of rotation suggestions.
      *
      * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
@@ -630,15 +617,6 @@
             return true;
         }
 
-        if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
-            // The screen rotation animation uses a screenshot to freeze the screen while windows
-            // resize underneath. When we are rotating seamlessly, we allow the elements to
-            // transition to their rotated state independently and without a freeze required.
-            prepareSeamlessRotation();
-        } else {
-            cancelSeamlessRotation();
-        }
-
         // Give a remote handler (system ui) some time to reposition things.
         startRemoteRotation(oldRotation, mRotation);
 
@@ -677,42 +655,6 @@
         }
     }
 
-    /**
-     * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
-     * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
-     * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
-     * and it doesn't choose seamless rotation.
-     */
-    void cancelSeamlessRotation() {
-        if (!mRotatingSeamlessly) {
-            return;
-        }
-        mDisplayContent.forAllWindows(w -> {
-            if (w.mSeamlesslyRotated) {
-                w.cancelSeamlessRotation();
-                w.mSeamlesslyRotated = false;
-            }
-        }, true /* traverseTopToBottom */);
-        mSeamlessRotationCount = 0;
-        mRotatingSeamlessly = false;
-        mDisplayContent.finishAsyncRotationIfPossible();
-    }
-
-    private void prepareSeamlessRotation() {
-        // We are careful to reset this in case a window was removed before it finished
-        // seamless rotation.
-        mSeamlessRotationCount = 0;
-        mRotatingSeamlessly = true;
-    }
-
-    boolean isRotatingSeamlessly() {
-        return mRotatingSeamlessly;
-    }
-
-    boolean hasSeamlessRotatingWindow() {
-        return mSeamlessRotationCount > 0;
-    }
-
     @VisibleForTesting
     boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
         // Display doesn't need to be frozen because application has been started in correct
@@ -750,13 +692,6 @@
             return false;
         }
 
-        // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
-        // complete (that is, waiting for windows to redraw). It's tempting to check
-        // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
-        if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
-            return false;
-        }
-
         return true;
     }
 
@@ -774,28 +709,6 @@
         return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180;
     }
 
-    void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
-        if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
-            return;
-        }
-
-        w.mSeamlesslyRotated = seamlesslyRotated;
-        if (seamlesslyRotated) {
-            mSeamlessRotationCount++;
-        } else {
-            mSeamlessRotationCount--;
-        }
-        if (mSeamlessRotationCount == 0) {
-            ProtoLog.i(WM_DEBUG_ORIENTATION,
-                    "Performing post-rotate rotation after seamless rotation");
-            // Finish seamless rotation.
-            mRotatingSeamlessly = false;
-            mDisplayContent.finishAsyncRotationIfPossible();
-
-            updateRotationAndSendNewConfigIfChanged();
-        }
-    }
-
     void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
         mFixedToUserRotation = fixedToUserRotation;
 
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 040bbe4..234fbd2 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -82,6 +82,13 @@
      */
     private boolean mGivenInsetsReady = false;
 
+    /**
+     * The last state of the windowContainer. This is used to reset server visibility, in case of
+     * the IME (temporarily) redrawing  (e.g. during a rotation), to dispatch the control with
+     * leash again after it has finished drawing.
+     */
+    private boolean mLastDrawn = false;
+
     ImeInsetsSourceProvider(@NonNull InsetsSource source,
             @NonNull InsetsStateController stateController,
             @NonNull DisplayContent displayContent) {
@@ -97,6 +104,7 @@
             final WindowState ws =
                     mWindowContainer != null ? mWindowContainer.asWindowState() : null;
             final boolean givenInsetsPending = ws != null && ws.mGivenInsetsPending;
+            mLastDrawn = ws != null && ws.isDrawn();
 
             // isLeashReadyForDispatching (used to dispatch the leash of the control) is
             // depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
@@ -158,6 +166,35 @@
         }
     }
 
+    /**
+     * This is used to determine the desired serverVisibility state. For the IME, just having a
+     * window state that would be visible by policy is not enough.
+     */
+    @Override
+    protected boolean isSurfaceVisible() {
+        final boolean isSurfaceVisible = super.isSurfaceVisible();
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            final WindowState windowState = mWindowContainer.asWindowState();
+            if (mControl != null && windowState != null) {
+                final boolean isDrawn = windowState.isDrawn();
+                if (!isServerVisible() && isSurfaceVisible) {
+                    // In case the IME becomes visible, we need to check if it is already drawn and
+                    // does not have given insets pending. If it's not yet drawn, we do not set
+                    // server visibility
+                    return isDrawn && !windowState.mGivenInsetsPending;
+                } else if (mLastDrawn && !isDrawn) {
+                    // If the IME was drawn before, but is not drawn anymore, we need to reset
+                    // server visibility, which will also reset {@link
+                    // ImeInsetsSourceProvider#mGivenInsetsReady}. Otherwise, the new control
+                    // with leash won't be dispatched after the surface has redrawn.
+                    return false;
+                }
+            }
+        }
+        return isSurfaceVisible;
+    }
+
+
     @Nullable
     @Override
     InsetsSourceControl getControl(InsetsControlTarget target) {
@@ -779,6 +816,8 @@
         pw.print(prefix);
         pw.print("mImeShowing=");
         pw.print(mImeShowing);
+        pw.print(" mLastDrawn=");
+        pw.print(mLastDrawn);
         if (mImeRequester != null) {
             pw.print(prefix);
             pw.print("showImePostLayout pending for mImeRequester=");
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1b693fc..dcbb781 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -176,6 +176,16 @@
     }
 
     /**
+     * @return Whether the current window container has a visible surface.
+     */
+    protected boolean isSurfaceVisible() {
+        final WindowState windowState = mWindowContainer.asWindowState();
+        return windowState != null
+                ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
+                : mWindowContainer.isVisibleRequested();
+    }
+
+    /**
      * Updates the window container that currently backs this source.
      *
      * @param windowContainer The window container that links to this source.
@@ -368,20 +378,9 @@
         if (mWindowContainer == null) {
             return;
         }
-        WindowState windowState = mWindowContainer.asWindowState();
-        boolean isServerVisible = windowState != null
-                ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
-                : mWindowContainer.isVisibleRequested();
+        final WindowState windowState = mWindowContainer.asWindowState();
+        final boolean isServerVisible = isSurfaceVisible();
 
-        if (android.view.inputmethod.Flags.refactorInsetsController()) {
-            if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible
-                    && isServerVisible && windowState != null) {
-                // in case the IME becomes visible, we need to check if it is already drawn and
-                // does not have given insets pending. If it's not yet drawn, we do not set
-                // server visibility
-                isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending;
-            }
-        }
         final boolean serverVisibleChanged = mServerVisible != isServerVisible;
         setServerVisible(isServerVisible);
         if (mControl != null && mControlTarget != null) {
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 6dd7d35..6e59828 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -21,20 +21,13 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.PictureInPictureParams;
 import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.RotationUtils;
 import android.util.Slog;
 import android.view.IPinnedTaskListener;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
 
 import java.io.PrintWriter;
 
@@ -71,11 +64,7 @@
      * based on the new rotation.
      */
     private Rect mDestRotatedBounds;
-    /**
-     * Non-null if the entering PiP task from recents animation will cause display rotation to
-     * change. The transaction is based on the old rotation.
-     */
-    private PictureInPictureSurfaceTransaction mPipTransaction;
+
     /** Whether to skip task configuration change once. */
     private boolean mFreezingTaskConfig;
     /** Defer display orientation change if the PiP task is animating across orientations. */
@@ -212,14 +201,12 @@
     }
 
     /**
-     * Sets the transaction for {@link #startSeamlessRotationIfNeeded} if the orientation of display
-     * will be changed. This is only called when finishing recents animation with pending
-     * orientation change that will be handled by
-     * {@link DisplayContent.FixedRotationTransitionListener#onFinishRecentsAnimation}.
+     * Sets a hint if the orientation of display will be changed. This is only called when
+     * finishing recents animation with pending orientation change that will be handled by
+     * {@link DisplayContent.FixedRotationTransitionListener}.
      */
-    void setEnterPipTransaction(PictureInPictureSurfaceTransaction tx) {
+    void setEnterPipWithRotatedTransientLaunch() {
         mFreezingTaskConfig = true;
-        mPipTransaction = tx;
     }
 
     /** Called when the activity in PiP task has PiP windowing mode (at the end of animation). */
@@ -233,81 +220,6 @@
     }
 
     /**
-     * Resets rotation and applies scale and position to PiP task surface to match the current
-     * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
-     * receives the callback of fixed rotation completion.
-     */
-    void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
-            int oldRotation, int newRotation) {
-        final Rect bounds = mDestRotatedBounds;
-        final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
-        final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
-        if (bounds == null && emptyPipPositionTx) {
-            return;
-        }
-        final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task pinnedTask = taskArea.getRootPinnedTask();
-        if (pinnedTask == null) {
-            return;
-        }
-
-        mDestRotatedBounds = null;
-        mPipTransaction = null;
-        final Rect areaBounds = taskArea.getBounds();
-        if (!emptyPipPositionTx) {
-            // The transaction from recents animation is in old rotation. So the position needs to
-            // be rotated.
-            float dx = pipTx.mPosition.x;
-            float dy = pipTx.mPosition.y;
-            final Matrix matrix = pipTx.getMatrix();
-            if (pipTx.mRotation == 90) {
-                dx = pipTx.mPosition.y;
-                dy = areaBounds.right - pipTx.mPosition.x;
-                matrix.postRotate(-90);
-            } else if (pipTx.mRotation == -90) {
-                dx = areaBounds.bottom - pipTx.mPosition.y;
-                dy = pipTx.mPosition.x;
-                matrix.postRotate(90);
-            }
-            matrix.postTranslate(dx, dy);
-            final SurfaceControl leash = pinnedTask.getSurfaceControl();
-            t.setMatrix(leash, matrix, new float[9]);
-            if (pipTx.hasCornerRadiusSet()) {
-                t.setCornerRadius(leash, pipTx.mCornerRadius);
-            }
-            Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
-            return;
-        }
-
-        final PictureInPictureParams params = pinnedTask.getPictureInPictureParams();
-        final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
-                ? params.getSourceRectHint()
-                : null;
-        Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
-        final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
-        // Adjust for display cutout if applicable.
-        if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
-            if (pinnedTask.getDisplayCutoutInsets() != null) {
-                final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
-                final Rect displayCutoutInsets = RotationUtils.rotateInsets(
-                        Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
-                sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
-            }
-        }
-        final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
-                ? sourceHintRect : areaBounds;
-        final int w = contentBounds.width();
-        final int h = contentBounds.height();
-        final float scale = w <= h ? (float) bounds.width() / w : (float) bounds.height() / h;
-        final int insetLeft = (int) ((contentBounds.left - areaBounds.left) * scale + .5f);
-        final int insetTop = (int) ((contentBounds.top - areaBounds.top) * scale + .5f);
-        final Matrix matrix = new Matrix();
-        matrix.setScale(scale, scale);
-        matrix.postTranslate(bounds.left - insetLeft, bounds.top - insetTop);
-        t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]);
-    }
-
-    /**
      * Returns {@code true} to skip {@link Task#onConfigurationChanged} because it is expected that
      * there will be a orientation change and a PiP configuration change.
      */
@@ -321,7 +233,6 @@
         mFreezingTaskConfig = false;
         mDeferOrientationChanging = false;
         mDestRotatedBounds = null;
-        mPipTransaction = null;
     }
 
     /**
@@ -381,9 +292,6 @@
         if (mDestRotatedBounds != null) {
             pw.println(prefix + "  mPendingBounds=" + mDestRotatedBounds);
         }
-        if (mPipTransaction != null) {
-            pw.println(prefix + "  mPipTransaction=" + mPipTransaction);
-        }
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
         pw.println(prefix + "  mImeHeight=" + mImeHeight);
         pw.println(prefix + "  mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 609302c..39d1062 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,8 +79,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -655,9 +653,6 @@
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
             final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
-            if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
-            }
             if (pendingChanges != 0) {
                 hasChanges = true;
             }
@@ -858,7 +853,8 @@
 
         // Post these on a handler such that we don't call into power manager service while
         // holding the window manager lock to avoid lock contention with power manager lock.
-        mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides)
+        // Send a copy of the brightness overrides as they may be cleared before being sent out.
+        mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides.clone())
                 .sendToTarget();
         mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
 
@@ -1024,18 +1020,6 @@
         return changed;
     }
 
-    boolean copyAnimToLayoutParams() {
-        boolean doRequest = false;
-
-        final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
-        if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
-            mUpdateRotation = true;
-            doRequest = true;
-        }
-
-        return doRequest;
-    }
-
     private final class MyHandler extends Handler {
 
         public MyHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 803c21c..30313fc 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1249,7 +1249,7 @@
                 // Skip dispatching the change for PiP task to avoid its activity drawing for the
                 // intermediate state which will cause flickering. The final PiP bounds in new
                 // rotation will be applied by PipTransition.
-                ar.mDisplayContent.mPinnedTaskController.setEnterPipTransaction(null);
+                ar.mDisplayContent.mPinnedTaskController.setEnterPipWithRotatedTransientLaunch();
             }
             return inPip;
         }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 80137a2..3f2b40c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -65,9 +65,6 @@
     /** Time of current animation step. Reset on each iteration */
     long mCurrentTime;
 
-    int mBulkUpdateParams = 0;
-    Object mLastWindowFreezeSource;
-
     private boolean mInitialized = false;
 
     private Choreographer mChoreographer;
@@ -145,7 +142,6 @@
         final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
         boolean rootAnimating = false;
         mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
-        mBulkUpdateParams = 0;
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
         }
@@ -202,8 +198,7 @@
         }
 
         final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
-        final boolean doRequest = mBulkUpdateParams != 0 && root.copyAnimToLayoutParams();
-        if (hasPendingLayoutChanges || doRequest) {
+        if (hasPendingLayoutChanges) {
             mService.mWindowPlacerLocked.requestTraversal();
         }
 
@@ -245,7 +240,6 @@
 
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit"
-                    + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
                     + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
         }
     }
@@ -265,17 +259,6 @@
         mRunningExpensiveAnimations = runningExpensiveAnimations;
     }
 
-    private static String bulkUpdateParamsToString(int bulkUpdateParams) {
-        StringBuilder builder = new StringBuilder(128);
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
-            builder.append(" UPDATE_ROTATION");
-        }
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
-            builder.append(" SET_WALLPAPER_ACTION_PENDING");
-        }
-        return builder.toString();
-    }
-
     public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
         final String subPrefix = "  " + prefix;
 
@@ -292,11 +275,6 @@
             pw.print(prefix); pw.print("mCurrentTime=");
                     pw.println(TimeUtils.formatUptime(mCurrentTime));
         }
-        if (mBulkUpdateParams != 0) {
-            pw.print(prefix); pw.print("mBulkUpdateParams=0x");
-                    pw.print(Integer.toHexString(mBulkUpdateParams));
-                    pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
-        }
     }
 
     void scheduleAnimation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c078d67b..d7626f0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -198,6 +198,8 @@
 import android.graphics.Region;
 import android.hardware.configstore.V1_0.OptionalBool;
 import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputSettings;
@@ -1032,6 +1034,21 @@
     PowerManager mPowerManager;
     PowerManagerInternal mPowerManagerInternal;
 
+    private DeviceStateManager mDeviceStateManager;
+    private DeviceStateCallback mDeviceStateCallback;
+    private class DeviceStateCallback implements DeviceStateManager.DeviceStateCallback {
+        private DeviceState mCurrentDeviceState;
+        @Override
+        public void onDeviceStateChanged(@NonNull DeviceState state) {
+            mCurrentDeviceState = state;
+        }
+
+        boolean isInRearDisplayOuterDefaultState() {
+            return mCurrentDeviceState != null && mCurrentDeviceState
+                    .hasProperties(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+        }
+    }
+
     private float mWindowAnimationScaleSetting = 1.0f;
     private float mTransitionAnimationScaleSetting = 1.0f;
     private float mAnimatorDurationScaleSetting = 1.0f;
@@ -1317,6 +1334,10 @@
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
 
+        mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+        mDeviceStateCallback = new DeviceStateCallback();
+        mDeviceStateManager.registerCallback(new HandlerExecutor(mH), mDeviceStateCallback);
+
         if (mPowerManagerInternal != null) {
             mPowerManagerInternal.registerLowPowerModeObserver(
                     new PowerManagerInternal.LowPowerModeListener() {
@@ -2132,7 +2153,6 @@
         }
 
         final DisplayContent dc = win.getDisplayContent();
-        dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */);
 
         win.resetAppOpsState();
 
@@ -7594,6 +7614,26 @@
     }
 
     @Override
+    public boolean isEligibleForDesktopMode(int displayId) {
+        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "isEligibleForDesktopMode()")) {
+            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+        }
+
+        synchronized (mGlobalLock) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                ProtoLog.e(WM_ERROR, "Attempted to check isEligibleForDesktopMode() "
+                        + "for a display that does not exist: %d", displayId);
+                return false;
+            }
+            if (!displayContent.isSystemDecorationsSupported()) {
+                return false;
+            }
+            return displayContent.isDefaultDisplay || displayContent.allowContentModeSwitch();
+        }
+    }
+
+    @Override
     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
         if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) {
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
@@ -8950,6 +8990,17 @@
             }
         }
 
+        if (mDeviceStateCallback.isInRearDisplayOuterDefaultState()) {
+            final Display[] rearDisplays = mDisplayManager
+                    .getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
+            if (rearDisplays.length > 0 && rearDisplays[0].getDisplayId() == t.getDisplayId()) {
+                // Do not change display focus to the inner display if we're in this mode. Note that
+                // in this mode, the inner display is configured as a rear display.
+                Slog.w(TAG, "Ignoring focus change because device is in RDM.");
+                return;
+            }
+        }
+
         clearPointerDownOutsideFocusRunnable();
 
         final InputTarget focusedInputTarget = mFocusedInputTarget;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 6c3d516..c19fa8c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -473,35 +473,6 @@
         transition.setAllReady();
     }
 
-    // TODO(b/365884835): remove this method and callers.
-    @Override
-    public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
-            @NonNull IWindowContainerTransactionCallback callback,
-            @NonNull WindowContainerTransaction t) {
-        enforceTaskPermission("startLegacyTransition()");
-        final CallerInfo caller = new CallerInfo();
-        final long ident = Binder.clearCallingIdentity();
-        int syncId;
-        try {
-            synchronized (mGlobalLock) {
-                if (type < 0) {
-                    throw new IllegalArgumentException("Can't create transition with no type");
-                }
-                if (mTransitionController.getTransitionPlayer() != null) {
-                    throw new IllegalArgumentException("Can't use legacy transitions in"
-                            + " when shell transitions are enabled.");
-                }
-                syncId = startSyncWithOrganizer(callback);
-                applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
-                        caller);
-                setSyncReady(syncId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return syncId;
-    }
-
     @Override
     public void finishTransition(@NonNull IBinder transitionToken,
             @Nullable WindowContainerTransaction t) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4a93904..a270af5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -169,7 +169,6 @@
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
 import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
 import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
-import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.REMOVED;
 import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
 import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -231,7 +230,6 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.Surface;
-import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewDebug;
@@ -399,7 +397,6 @@
      * rotation.
      */
     final boolean mForceSeamlesslyRotate;
-    SeamlessRotator mPendingSeamlessRotate;
 
     private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
 
@@ -593,13 +590,6 @@
     /** The time when the window was last requested to redraw for orientation change. */
     private long mOrientationChangeRedrawRequestTime;
 
-    /**
-     * The orientation during the last visible call to relayout. If our
-     * current orientation is different, the window can't be ready
-     * to be shown.
-     */
-    int mLastVisibleLayoutRotation = -1;
-
     /** Is this window now (or just being) removed? */
     boolean mRemoved;
 
@@ -655,15 +645,6 @@
     boolean mIsSurfacePositionPaused;
 
     /**
-     * During seamless rotation we have two phases, first the old window contents
-     * are rotated to look as if they didn't move in the new coordinate system. Then we
-     * have to freeze updates to this layer (to preserve the transformation) until
-     * the resize actually occurs. This is true from when the transformation is set
-     * and false until the transaction to resize is sent.
-     */
-    boolean mSeamlesslyRotated = false;
-
-    /**
      * Whether the IME insets have been consumed. If {@code true}, this window won't be able to
      * receive visible IME insets; {@code false}, otherwise.
      */
@@ -778,11 +759,6 @@
      */
     private boolean mInsetsAnimationRunning;
 
-    private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
-        finishSeamlessRotation(t);
-        updateSurfacePosition(t);
-    };
-
     private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
         // Only apply the position to the surface when there's no leash created.
         if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
@@ -899,69 +875,6 @@
         return visible && mFrozenInsetsState == null;
     }
 
-    void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
-            @Rotation int rotation, boolean requested) {
-        // Invisible windows and the wallpaper do not participate in the seamless rotation animation
-        if (!isVisibleNow() || mIsWallpaper) {
-            return;
-        }
-
-        if (mToken.hasFixedRotationTransform()) {
-            // The transform of its surface is handled by fixed rotation.
-            return;
-        }
-        final Task task = getTask();
-        if (task != null && task.inPinnedWindowingMode()) {
-            // It is handled by PinnedTaskController. Note that the windowing mode of activity
-            // and windows may still be fullscreen.
-            return;
-        }
-
-        if (mPendingSeamlessRotate != null) {
-            oldRotation = mPendingSeamlessRotate.getOldRotation();
-        }
-
-        // Skip performing seamless rotation when the controlled insets is IME with visible state.
-        if (mControllableInsetProvider != null
-                && mControllableInsetProvider.getSource().getType() == WindowInsets.Type.ime()) {
-            return;
-        }
-
-        if (mForceSeamlesslyRotate || requested) {
-            if (mControllableInsetProvider != null) {
-                mControllableInsetProvider.startSeamlessRotation();
-            }
-            mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo(),
-                    false /* applyFixedTransformationHint */);
-            // The surface position is going to be unrotated according to the last position.
-            // Make sure the source position is up-to-date.
-            mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
-            mPendingSeamlessRotate.unrotate(transaction, this);
-            getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
-                    true /* seamlesslyRotated */);
-            applyWithNextDraw(mSeamlessRotationFinishedConsumer);
-        }
-    }
-
-    void cancelSeamlessRotation() {
-        finishSeamlessRotation(getPendingTransaction());
-    }
-
-    void finishSeamlessRotation(SurfaceControl.Transaction t) {
-        if (mPendingSeamlessRotate == null) {
-            return;
-        }
-
-        mPendingSeamlessRotate.finish(t, this);
-        mPendingSeamlessRotate = null;
-
-        getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
-            false /* seamlesslyRotated */);
-        if (mControllableInsetProvider != null) {
-            mControllableInsetProvider.finishSeamlessRotation();
-        }
-    }
-
     List<Rect> getSystemGestureExclusion() {
         return mExclusionRects;
     }
@@ -2176,8 +2089,7 @@
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                 && !isDragResizing()
                 && hasMovementAnimation
-                && !mWinAnimator.mLastHidden
-                && !mSeamlesslyRotated;
+                && !mWinAnimator.mLastHidden;
     }
 
     /**
@@ -3998,7 +3910,6 @@
         proto.write(REMOVED, mRemoved);
         proto.write(IS_ON_SCREEN, isOnScreen());
         proto.write(IS_VISIBLE, isVisible);
-        proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
         proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
         proto.write(HAS_COMPAT_SCALE, hasCompatScale());
         proto.write(GLOBAL_SCALE, mGlobalScale);
@@ -4144,14 +4055,6 @@
                     + " mDestroying=" + mDestroying
                     + " mRemoved=" + mRemoved);
         }
-        pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
-                + " seamlesslyRotate: pending=");
-        if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.dump(pw);
-        } else {
-            pw.print("null");
-        }
-        pw.println();
 
         if (mXOffset != 0 || mYOffset != 0) {
             pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
@@ -4883,8 +4786,6 @@
             mWinAnimator.mEnterAnimationPending = true;
         }
 
-        mLastVisibleLayoutRotation = getDisplayContent().getRotation();
-
         mWinAnimator.mEnteringAnimation = true;
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
@@ -5282,9 +5183,8 @@
 
         final AsyncRotationController asyncRotationController =
                 mDisplayContent.getAsyncRotationController();
-        if ((asyncRotationController != null
-                && asyncRotationController.hasSeamlessOperation(mToken))
-                || mPendingSeamlessRotate != null) {
+        if (asyncRotationController != null
+                && asyncRotationController.hasSeamlessOperation(mToken)) {
             // Freeze position while un-rotating the window, so its surface remains at the position
             // corresponding to the original rotation.
             return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a34b511..4fb74ef 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -42,9 +42,6 @@
     /** Only do a maximum of 6 repeated layouts. After that quit */
     private int mLayoutRepeatCount;
 
-    static final int SET_UPDATE_ROTATION                = 1 << 0;
-    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 1;
-
     private boolean mTraversalScheduled;
     private int mDeferDepth = 0;
     /** The number of layout requests when deferring. */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 66d04df..adfabe1 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -169,7 +169,7 @@
         "android.hardware.broadcastradio@1.1",
         "android.hardware.contexthub@1.0",
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.gnss-V3-cpp",
+        "android.hardware.gnss-V5-cpp",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
@@ -204,6 +204,7 @@
         "android.system.suspend.control-V1-cpp",
         "android.system.suspend.control.internal-cpp",
         "android.system.suspend-V1-ndk",
+        "android_location_flags_c_lib",
         "server_configurable_flags",
         "service.incremental",
     ],
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e32ce52..ec8794f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -671,9 +671,13 @@
         return;
     }
 
-    // TODO(b/383092013): Add topology validation
     const DisplayTopologyGraph displayTopology =
             android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+    if (input_flags::enable_display_topology_validation() && !displayTopology.isValid()) {
+        LOG(ERROR) << "Ignoring Invalid DisplayTopology";
+        return;
+    }
+
     mInputManager->getDispatcher().setDisplayTopology(displayTopology);
     mInputManager->getChoreographer().setDisplayTopology(displayTopology);
 }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9c033e2..93f6e95 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -36,6 +36,7 @@
 #include <android/hardware/gnss/BnGnssMeasurementCallback.h>
 #include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
 #include <android/hardware/gnss/BnGnssPsdsCallback.h>
+#include <android_location_flags.h>
 #include <binder/IServiceManager.h>
 #include <nativehelper/JNIHelp.h>
 #include <pthread.h>
@@ -53,6 +54,8 @@
 #include "gnss/Gnss.h"
 #include "gnss/GnssAntennaInfo.h"
 #include "gnss/GnssAntennaInfoCallback.h"
+#include "gnss/GnssAssistance.h"
+#include "gnss/GnssAssistanceCallback.h"
 #include "gnss/GnssBatching.h"
 #include "gnss/GnssConfiguration.h"
 #include "gnss/GnssDebug.h"
@@ -114,6 +117,7 @@
 using android::hardware::gnss::GnssPowerStats;
 using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPowerIndicationCallback;
+using android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
 
 using IGnssAidl = android::hardware::gnss::IGnss;
 using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
@@ -140,6 +144,9 @@
 std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr;
 std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface =
         nullptr;
+std::unique_ptr<android::gnss::GnssAssistanceInterface> gnssAssistanceIface = nullptr;
+
+namespace location_flags = android::location::flags;
 
 namespace android {
 
@@ -229,6 +236,9 @@
     gnss::GnssVisibilityControl_class_init_once(env, clazz);
     gnss::MeasurementCorrections_class_init_once(env, clazz);
     gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz);
+    if (location_flags::gnss_assistance_interface_jni()) {
+        gnss::GnssAssistance_class_init_once(env, clazz);
+    }
     gnss::Utils_class_init_once(env);
 }
 
@@ -266,7 +276,9 @@
     gnssBatchingIface = gnssHal->getGnssBatchingInterface();
     gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface();
     gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface();
-
+    if (location_flags::gnss_assistance_interface_jni()) {
+        gnssAssistanceIface = gnssHal->getGnssAssistanceInterface();
+    }
     if (mCallbacksObj) {
         ALOGE("Callbacks already initialized");
     } else {
@@ -355,13 +367,22 @@
     // Set IGnssPowerIndication.hal callback.
     if (gnssPowerIndicationIface != nullptr) {
         sp<IGnssPowerIndicationCallback> gnssPowerIndicationCallback =
-                new GnssPowerIndicationCallback();
+                sp<GnssPowerIndicationCallback>::make();
         auto status = gnssPowerIndicationIface->setCallback(gnssPowerIndicationCallback);
         if (!checkAidlStatus(status, "IGnssPowerIndication setCallback() failed.")) {
             gnssPowerIndicationIface = nullptr;
         }
     }
 
+    // Set IGnssAssistance callback.
+    if (gnssAssistanceIface != nullptr) {
+        sp<IGnssAssistanceCallback> gnssAssistanceCallback =
+                sp<gnss::GnssAssistanceCallback>::make();
+        if (!gnssAssistanceIface->setCallback(gnssAssistanceCallback)) {
+            ALOGI("IGnssAssistanceInterface setCallback() failed");
+        }
+    }
+
     return JNI_TRUE;
 }
 
@@ -493,6 +514,15 @@
     gnssPsdsIface->injectPsdsData(data, length, psdsType);
 }
 
+static void android_location_gnss_hal_GnssNative_inject_gnss_assistance(JNIEnv* env, jclass,
+                                                                        jobject gnssAssistanceObj) {
+    if (gnssAssistanceIface == nullptr) {
+        ALOGE("%s: IGnssAssistance interface not available.", __func__);
+        return;
+    }
+    gnssAssistanceIface->injectGnssAssistance(env, gnssAssistanceObj);
+}
+
 static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
         JNIEnv* env, jobject /* obj */, jlong networkHandle, jstring apn, jint apnIpType) {
     if (apn == nullptr) {
@@ -937,6 +967,8 @@
         {"native_stop_nmea_message_collection", "()Z",
          reinterpret_cast<void*>(
                  android_location_gnss_hal_GnssNative_stop_nmea_message_collection)},
+        {"native_inject_gnss_assistance", "(Landroid/location/GnssAssistance;)V",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_gnss_assistance)},
 };
 
 static const JNINativeMethod sBatchingMethods[] = {
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index eb729de..0bee718 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -60,6 +60,8 @@
     void unregisterRuntimeSensor(jint handle);
     jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
                                     jfloatArray values);
+    jboolean sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, jint serial,
+                                             jlong timestamp, jfloatArray values);
 
 private:
     sp<SensorService> mService;
@@ -172,9 +174,9 @@
 
     sensors_event_t event{
             .version = sizeof(sensors_event_t),
-            .timestamp = timestamp,
             .sensor = handle,
             .type = type,
+            .timestamp = timestamp,
     };
 
     int valuesLength = env->GetArrayLength(values);
@@ -234,6 +236,42 @@
     return err == OK;
 }
 
+jboolean NativeSensorService::sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type,
+                                                              jint serial, jlong timestamp,
+                                                              jfloatArray values) {
+    if (mService == nullptr) {
+        ALOGD("Dropping sendRuntimeSensorAdditionalInfo, sensor service not available.");
+        return false;
+    }
+
+    sensors_event_t event{
+            .version = sizeof(sensors_event_t),
+            .sensor = handle,
+            .type = SENSOR_TYPE_ADDITIONAL_INFO,
+            .timestamp = timestamp,
+            .additional_info =
+                    (additional_info_event_t){
+                            .type = type,
+                            .serial = serial,
+                    },
+    };
+
+    if (values != nullptr) {
+        int valuesLength = env->GetArrayLength(values);
+        if (valuesLength > 14) {
+            ALOGD("Dropping sendRuntimeSensorAdditionalInfo, number of values exceeds maximum.");
+            return false;
+        }
+        if (valuesLength > 0) {
+            jfloat* sensorValues = env->GetFloatArrayElements(values, nullptr);
+            memcpy(event.additional_info.data_float, sensorValues, valuesLength * sizeof(float));
+        }
+    }
+
+    status_t err = mService->sendRuntimeSensorEvent(event);
+    return err == OK;
+}
+
 NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate(
         JNIEnv* env, jobject listener)
       : mListener(env->NewGlobalRef(listener)) {}
@@ -326,6 +364,13 @@
     return service->sendRuntimeSensorEvent(env, handle, type, timestamp, values);
 }
 
+static jboolean sendRuntimeSensorAdditionalInfoNative(JNIEnv* env, jclass, jlong ptr, jint handle,
+                                                      jint type, jint serial, jlong timestamp,
+                                                      jfloatArray values) {
+    auto* service = reinterpret_cast<NativeSensorService*>(ptr);
+    return service->sendRuntimeSensorAdditionalInfo(env, handle, type, serial, timestamp, values);
+}
+
 static const JNINativeMethod methods[] = {
         {"startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J",
          reinterpret_cast<void*>(startSensorServiceNative)},
@@ -340,6 +385,8 @@
          reinterpret_cast<void*>(unregisterRuntimeSensorNative)},
         {"sendRuntimeSensorEventNative", "(JIIJ[F)Z",
          reinterpret_cast<void*>(sendRuntimeSensorEventNative)},
+        {"sendRuntimeSensorAdditionalInfoNative", "(JIIIJ[F)Z",
+         reinterpret_cast<void*>(sendRuntimeSensorAdditionalInfoNative)},
 };
 
 int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index e72259f..562e82f 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -17,7 +17,6 @@
         "-Werror",
         "-Wno-unused-parameter",
         "-Wthread-safety",
-
         "-DEGL_EGLEXT_PROTOTYPES",
         "-DGL_GLEXT_PROTOTYPES",
     ],
@@ -41,6 +40,8 @@
         "GnssMeasurementCallback.cpp",
         "GnssNavigationMessage.cpp",
         "GnssNavigationMessageCallback.cpp",
+        "GnssAssistance.cpp",
+        "GnssAssistanceCallback.cpp",
         "GnssPsds.cpp",
         "GnssPsdsCallback.cpp",
         "GnssVisibilityControl.cpp",
@@ -61,7 +62,7 @@
         "libnativehelper",
         "libhardware_legacy",
         "libutils",
-        "android.hardware.gnss-V3-cpp",
+        "android.hardware.gnss-V5-cpp",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp
index da8928b5..a3fd9aa 100644
--- a/services/core/jni/gnss/Gnss.cpp
+++ b/services/core/jni/gnss/Gnss.cpp
@@ -765,4 +765,15 @@
     return nullptr;
 }
 
+std::unique_ptr<GnssAssistanceInterface> GnssHal::getGnssAssistanceInterface() {
+    if (gnssHalAidl != nullptr) {
+        sp<hardware::gnss::gnss_assistance::IGnssAssistanceInterface> gnssAssistance;
+        auto status = gnssHalAidl->getExtensionGnssAssistanceInterface(&gnssAssistance);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssAssistance")) {
+            return std::make_unique<GnssAssistanceInterface>(gnssAssistance);
+        }
+    }
+    return nullptr;
+}
+
 } // namespace android::gnss
diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h
index 458da8a..2b6b751 100644
--- a/services/core/jni/gnss/Gnss.h
+++ b/services/core/jni/gnss/Gnss.h
@@ -34,6 +34,7 @@
 #include "AGnss.h"
 #include "AGnssRil.h"
 #include "GnssAntennaInfo.h"
+#include "GnssAssistance.h"
 #include "GnssBatching.h"
 #include "GnssCallback.h"
 #include "GnssConfiguration.h"
@@ -115,6 +116,7 @@
     std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface();
     std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface();
     std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface();
+    std::unique_ptr<GnssAssistanceInterface> getGnssAssistanceInterface();
 
     sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface();
     sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface();
diff --git a/services/core/jni/gnss/GnssAssistance.cpp b/services/core/jni/gnss/GnssAssistance.cpp
new file mode 100644
index 0000000..fff396e
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.cpp
@@ -0,0 +1,2047 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 before <log/log.h> to overwrite the default value.
+
+#define LOG_TAG "GnssAssistanceJni"
+
+#include "GnssAssistance.h"
+
+#include <utils/String16.h>
+
+#include "GnssAssistanceCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using GnssConstellationType = android::hardware::gnss::GnssConstellationType;
+using GnssCorrectionComponent = android::hardware::gnss::gnss_assistance::GnssCorrectionComponent;
+using GnssInterval =
+        android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::GnssInterval;
+using GnssSatelliteAlmanac =
+        android::hardware::gnss::gnss_assistance::GnssAlmanac::GnssSatelliteAlmanac;
+using IonosphericCorrection = android::hardware::gnss::gnss_assistance::IonosphericCorrection;
+using PseudorangeCorrection =
+        android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::PseudorangeCorrection;
+using GalileoSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+        GalileoSatelliteEphemeris::GalileoSatelliteClockModel;
+using GalileoSvHealth =
+        android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris::GalileoSvHealth;
+using GlonassSatelliteAlmanac =
+        android::hardware::gnss::gnss_assistance::GlonassAlmanac::GlonassSatelliteAlmanac;
+using GlonassSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+        GlonassSatelliteEphemeris::GlonassSatelliteClockModel;
+using GlonassSatelliteOrbitModel = android::hardware::gnss::gnss_assistance::
+        GlonassSatelliteEphemeris::GlonassSatelliteOrbitModel;
+using GnssSignalType = hardware::gnss::GnssSignalType;
+using GnssConstellationType = hardware::gnss::GnssConstellationType;
+using BeidouB1CSatelliteOrbitType =
+        android::hardware::gnss::gnss_assistance::AuxiliaryInformation::BeidouB1CSatelliteOrbitType;
+using QzssSatelliteEphemeris = android::hardware::gnss::gnss_assistance::QzssSatelliteEphemeris;
+
+// Implementation of GnssAssistance (AIDL HAL)
+
+namespace {
+jmethodID method_gnssAssistanceGetGpsAssistance;
+jmethodID method_gnssAssistanceGetGlonassAssistance;
+jmethodID method_gnssAssistanceGetGalileoAssistance;
+jmethodID method_gnssAssistanceGetBeidouAssistance;
+jmethodID method_gnssAssistanceGetQzssAssistance;
+
+jmethodID method_listSize;
+jmethodID method_listGet;
+
+jmethodID method_gnssAlmanacGetIssueDateMillis;
+jmethodID method_gnssAlmanacGetIoda;
+jmethodID method_gnssAlmanacGetWeekNumber;
+jmethodID method_gnssAlmanacGetToaSeconds;
+jmethodID method_gnssAlmanacGetSatelliteAlmanacs;
+jmethodID method_gnssAlmanacIsCompleteAlmanacProvided;
+jmethodID method_satelliteAlmanacGetSvid;
+jmethodID method_satelliteAlmanacGetSvHealth;
+jmethodID method_satelliteAlmanacGetAf0;
+jmethodID method_satelliteAlmanacGetAf1;
+jmethodID method_satelliteAlmanacGetEccentricity;
+jmethodID method_satelliteAlmanacGetInclination;
+jmethodID method_satelliteAlmanacGetM0;
+jmethodID method_satelliteAlmanacGetOmega;
+jmethodID method_satelliteAlmanacGetOmega0;
+jmethodID method_satelliteAlmanacGetOmegaDot;
+jmethodID method_satelliteAlmanacGetRootA;
+
+jmethodID method_satelliteEphemerisTimeGetIode;
+jmethodID method_satelliteEphemerisTimeGetToeSeconds;
+jmethodID method_satelliteEphemerisTimeGetWeekNumber;
+
+jmethodID method_keplerianOrbitModelGetDeltaN;
+jmethodID method_keplerianOrbitModelGetEccentricity;
+jmethodID method_keplerianOrbitModelGetI0;
+jmethodID method_keplerianOrbitModelGetIDot;
+jmethodID method_keplerianOrbitModelGetM0;
+jmethodID method_keplerianOrbitModelGetOmega;
+jmethodID method_keplerianOrbitModelGetOmega0;
+jmethodID method_keplerianOrbitModelGetOmegaDot;
+jmethodID method_keplerianOrbitModelGetRootA;
+jmethodID method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation;
+jmethodID method_secondOrderHarmonicPerturbationGetCic;
+jmethodID method_secondOrderHarmonicPerturbationGetCis;
+jmethodID method_secondOrderHarmonicPerturbationGetCrc;
+jmethodID method_secondOrderHarmonicPerturbationGetCrs;
+jmethodID method_secondOrderHarmonicPerturbationGetCuc;
+jmethodID method_secondOrderHarmonicPerturbationGetCus;
+
+jmethodID method_klobucharIonosphericModelGetAlpha0;
+jmethodID method_klobucharIonosphericModelGetAlpha1;
+jmethodID method_klobucharIonosphericModelGetAlpha2;
+jmethodID method_klobucharIonosphericModelGetAlpha3;
+jmethodID method_klobucharIonosphericModelGetBeta0;
+jmethodID method_klobucharIonosphericModelGetBeta1;
+jmethodID method_klobucharIonosphericModelGetBeta2;
+jmethodID method_klobucharIonosphericModelGetBeta3;
+
+jmethodID method_utcModelGetA0;
+jmethodID method_utcModelGetA1;
+jmethodID method_utcModelGetTimeOfWeek;
+jmethodID method_utcModelGetWeekNumber;
+
+jmethodID method_leapSecondsModelGetDayNumberLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetLeapSeconds;
+jmethodID method_leapSecondsModelGetLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetWeekNumberLeapSecondsFuture;
+
+jmethodID method_timeModelsGetTimeOfWeek;
+jmethodID method_timeModelsGetToGnss;
+jmethodID method_timeModelsGetWeekNumber;
+jmethodID method_timeModelsGetA0;
+jmethodID method_timeModelsGetA1;
+
+jmethodID method_realTimeIntegrityModelGetBadSvid;
+jmethodID method_realTimeIntegrityModelGetBadSignalTypes;
+jmethodID method_realTimeIntegrityModelGetStartDateSeconds;
+jmethodID method_realTimeIntegrityModelGetEndDateSeconds;
+jmethodID method_realTimeIntegrityModelGetPublishDateSeconds;
+jmethodID method_realTimeIntegrityModelGetAdvisoryNumber;
+jmethodID method_realTimeIntegrityModelGetAdvisoryType;
+
+jmethodID method_gnssSignalTypeGetConstellationType;
+jmethodID method_gnssSignalTypeGetCarrierFrequencyHz;
+jmethodID method_gnssSignalTypeGetCodeType;
+
+jmethodID method_auxiliaryInformationGetSvid;
+jmethodID method_auxiliaryInformationGetAvailableSignalTypes;
+jmethodID method_auxiliaryInformationGetFrequencyChannelNumber;
+jmethodID method_auxiliaryInformationGetSatType;
+
+jmethodID method_satelliteCorrectionGetSvid;
+jmethodID method_satelliteCorrectionGetIonosphericCorrections;
+jmethodID method_ionosphericCorrectionGetCarrierFrequencyHz;
+jmethodID method_ionosphericCorrectionGetIonosphericCorrection;
+jmethodID method_gnssCorrectionComponentGetPseudorangeCorrection;
+jmethodID method_gnssCorrectionComponentGetSourceKey;
+jmethodID method_gnssCorrectionComponentGetValidityInterval;
+jmethodID method_pseudorangeCorrectionGetCorrectionMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionUncertaintyMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond;
+jmethodID method_gnssIntervalGetStartMillisSinceGpsEpoch;
+jmethodID method_gnssIntervalGetEndMillisSinceGpsEpoch;
+
+jmethodID method_gpsAssistanceGetAlmanac;
+jmethodID method_gpsAssistanceGetIonosphericModel;
+jmethodID method_gpsAssistanceGetUtcModel;
+jmethodID method_gpsAssistanceGetLeapSecondsModel;
+jmethodID method_gpsAssistanceGetTimeModels;
+jmethodID method_gpsAssistanceGetSatelliteEphemeris;
+jmethodID method_gpsAssistanceGetRealTimeIntegrityModels;
+jmethodID method_gpsAssistanceGetSatelliteCorrections;
+jmethodID method_gpsSatelliteEphemerisGetSvid;
+jmethodID method_gpsSatelliteEphemerisGetGpsL2Params;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_gpsL2ParamsGetL2Code;
+jmethodID method_gpsL2ParamsGetL2Flag;
+jmethodID method_gpsSatelliteClockModelGetAf0;
+jmethodID method_gpsSatelliteClockModelGetAf1;
+jmethodID method_gpsSatelliteClockModelGetAf2;
+jmethodID method_gpsSatelliteClockModelGetTgd;
+jmethodID method_gpsSatelliteClockModelGetIodc;
+jmethodID method_gpsSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_gpsSatelliteHealthGetFitInt;
+jmethodID method_gpsSatelliteHealthGetSvAccur;
+jmethodID method_gpsSatelliteHealthGetSvHealth;
+
+jmethodID method_beidouAssistanceGetAlmanac;
+jmethodID method_beidouAssistanceGetIonosphericModel;
+jmethodID method_beidouAssistanceGetUtcModel;
+jmethodID method_beidouAssistanceGetLeapSecondsModel;
+jmethodID method_beidouAssistanceGetTimeModels;
+jmethodID method_beidouAssistanceGetSatelliteEphemeris;
+jmethodID method_beidouAssistanceGetSatelliteCorrections;
+jmethodID method_beidouAssistanceGetRealTimeIntegrityModels;
+jmethodID method_beidouSatelliteEphemerisGetSvid;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_beidouSatelliteClockModelGetAf0;
+jmethodID method_beidouSatelliteClockModelGetAf1;
+jmethodID method_beidouSatelliteClockModelGetAf2;
+jmethodID method_beidouSatelliteClockModelGetAodc;
+jmethodID method_beidouSatelliteClockModelGetTgd1;
+jmethodID method_beidouSatelliteClockModelGetTgd2;
+jmethodID method_beidouSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_beidouSatelliteHealthGetSatH1;
+jmethodID method_beidouSatelliteHealthGetSvAccur;
+jmethodID method_beidouSatelliteEphemerisTimeGetIode;
+jmethodID method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber;
+jmethodID method_beidouSatelliteEphemerisTimeGetToeSeconds;
+
+jmethodID method_galileoAssistanceGetAlmanac;
+jmethodID method_galileoAssistanceGetIonosphericModel;
+jmethodID method_galileoAssistanceGetUtcModel;
+jmethodID method_galileoAssistanceGetLeapSecondsModel;
+jmethodID method_galileoAssistanceGetTimeModels;
+jmethodID method_galileoAssistanceGetSatelliteEphemeris;
+jmethodID method_galileoAssistanceGetSatelliteCorrections;
+jmethodID method_galileoAssistanceGetRealTimeIntegrityModels;
+jmethodID method_galileoSatelliteEphemerisGetSvid;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteClockModels;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_galileoSatelliteClockModelGetAf0;
+jmethodID method_galileoSatelliteClockModelGetAf1;
+jmethodID method_galileoSatelliteClockModelGetAf2;
+jmethodID method_galileoSatelliteClockModelGetBgdSeconds;
+jmethodID method_galileoSatelliteClockModelGetSatelliteClockType;
+jmethodID method_galileoSatelliteClockModelGetSisaMeters;
+jmethodID method_galileoSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_galileoSvHealthGetDataValidityStatusE1b;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5a;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE1b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5a;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5b;
+jmethodID method_galileoIonosphericModelGetAi0;
+jmethodID method_galileoIonosphericModelGetAi1;
+jmethodID method_galileoIonosphericModelGetAi2;
+
+jmethodID method_glonassAssistanceGetAlmanac;
+jmethodID method_glonassAssistanceGetUtcModel;
+jmethodID method_glonassAssistanceGetTimeModels;
+jmethodID method_glonassAssistanceGetSatelliteEphemeris;
+jmethodID method_glonassAssistanceGetSatelliteCorrections;
+jmethodID method_glonassAlmanacGetIssueDateMillis;
+jmethodID method_glonassAlmanacGetSatelliteAlmanacs;
+jmethodID method_glonassSatelliteAlmanacGetDeltaI;
+jmethodID method_glonassSatelliteAlmanacGetDeltaT;
+jmethodID method_glonassSatelliteAlmanacGetDeltaTDot;
+jmethodID method_glonassSatelliteAlmanacGetEccentricity;
+jmethodID method_glonassSatelliteAlmanacGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteAlmanacGetLambda;
+jmethodID method_glonassSatelliteAlmanacGetOmega;
+jmethodID method_glonassSatelliteAlmanacGetSlotNumber;
+jmethodID method_glonassSatelliteAlmanacGetHealthState;
+jmethodID method_glonassSatelliteAlmanacGetTLambda;
+jmethodID method_glonassSatelliteAlmanacGetTau;
+jmethodID method_glonassSatelliteAlmanacGetIsGlonassM;
+jmethodID method_glonassSatelliteAlmanacGetCalendarDayNumber;
+jmethodID method_glonassSatelliteEphemerisGetAgeInDays;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_glonassSatelliteEphemerisGetHealthState;
+jmethodID method_glonassSatelliteEphemerisGetSlotNumber;
+jmethodID method_glonassSatelliteEphemerisGetFrameTimeSeconds;
+jmethodID method_glonassSatelliteEphemerisGetUpdateIntervalMinutes;
+jmethodID method_glonassSatelliteEphemerisGetIsGlonassM;
+jmethodID method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd;
+
+jmethodID method_glonassSatelliteOrbitModelGetX;
+jmethodID method_glonassSatelliteOrbitModelGetY;
+jmethodID method_glonassSatelliteOrbitModelGetZ;
+jmethodID method_glonassSatelliteOrbitModelGetXAccel;
+jmethodID method_glonassSatelliteOrbitModelGetYAccel;
+jmethodID method_glonassSatelliteOrbitModelGetZAccel;
+jmethodID method_glonassSatelliteOrbitModelGetXDot;
+jmethodID method_glonassSatelliteOrbitModelGetYDot;
+jmethodID method_glonassSatelliteOrbitModelGetZDot;
+jmethodID method_glonassSatelliteClockModelGetClockBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteClockModelGetTimeOfClockSeconds;
+
+jmethodID method_qzssAssistanceGetAlmanac;
+jmethodID method_qzssAssistanceGetIonosphericModel;
+jmethodID method_qzssAssistanceGetUtcModel;
+jmethodID method_qzssAssistanceGetLeapSecondsModel;
+jmethodID method_qzssAssistanceGetTimeModels;
+jmethodID method_qzssAssistanceGetSatelliteEphemeris;
+jmethodID method_qzssAssistanceGetSatelliteCorrections;
+jmethodID method_qzssAssistanceGetRealTimeIntegrityModels;
+jmethodID method_qzssSatelliteEphemerisGetSvid;
+jmethodID method_qzssSatelliteEphemerisGetGpsL2Params;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_qzssSatelliteClockModelGetAf0;
+jmethodID method_qzssSatelliteClockModelGetAf1;
+jmethodID method_qzssSatelliteClockModelGetAf2;
+jmethodID method_qzssSatelliteClockModelGetAodc;
+jmethodID method_qzssSatelliteClockModelGetTgd1;
+jmethodID method_qzssSatelliteClockModelGetTgd2;
+jmethodID method_qzssSatelliteClockModelGetTimeOfClockSeconds;
+} // namespace
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz) {
+    // Get the methods of GnssAssistance class.
+    jclass gnssAssistanceClass = env->FindClass("android/location/GnssAssistance");
+
+    method_gnssAssistanceGetGpsAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getGpsAssistance",
+                             "()Landroid/location/GpsAssistance;");
+    method_gnssAssistanceGetGlonassAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getGlonassAssistance",
+                             "()Landroid/location/GlonassAssistance;");
+    method_gnssAssistanceGetGalileoAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getGalileoAssistance",
+                             "()Landroid/location/GalileoAssistance;");
+    method_gnssAssistanceGetBeidouAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getBeidouAssistance",
+                             "()Landroid/location/BeidouAssistance;");
+    method_gnssAssistanceGetQzssAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getQzssAssistance",
+                             "()Landroid/location/QzssAssistance;");
+
+    // Get the methods of List class.
+    jclass listClass = env->FindClass("java/util/List");
+
+    method_listSize = env->GetMethodID(listClass, "size", "()I");
+    method_listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+
+    // Get the methods of GnssAlmanac class.
+    jclass gnssAlmanacClass = env->FindClass("android/location/GnssAlmanac");
+
+    method_gnssAlmanacGetIssueDateMillis =
+            env->GetMethodID(gnssAlmanacClass, "getIssueDateMillis", "()J");
+    method_gnssAlmanacGetIoda = env->GetMethodID(gnssAlmanacClass, "getIoda", "()I");
+    method_gnssAlmanacGetWeekNumber = env->GetMethodID(gnssAlmanacClass, "getWeekNumber", "()I");
+    method_gnssAlmanacGetToaSeconds = env->GetMethodID(gnssAlmanacClass, "getToaSeconds", "()I");
+    method_gnssAlmanacGetSatelliteAlmanacs =
+            env->GetMethodID(gnssAlmanacClass, "getGnssSatelliteAlmanacs", "()Ljava/util/List;");
+    method_gnssAlmanacIsCompleteAlmanacProvided =
+            env->GetMethodID(gnssAlmanacClass, "isCompleteAlmanacProvided", "()Z");
+
+    // Get the methods of SatelliteAlmanac class.
+    jclass satelliteAlmanacClass =
+            env->FindClass("android/location/GnssAlmanac$GnssSatelliteAlmanac");
+
+    method_satelliteAlmanacGetSvid = env->GetMethodID(satelliteAlmanacClass, "getSvid", "()I");
+    method_satelliteAlmanacGetSvHealth =
+            env->GetMethodID(satelliteAlmanacClass, "getSvHealth", "()I");
+    method_satelliteAlmanacGetAf0 = env->GetMethodID(satelliteAlmanacClass, "getAf0", "()D");
+    method_satelliteAlmanacGetAf1 = env->GetMethodID(satelliteAlmanacClass, "getAf1", "()D");
+    method_satelliteAlmanacGetEccentricity =
+            env->GetMethodID(satelliteAlmanacClass, "getEccentricity", "()D");
+    method_satelliteAlmanacGetInclination =
+            env->GetMethodID(satelliteAlmanacClass, "getInclination", "()D");
+    method_satelliteAlmanacGetM0 = env->GetMethodID(satelliteAlmanacClass, "getM0", "()D");
+    method_satelliteAlmanacGetOmega = env->GetMethodID(satelliteAlmanacClass, "getOmega", "()D");
+    method_satelliteAlmanacGetOmega0 = env->GetMethodID(satelliteAlmanacClass, "getOmega0", "()D");
+    method_satelliteAlmanacGetOmegaDot =
+            env->GetMethodID(satelliteAlmanacClass, "getOmegaDot", "()D");
+    method_satelliteAlmanacGetRootA = env->GetMethodID(satelliteAlmanacClass, "getRootA", "()D");
+
+    // Get the mothods of SatelliteEphemerisTime class.
+    jclass satelliteEphemerisTimeClass = env->FindClass("android/location/SatelliteEphemerisTime");
+
+    method_satelliteEphemerisTimeGetIode =
+            env->GetMethodID(satelliteEphemerisTimeClass, "getIode", "()I");
+    method_satelliteEphemerisTimeGetToeSeconds =
+            env->GetMethodID(satelliteEphemerisTimeClass, "getToeSeconds", "()I");
+    method_satelliteEphemerisTimeGetWeekNumber =
+            env->GetMethodID(satelliteEphemerisTimeClass, "getWeekNumber", "()I");
+
+    // Get the mothods of KeplerianOrbitModel class.
+    jclass keplerianOrbitModelClass = env->FindClass("android/location/KeplerianOrbitModel");
+
+    method_keplerianOrbitModelGetDeltaN =
+            env->GetMethodID(keplerianOrbitModelClass, "getDeltaN", "()D");
+    method_keplerianOrbitModelGetEccentricity =
+            env->GetMethodID(keplerianOrbitModelClass, "getEccentricity", "()D");
+    method_keplerianOrbitModelGetI0 = env->GetMethodID(keplerianOrbitModelClass, "getI0", "()D");
+    method_keplerianOrbitModelGetIDot =
+            env->GetMethodID(keplerianOrbitModelClass, "getIDot", "()D");
+    method_keplerianOrbitModelGetM0 = env->GetMethodID(keplerianOrbitModelClass, "getM0", "()D");
+    method_keplerianOrbitModelGetOmega =
+            env->GetMethodID(keplerianOrbitModelClass, "getOmega", "()D");
+    method_keplerianOrbitModelGetOmega0 =
+            env->GetMethodID(keplerianOrbitModelClass, "getOmega0", "()D");
+    method_keplerianOrbitModelGetOmegaDot =
+            env->GetMethodID(keplerianOrbitModelClass, "getOmegaDot", "()D");
+    method_keplerianOrbitModelGetRootA =
+            env->GetMethodID(keplerianOrbitModelClass, "getRootA", "()D");
+    method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation =
+            env->GetMethodID(keplerianOrbitModelClass, "getSecondOrderHarmonicPerturbation",
+                             "()Landroid/location/"
+                             "KeplerianOrbitModel$SecondOrderHarmonicPerturbation;");
+
+    // Get the methods of SecondOrderHarmonicPerturbation class.
+    jclass secondOrderHarmonicPerturbationClass =
+            env->FindClass("android/location/KeplerianOrbitModel$SecondOrderHarmonicPerturbation");
+
+    method_secondOrderHarmonicPerturbationGetCic =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCic", "()D");
+    method_secondOrderHarmonicPerturbationGetCis =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCis", "()D");
+    method_secondOrderHarmonicPerturbationGetCrc =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrc", "()D");
+    method_secondOrderHarmonicPerturbationGetCrs =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrs", "()D");
+    method_secondOrderHarmonicPerturbationGetCuc =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCuc", "()D");
+    method_secondOrderHarmonicPerturbationGetCus =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCus", "()D");
+
+    // Get the methods of KlobucharIonosphericModel class.
+    jclass klobucharIonosphericModelClass =
+            env->FindClass("android/location/KlobucharIonosphericModel");
+
+    method_klobucharIonosphericModelGetAlpha0 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha0", "()D");
+    method_klobucharIonosphericModelGetAlpha1 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha1", "()D");
+    method_klobucharIonosphericModelGetAlpha2 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha2", "()D");
+    method_klobucharIonosphericModelGetAlpha3 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha3", "()D");
+    method_klobucharIonosphericModelGetBeta0 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta0", "()D");
+    method_klobucharIonosphericModelGetBeta1 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta1", "()D");
+    method_klobucharIonosphericModelGetBeta2 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta2", "()D");
+    method_klobucharIonosphericModelGetBeta3 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta3", "()D");
+
+    // Get the methods of UtcModel class.
+    jclass utcModelClass = env->FindClass("android/location/UtcModel");
+
+    method_utcModelGetA0 = env->GetMethodID(utcModelClass, "getA0", "()D");
+    method_utcModelGetA1 = env->GetMethodID(utcModelClass, "getA1", "()D");
+    method_utcModelGetTimeOfWeek = env->GetMethodID(utcModelClass, "getTimeOfWeek", "()I");
+    method_utcModelGetWeekNumber = env->GetMethodID(utcModelClass, "getWeekNumber", "()I");
+
+    // Get the methods of LeapSecondsModel class.
+    jclass leapSecondsModelClass = env->FindClass("android/location/LeapSecondsModel");
+
+    method_leapSecondsModelGetDayNumberLeapSecondsFuture =
+            env->GetMethodID(leapSecondsModelClass, "getDayNumberLeapSecondsFuture", "()I");
+    method_leapSecondsModelGetLeapSeconds =
+            env->GetMethodID(leapSecondsModelClass, "getLeapSeconds", "()I");
+    method_leapSecondsModelGetLeapSecondsFuture =
+            env->GetMethodID(leapSecondsModelClass, "getLeapSecondsFuture", "()I");
+    method_leapSecondsModelGetWeekNumberLeapSecondsFuture =
+            env->GetMethodID(leapSecondsModelClass, "getWeekNumberLeapSecondsFuture", "()I");
+
+    // Get the methods of TimeModel class.
+    jclass timeModelsClass = env->FindClass("android/location/TimeModel");
+
+    method_timeModelsGetTimeOfWeek = env->GetMethodID(timeModelsClass, "getTimeOfWeek", "()I");
+    method_timeModelsGetToGnss = env->GetMethodID(timeModelsClass, "getToGnss", "()I");
+    method_timeModelsGetWeekNumber = env->GetMethodID(timeModelsClass, "getWeekNumber", "()I");
+    method_timeModelsGetA0 = env->GetMethodID(timeModelsClass, "getA0", "()D");
+    method_timeModelsGetA1 = env->GetMethodID(timeModelsClass, "getA1", "()D");
+
+    // Get the methods of AuxiliaryInformation class.
+    jclass auxiliaryInformationClass = env->FindClass("android/location/AuxiliaryInformation");
+
+    method_auxiliaryInformationGetSvid =
+            env->GetMethodID(auxiliaryInformationClass, "getSvid", "()I");
+    method_auxiliaryInformationGetAvailableSignalTypes =
+            env->GetMethodID(auxiliaryInformationClass, "getAvailableSignalTypes",
+                             "()Ljava/util/List;");
+    method_auxiliaryInformationGetFrequencyChannelNumber =
+            env->GetMethodID(auxiliaryInformationClass, "getFrequencyChannelNumber", "()I");
+    method_auxiliaryInformationGetSatType =
+            env->GetMethodID(auxiliaryInformationClass, "getSatType", "()I");
+
+    // Get the methods of RealTimeIntegrityModel
+    jclass realTimeIntegrityModelClass = env->FindClass("android/location/RealTimeIntegrityModel");
+
+    method_realTimeIntegrityModelGetBadSvid =
+            env->GetMethodID(realTimeIntegrityModelClass, "getBadSvid", "()I");
+    method_realTimeIntegrityModelGetBadSignalTypes =
+            env->GetMethodID(realTimeIntegrityModelClass, "getBadSignalTypes",
+                             "()Ljava/util/List;");
+    method_realTimeIntegrityModelGetStartDateSeconds =
+            env->GetMethodID(realTimeIntegrityModelClass, "getStartDateSeconds", "()J");
+    method_realTimeIntegrityModelGetEndDateSeconds =
+            env->GetMethodID(realTimeIntegrityModelClass, "getEndDateSeconds", "()J");
+    method_realTimeIntegrityModelGetPublishDateSeconds =
+            env->GetMethodID(realTimeIntegrityModelClass, "getPublishDateSeconds", "()J");
+    method_realTimeIntegrityModelGetAdvisoryNumber =
+            env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryNumber",
+                             "()Ljava/lang/String;");
+    method_realTimeIntegrityModelGetAdvisoryType =
+            env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryType",
+                             "()Ljava/lang/String;");
+
+    // Get the methods of GnssSignalType class.
+    jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType");
+
+    method_gnssSignalTypeGetConstellationType =
+            env->GetMethodID(gnssSignalTypeClass, "getConstellationType", "()I");
+    method_gnssSignalTypeGetCarrierFrequencyHz =
+            env->GetMethodID(gnssSignalTypeClass, "getCarrierFrequencyHz", "()D");
+    method_gnssSignalTypeGetCodeType =
+            env->GetMethodID(gnssSignalTypeClass, "getCodeType", "()Ljava/lang/String;");
+
+    // Get the methods of SatelliteCorrection class.
+    jclass satelliteCorrectionClass =
+            env->FindClass("android/location/GnssAssistance$GnssSatelliteCorrections");
+
+    method_satelliteCorrectionGetSvid =
+            env->GetMethodID(satelliteCorrectionClass, "getSvid", "()I");
+    method_satelliteCorrectionGetIonosphericCorrections =
+            env->GetMethodID(satelliteCorrectionClass, "getIonosphericCorrections",
+                             "()Ljava/util/List;");
+
+    // Get the methods of IonosphericCorrection class.
+    jclass ionosphericCorrectionClass = env->FindClass("android/location/IonosphericCorrection");
+
+    method_ionosphericCorrectionGetCarrierFrequencyHz =
+            env->GetMethodID(ionosphericCorrectionClass, "getCarrierFrequencyHz", "()J");
+    method_ionosphericCorrectionGetIonosphericCorrection =
+            env->GetMethodID(ionosphericCorrectionClass, "getIonosphericCorrection",
+                             "()Landroid/location/GnssCorrectionComponent;");
+
+    // Get the methods of GnssCorrectionComponent class.
+    jclass gnssCorrectionComponentClass =
+            env->FindClass("android/location/GnssCorrectionComponent");
+
+    method_gnssCorrectionComponentGetPseudorangeCorrection =
+            env->GetMethodID(gnssCorrectionComponentClass, "getPseudorangeCorrection",
+                             "()Landroid/location/GnssCorrectionComponent$PseudorangeCorrection;");
+    method_gnssCorrectionComponentGetSourceKey =
+            env->GetMethodID(gnssCorrectionComponentClass, "getSourceKey", "()Ljava/lang/String;");
+    method_gnssCorrectionComponentGetValidityInterval =
+            env->GetMethodID(gnssCorrectionComponentClass, "getValidityInterval",
+                             "()Landroid/location/GnssCorrectionComponent$GnssInterval;");
+
+    // Get the methods of PseudorangeCorrection class.
+    jclass pseudorangeCorrectionClass =
+            env->FindClass("android/location/GnssCorrectionComponent$PseudorangeCorrection");
+
+    method_pseudorangeCorrectionGetCorrectionMeters =
+            env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionMeters", "()D");
+    method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond =
+            env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionRateMetersPerSecond", "()D");
+    method_pseudorangeCorrectionGetCorrectionUncertaintyMeters =
+            env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionUncertaintyMeters", "()D");
+
+    // Get the methods of GnssInterval class.
+    jclass gnssIntervalClass =
+            env->FindClass("android/location/GnssCorrectionComponent$GnssInterval");
+
+    method_gnssIntervalGetStartMillisSinceGpsEpoch =
+            env->GetMethodID(gnssIntervalClass, "getStartMillisSinceGpsEpoch", "()J");
+    method_gnssIntervalGetEndMillisSinceGpsEpoch =
+            env->GetMethodID(gnssIntervalClass, "getEndMillisSinceGpsEpoch", "()J");
+
+    // Get the methods of GpsAssistance class.
+    jclass gpsAssistanceClass = env->FindClass("android/location/GpsAssistance");
+
+    method_gpsAssistanceGetAlmanac =
+            env->GetMethodID(gpsAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+    method_gpsAssistanceGetIonosphericModel =
+            env->GetMethodID(gpsAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_gpsAssistanceGetUtcModel =
+            env->GetMethodID(gpsAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+    method_gpsAssistanceGetLeapSecondsModel =
+            env->GetMethodID(gpsAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_gpsAssistanceGetTimeModels =
+            env->GetMethodID(gpsAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_gpsAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(gpsAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_gpsAssistanceGetRealTimeIntegrityModels =
+            env->GetMethodID(gpsAssistanceClass, "getRealTimeIntegrityModels",
+                             "()Ljava/util/List;");
+    method_gpsAssistanceGetSatelliteCorrections =
+            env->GetMethodID(gpsAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+    // Get the methods of GpsSatelliteEphemeris class.
+    jclass gpsSatelliteEphemerisClass = env->FindClass("android/location/GpsSatelliteEphemeris");
+
+    method_gpsSatelliteEphemerisGetSvid =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSvid", "()I");
+    method_gpsSatelliteEphemerisGetGpsL2Params =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getGpsL2Params",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+    method_gpsSatelliteEphemerisGetSatelliteClockModel =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteClockModel",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteClockModel;");
+    method_gpsSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+    method_gpsSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+    method_gpsSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/SatelliteEphemerisTime;");
+
+    // Get the methods of GpsL2Params class.
+    jclass gpsL2ParamsClass = env->FindClass("android/location/GpsSatelliteEphemeris$GpsL2Params");
+    method_gpsL2ParamsGetL2Code = env->GetMethodID(gpsL2ParamsClass, "getL2Code", "()I");
+    method_gpsL2ParamsGetL2Flag = env->GetMethodID(gpsL2ParamsClass, "getL2Flag", "()I");
+
+    // Get the methods of GpsSatelliteClockModel class.
+    jclass gpsSatelliteClockModelClass =
+            env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteClockModel");
+    method_gpsSatelliteClockModelGetAf0 =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getAf0", "()D");
+    method_gpsSatelliteClockModelGetAf1 =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getAf1", "()D");
+    method_gpsSatelliteClockModelGetAf2 =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getAf2", "()D");
+    method_gpsSatelliteClockModelGetTgd =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getTgd", "()D");
+    method_gpsSatelliteClockModelGetIodc =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getIodc", "()I");
+    method_gpsSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of GpsSatelliteHealth class.
+    jclass gpsSatelliteHealthClass =
+            env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteHealth");
+    method_gpsSatelliteHealthGetFitInt =
+            env->GetMethodID(gpsSatelliteHealthClass, "getFitInt", "()D");
+    method_gpsSatelliteHealthGetSvAccur =
+            env->GetMethodID(gpsSatelliteHealthClass, "getSvAccur", "()D");
+    method_gpsSatelliteHealthGetSvHealth =
+            env->GetMethodID(gpsSatelliteHealthClass, "getSvHealth", "()I");
+
+    // Get the methods of BeidouAssistance class.
+    jclass beidouAssistanceClass = env->FindClass("android/location/BeidouAssistance");
+    method_beidouAssistanceGetAlmanac = env->GetMethodID(beidouAssistanceClass, "getAlmanac",
+                                                         "()Landroid/location/GnssAlmanac;");
+    method_beidouAssistanceGetIonosphericModel =
+            env->GetMethodID(beidouAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_beidouAssistanceGetUtcModel =
+            env->GetMethodID(beidouAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+    method_beidouAssistanceGetLeapSecondsModel =
+            env->GetMethodID(beidouAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_beidouAssistanceGetTimeModels =
+            env->GetMethodID(beidouAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_beidouAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(beidouAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_beidouAssistanceGetSatelliteCorrections =
+            env->GetMethodID(beidouAssistanceClass, "getSatelliteCorrections",
+                             "()Ljava/util/List;");
+    method_beidouAssistanceGetRealTimeIntegrityModels =
+            env->GetMethodID(beidouAssistanceClass, "getRealTimeIntegrityModels",
+                             "()Ljava/util/List;");
+
+    // Get the methods of BeidouSatelliteEphemeris class.
+    jclass beidouSatelliteEphemerisClass =
+            env->FindClass("android/location/BeidouSatelliteEphemeris");
+    method_beidouSatelliteEphemerisGetSvid =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSvid", "()I");
+    method_beidouSatelliteEphemerisGetSatelliteClockModel =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteClockModel",
+                             "()Landroid/location/"
+                             "BeidouSatelliteEphemeris$BeidouSatelliteClockModel;");
+    method_beidouSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+    method_beidouSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth;");
+    method_beidouSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/"
+                             "BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime;");
+
+    // Get the methods of BeidouSatelliteClockModel
+    jclass beidouSatelliteClockModelClass =
+            env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteClockModel");
+    method_beidouSatelliteClockModelGetAf0 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAf0", "()D");
+    method_beidouSatelliteClockModelGetAf1 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAf1", "()D");
+    method_beidouSatelliteClockModelGetAf2 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAf2", "()D");
+    method_beidouSatelliteClockModelGetAodc =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAodc", "()I");
+    method_beidouSatelliteClockModelGetTgd1 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getTgd1", "()D");
+    method_beidouSatelliteClockModelGetTgd2 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getTgd2", "()D");
+    method_beidouSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of BeidouSatelliteHealth
+    jclass beidouSatelliteHealthClass =
+            env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth");
+    method_beidouSatelliteHealthGetSatH1 =
+            env->GetMethodID(beidouSatelliteHealthClass, "getSatH1", "()I");
+    method_beidouSatelliteHealthGetSvAccur =
+            env->GetMethodID(beidouSatelliteHealthClass, "getSvAccur", "()D");
+
+    // Get the methods of BeidouSatelliteEphemerisTime
+    jclass beidouSatelliteEphemerisTimeClass = env->FindClass(
+            "android/location/BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime");
+    method_beidouSatelliteEphemerisTimeGetIode =
+            env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getIode", "()I");
+    method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber =
+            env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getBeidouWeekNumber", "()I");
+    method_beidouSatelliteEphemerisTimeGetToeSeconds =
+            env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getToeSeconds", "()I");
+
+    // Get the methods of GalileoAssistance class.
+    jclass galileoAssistanceClass = env->FindClass("android/location/GalileoAssistance");
+    method_galileoAssistanceGetAlmanac = env->GetMethodID(galileoAssistanceClass, "getAlmanac",
+                                                          "()Landroid/location/GnssAlmanac;");
+    method_galileoAssistanceGetIonosphericModel =
+            env->GetMethodID(galileoAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_galileoAssistanceGetUtcModel = env->GetMethodID(galileoAssistanceClass, "getUtcModel",
+                                                           "()Landroid/location/UtcModel;");
+    method_galileoAssistanceGetLeapSecondsModel =
+            env->GetMethodID(galileoAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_galileoAssistanceGetTimeModels =
+            env->GetMethodID(galileoAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_galileoAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(galileoAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_galileoAssistanceGetSatelliteCorrections =
+            env->GetMethodID(galileoAssistanceClass, "getSatelliteCorrections",
+                             "()Ljava/util/List;");
+    method_galileoAssistanceGetRealTimeIntegrityModels =
+            env->GetMethodID(galileoAssistanceClass, "getRealTimeIntegrityModels",
+                             "()Ljava/util/List;");
+
+    // Get the methods of GalileoSatelliteEphemeris class
+    jclass galileoSatelliteEphemerisClass =
+            env->FindClass("android/location/GalileoSatelliteEphemeris");
+    method_galileoSatelliteEphemerisGetSatelliteClockModels =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteClockModels",
+                             "()Ljava/util/List;");
+    method_galileoSatelliteEphemerisGetSvid =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSvid", "()I");
+    method_galileoSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/SatelliteEphemerisTime;");
+    method_galileoSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/GalileoSatelliteEphemeris$GalileoSvHealth;");
+    method_galileoSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+
+    // Get the methods of GalileoSatelliteClockModel class.
+    jclass galileoSatelliteClockModelClass =
+            env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSatelliteClockModel");
+    method_galileoSatelliteClockModelGetAf0 =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getAf0", "()D");
+    method_galileoSatelliteClockModelGetAf1 =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getAf1", "()D");
+    method_galileoSatelliteClockModelGetAf2 =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getAf2", "()D");
+    method_galileoSatelliteClockModelGetBgdSeconds =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getBgdSeconds", "()D");
+    method_galileoSatelliteClockModelGetSatelliteClockType =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getSatelliteClockType", "()I");
+    method_galileoSatelliteClockModelGetSisaMeters =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getSisaMeters", "()D");
+    method_galileoSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of GalileoSvHealth class.
+    jclass galileoSvHealthClass =
+            env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSvHealth");
+    method_galileoSvHealthGetDataValidityStatusE1b =
+            env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE1b", "()I");
+    method_galileoSvHealthGetDataValidityStatusE5a =
+            env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5a", "()I");
+    method_galileoSvHealthGetDataValidityStatusE5b =
+            env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5b", "()I");
+    method_galileoSvHealthGetSignalHealthStatusE1b =
+            env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE1b", "()I");
+    method_galileoSvHealthGetSignalHealthStatusE5a =
+            env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5a", "()I");
+    method_galileoSvHealthGetSignalHealthStatusE5b =
+            env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5b", "()I");
+
+    // Get the methods of GalileoIonosphericModel class.
+    jclass galileoIonosphericModelClass =
+            env->FindClass("android/location/GalileoIonosphericModel");
+    method_galileoIonosphericModelGetAi0 =
+            env->GetMethodID(galileoIonosphericModelClass, "getAi0", "()D");
+    method_galileoIonosphericModelGetAi1 =
+            env->GetMethodID(galileoIonosphericModelClass, "getAi1", "()D");
+    method_galileoIonosphericModelGetAi2 =
+            env->GetMethodID(galileoIonosphericModelClass, "getAi2", "()D");
+
+    // Get the methods of GlonassAssistance class.
+    jclass glonassAssistanceClass = env->FindClass("android/location/GlonassAssistance");
+    method_glonassAssistanceGetAlmanac = env->GetMethodID(glonassAssistanceClass, "getAlmanac",
+                                                          "()Landroid/location/GlonassAlmanac;");
+    method_glonassAssistanceGetUtcModel = env->GetMethodID(glonassAssistanceClass, "getUtcModel",
+                                                           "()Landroid/location/UtcModel;");
+    method_glonassAssistanceGetTimeModels =
+            env->GetMethodID(glonassAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_glonassAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(glonassAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_glonassAssistanceGetSatelliteCorrections =
+            env->GetMethodID(glonassAssistanceClass, "getSatelliteCorrections",
+                             "()Ljava/util/List;");
+
+    // Get the methods of GlonassAlmanac class.
+    jclass glonassAlmanacClass = env->FindClass("android/location/GlonassAlmanac");
+    method_glonassAlmanacGetIssueDateMillis =
+            env->GetMethodID(glonassAlmanacClass, "getIssueDateMillis", "()J");
+    method_glonassAlmanacGetSatelliteAlmanacs =
+            env->GetMethodID(glonassAlmanacClass, "getSatelliteAlmanacs", "()Ljava/util/List;");
+
+    // Get the methods of GlonassSatelliteAlmanac class
+    jclass glonassSatelliteAlmanacClass =
+            env->FindClass("android/location/GlonassAlmanac$GlonassSatelliteAlmanac");
+    method_glonassSatelliteAlmanacGetDeltaI =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaI", "()D");
+    method_glonassSatelliteAlmanacGetDeltaT =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaT", "()D");
+    method_glonassSatelliteAlmanacGetDeltaTDot =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaTDot", "()D");
+    method_glonassSatelliteAlmanacGetEccentricity =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getEccentricity", "()D");
+    method_glonassSatelliteAlmanacGetFrequencyChannelNumber =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getFrequencyChannelNumber", "()I");
+    method_glonassSatelliteAlmanacGetLambda =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getLambda", "()D");
+    method_glonassSatelliteAlmanacGetOmega =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getOmega", "()D");
+    method_glonassSatelliteAlmanacGetSlotNumber =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getSlotNumber", "()I");
+    method_glonassSatelliteAlmanacGetHealthState =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getHealthState", "()I");
+    method_glonassSatelliteAlmanacGetTLambda =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getTLambda", "()D");
+    method_glonassSatelliteAlmanacGetTau =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getTau", "()D");
+    method_glonassSatelliteAlmanacGetCalendarDayNumber =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getCalendarDayNumber", "()I");
+    method_glonassSatelliteAlmanacGetIsGlonassM =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "isGlonassM", "()Z");
+
+    // Get the methods of GlonassSatelliteEphemeris
+    jclass glonassSatelliteEphemerisClass =
+            env->FindClass("android/location/GlonassSatelliteEphemeris");
+    method_glonassSatelliteEphemerisGetAgeInDays =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getAgeInDays", "()I");
+    method_glonassSatelliteEphemerisGetFrameTimeSeconds =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getFrameTimeSeconds", "()D");
+    method_glonassSatelliteEphemerisGetHealthState =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getHealthState", "()I");
+    method_glonassSatelliteEphemerisGetSlotNumber =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getSlotNumber", "()I");
+    method_glonassSatelliteEphemerisGetSatelliteClockModel =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteClockModel",
+                             "()Landroid/location/"
+                             "GlonassSatelliteEphemeris$GlonassSatelliteClockModel;");
+    method_glonassSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/"
+                             "GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel;");
+    method_glonassSatelliteEphemerisGetUpdateIntervalMinutes =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getUpdateIntervalMinutes", "()I");
+    method_glonassSatelliteEphemerisGetIsGlonassM =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "isGlonassM", "()Z");
+    method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "isUpdateIntervalOdd", "()Z");
+
+    // Get the methods of GlonassSatelliteOrbitModel
+    jclass glonassSatelliteOrbitModelClass =
+            env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel");
+    method_glonassSatelliteOrbitModelGetX =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getX", "()D");
+    method_glonassSatelliteOrbitModelGetXAccel =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getXAccel", "()D");
+    method_glonassSatelliteOrbitModelGetXDot =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getXDot", "()D");
+    method_glonassSatelliteOrbitModelGetY =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getY", "()D");
+    method_glonassSatelliteOrbitModelGetYAccel =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getYAccel", "()D");
+    method_glonassSatelliteOrbitModelGetYDot =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getYDot", "()D");
+    method_glonassSatelliteOrbitModelGetZ =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getZ", "()D");
+    method_glonassSatelliteOrbitModelGetZAccel =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getZAccel", "()D");
+    method_glonassSatelliteOrbitModelGetZDot =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getZDot", "()D");
+
+    // Get the methods of GlonassSatelliteClockModel
+    jclass glonassSatelliteClockModelClass =
+            env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteClockModel");
+    method_glonassSatelliteClockModelGetClockBias =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getClockBias", "()D");
+    method_glonassSatelliteClockModelGetFrequencyBias =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyBias", "()D");
+    method_glonassSatelliteClockModelGetFrequencyChannelNumber =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyChannelNumber", "()I");
+    method_glonassSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of QzssAssistance class.
+    jclass qzssAssistanceClass = env->FindClass("android/location/QzssAssistance");
+    method_qzssAssistanceGetAlmanac =
+            env->GetMethodID(qzssAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+    method_qzssAssistanceGetIonosphericModel =
+            env->GetMethodID(qzssAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_qzssAssistanceGetUtcModel =
+            env->GetMethodID(qzssAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+    method_qzssAssistanceGetLeapSecondsModel =
+            env->GetMethodID(qzssAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_qzssAssistanceGetTimeModels =
+            env->GetMethodID(qzssAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_qzssAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(qzssAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_qzssAssistanceGetSatelliteCorrections =
+            env->GetMethodID(qzssAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+    // Get the methods of QzssSatelliteEphemeris class.
+    jclass qzssSatelliteEphemerisClass = env->FindClass("android/location/QzssSatelliteEphemeris");
+    method_qzssSatelliteEphemerisGetSvid =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSvid", "()I");
+    method_qzssSatelliteEphemerisGetGpsL2Params =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getGpsL2Params",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+    method_qzssSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/SatelliteEphemerisTime;");
+    method_qzssSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+    method_qzssSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+}
+
+GnssAssistanceInterface::GnssAssistanceInterface(
+        const sp<IGnssAssistanceInterface>& iGnssAssistance)
+      : mGnssAssistanceInterface(iGnssAssistance) {
+    assert(mGnssAssistanceInterface != nullptr);
+}
+
+jboolean GnssAssistanceInterface::injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj) {
+    GnssAssistance gnssAssistance;
+    GnssAssistanceUtil::setGnssAssistance(env, gnssAssistanceObj, gnssAssistance);
+    auto status = mGnssAssistanceInterface->injectGnssAssistance(gnssAssistance);
+    return checkAidlStatus(status, "IGnssAssistanceInterface injectGnssAssistance() failed.");
+}
+
+jboolean GnssAssistanceInterface::setCallback(const sp<IGnssAssistanceCallback>& callback) {
+    auto status = mGnssAssistanceInterface->setCallback(callback);
+    return checkAidlStatus(status, "IGnssAssistanceInterface setCallback() failed.");
+}
+
+void GnssAssistanceUtil::setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+                                           GnssAssistance& gnssAssistance) {
+    jobject gpsAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGpsAssistance);
+    jobject glonassAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGlonassAssistance);
+    jobject qzssAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetQzssAssistance);
+    jobject galileoAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGalileoAssistance);
+    jobject beidouAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetBeidouAssistance);
+    GnssAssistanceUtil::setGpsAssistance(env, gpsAssistanceObj, gnssAssistance.gpsAssistance);
+    GnssAssistanceUtil::setGlonassAssistance(env, glonassAssistanceObj,
+                                             gnssAssistance.glonassAssistance);
+    GnssAssistanceUtil::setQzssAssistance(env, qzssAssistanceObj, gnssAssistance.qzssAssistance);
+    GnssAssistanceUtil::setGalileoAssistance(env, galileoAssistanceObj,
+                                             gnssAssistance.galileoAssistance);
+    GnssAssistanceUtil::setBeidouAssistance(env, beidouAssistanceObj,
+                                            gnssAssistance.beidouAssistance);
+    env->DeleteLocalRef(gpsAssistanceObj);
+    env->DeleteLocalRef(glonassAssistanceObj);
+    env->DeleteLocalRef(qzssAssistanceObj);
+    env->DeleteLocalRef(galileoAssistanceObj);
+    env->DeleteLocalRef(beidouAssistanceObj);
+}
+
+void GnssAssistanceUtil::setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+                                           QzssAssistance& qzssAssistance) {
+    jobject qzssAlmanacObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetAlmanac);
+    jobject qzssIonosphericModelObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetIonosphericModel);
+    jobject qzssUtcModelObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetUtcModel);
+    jobject qzssLeapSecondsModelObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetLeapSecondsModel);
+    jobject qzssTimeModelsObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetTimeModels);
+    jobject qzssSatelliteEphemerisObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteEphemeris);
+    jobject qzssSatelliteCorrectionsObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteCorrections);
+    setGnssAlmanac(env, qzssAlmanacObj, qzssAssistance.almanac);
+    setKlobucharIonosphericModel(env, qzssIonosphericModelObj, qzssAssistance.ionosphericModel);
+    setUtcModel(env, qzssUtcModelObj, qzssAssistance.utcModel);
+    setLeapSecondsModel(env, qzssLeapSecondsModelObj, qzssAssistance.leapSecondsModel);
+    setTimeModels(env, qzssTimeModelsObj, qzssAssistance.timeModels);
+    setGpsOrQzssSatelliteEphemeris<QzssSatelliteEphemeris>(env, qzssSatelliteEphemerisObj,
+                                                           qzssAssistance.satelliteEphemeris);
+    setSatelliteCorrections(env, qzssSatelliteCorrectionsObj, qzssAssistance.satelliteCorrections);
+    env->DeleteLocalRef(qzssAlmanacObj);
+    env->DeleteLocalRef(qzssIonosphericModelObj);
+    env->DeleteLocalRef(qzssUtcModelObj);
+    env->DeleteLocalRef(qzssLeapSecondsModelObj);
+    env->DeleteLocalRef(qzssTimeModelsObj);
+    env->DeleteLocalRef(qzssSatelliteEphemerisObj);
+    env->DeleteLocalRef(qzssSatelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+                                              GlonassAssistance& galileoAssistance) {
+    jobject glonassAlmanacObj =
+            env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetAlmanac);
+    jobject utcModelObj =
+            env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetUtcModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(glonassAssistanceObj,
+                                  method_glonassAssistanceGetSatelliteEphemeris);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(glonassAssistanceObj,
+                                  method_glonassAssistanceGetSatelliteCorrections);
+    setGlonassAlmanac(env, glonassAlmanacObj, galileoAssistance.almanac);
+    setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+    setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+    setGlonassSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+    env->DeleteLocalRef(glonassAlmanacObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+                                           GlonassAlmanac& glonassAlmanac) {
+    if (glonassAlmanacObj == nullptr) {
+        glonassAlmanac.issueDateMs = -1;
+        return;
+    }
+    jlong issueDateMillis =
+            env->CallLongMethod(glonassAlmanacObj, method_glonassAlmanacGetIssueDateMillis);
+    glonassAlmanac.issueDateMs = issueDateMillis;
+    jobject satelliteAlmanacsObj =
+            env->CallObjectMethod(glonassAlmanacObj, method_glonassAlmanacGetSatelliteAlmanacs);
+    if (satelliteAlmanacsObj == nullptr) return;
+    auto len = env->CallIntMethod(satelliteAlmanacsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject glonassSatelliteAlmanacObj =
+                env->CallObjectMethod(satelliteAlmanacsObj, method_listGet, i);
+        if (glonassSatelliteAlmanacObj == nullptr) continue;
+        GlonassSatelliteAlmanac glonassSatelliteAlmanac;
+        jdouble deltaI = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                               method_glonassSatelliteAlmanacGetDeltaI);
+        glonassSatelliteAlmanac.deltaI = deltaI;
+        jdouble deltaT = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                               method_glonassSatelliteAlmanacGetDeltaT);
+        glonassSatelliteAlmanac.deltaT = deltaT;
+        jdouble deltaTDot = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                                  method_glonassSatelliteAlmanacGetDeltaTDot);
+        glonassSatelliteAlmanac.deltaTDot = deltaTDot;
+        jdouble eccentricity = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                                     method_glonassSatelliteAlmanacGetEccentricity);
+        glonassSatelliteAlmanac.eccentricity = eccentricity;
+        jint frequencyChannelNumber =
+                env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                   method_glonassSatelliteAlmanacGetFrequencyChannelNumber);
+        glonassSatelliteAlmanac.frequencyChannelNumber =
+                static_cast<int32_t>(frequencyChannelNumber);
+        jdouble lambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                               method_glonassSatelliteAlmanacGetLambda);
+        glonassSatelliteAlmanac.lambda = lambda;
+        jdouble omega = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                              method_glonassSatelliteAlmanacGetOmega);
+        glonassSatelliteAlmanac.omega = omega;
+        jint slotNumber = env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                             method_glonassSatelliteAlmanacGetSlotNumber);
+        glonassSatelliteAlmanac.slotNumber = static_cast<int32_t>(slotNumber);
+        jint healthState = env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                              method_glonassSatelliteAlmanacGetHealthState);
+        glonassSatelliteAlmanac.svHealth = static_cast<int32_t>(healthState);
+        jdouble tLambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                                method_glonassSatelliteAlmanacGetTLambda);
+        glonassSatelliteAlmanac.tLambda = tLambda;
+        jdouble tau = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                            method_glonassSatelliteAlmanacGetTau);
+        glonassSatelliteAlmanac.tau = tau;
+        jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteAlmanacObj,
+                                                     method_glonassSatelliteAlmanacGetIsGlonassM);
+        glonassSatelliteAlmanac.isGlonassM = isGlonassM;
+        jint calendarDayNumber =
+                env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                   method_glonassSatelliteAlmanacGetCalendarDayNumber);
+        glonassSatelliteAlmanac.calendarDayNumber = static_cast<int32_t>(calendarDayNumber);
+        glonassAlmanac.satelliteAlmanacs.push_back(glonassSatelliteAlmanac);
+        env->DeleteLocalRef(glonassSatelliteAlmanacObj);
+    }
+    env->DeleteLocalRef(satelliteAlmanacsObj);
+}
+
+void GnssAssistanceUtil::setGlonassSatelliteEphemeris(
+        JNIEnv* env, jobject glonassSatelliteEphemerisListObj,
+        std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList) {
+    if (glonassSatelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(glonassSatelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject glonassSatelliteEphemerisObj =
+                env->CallObjectMethod(glonassSatelliteEphemerisListObj, method_listGet, i);
+        if (glonassSatelliteEphemerisObj == nullptr) continue;
+        GlonassSatelliteEphemeris glonassSatelliteEphemeris;
+        jdouble ageInDays = env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+                                                  method_glonassSatelliteEphemerisGetAgeInDays);
+        glonassSatelliteEphemeris.ageInDays = ageInDays;
+
+        // Set the GlonassSatelliteClockModel.
+        jobject glonassSatelliteClockModelObj =
+                env->CallObjectMethod(glonassSatelliteEphemerisObj,
+                                      method_glonassSatelliteEphemerisGetSatelliteClockModel);
+        GlonassSatelliteClockModel glonassSatelliteClockModel;
+        jdouble clockBias = env->CallDoubleMethod(glonassSatelliteClockModelObj,
+                                                  method_glonassSatelliteClockModelGetClockBias);
+        glonassSatelliteClockModel.clockBias = clockBias;
+        jdouble frequencyBias =
+                env->CallDoubleMethod(glonassSatelliteClockModelObj,
+                                      method_glonassSatelliteClockModelGetFrequencyBias);
+        glonassSatelliteClockModel.frequencyBias = frequencyBias;
+        jint frequencyChannelNumber =
+                env->CallIntMethod(glonassSatelliteClockModelObj,
+                                   method_glonassSatelliteClockModelGetFrequencyChannelNumber);
+        glonassSatelliteClockModel.frequencyChannelNumber =
+                static_cast<int32_t>(frequencyChannelNumber);
+        jdouble timeOfClockSeconds =
+                env->CallDoubleMethod(glonassSatelliteClockModelObj,
+                                      method_glonassSatelliteClockModelGetTimeOfClockSeconds);
+        glonassSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+        glonassSatelliteEphemeris.satelliteClockModel = glonassSatelliteClockModel;
+        env->DeleteLocalRef(glonassSatelliteClockModelObj);
+
+        // Set the GlonassSatelliteOrbitModel.
+        jobject glonassSatelliteOrbitModelObj =
+                env->CallObjectMethod(glonassSatelliteEphemerisObj,
+                                      method_glonassSatelliteEphemerisGetSatelliteOrbitModel);
+        GlonassSatelliteOrbitModel glonassSatelliteOrbitModel;
+        jdouble x = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                          method_glonassSatelliteOrbitModelGetX);
+        glonassSatelliteOrbitModel.x = x;
+        jdouble y = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                          method_glonassSatelliteOrbitModelGetY);
+        glonassSatelliteOrbitModel.y = y;
+        jdouble z = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                          method_glonassSatelliteOrbitModelGetZ);
+        glonassSatelliteOrbitModel.z = z;
+        jdouble xAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                               method_glonassSatelliteOrbitModelGetXAccel);
+        glonassSatelliteOrbitModel.xAccel = xAccel;
+        jdouble yAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                               method_glonassSatelliteOrbitModelGetYAccel);
+        glonassSatelliteOrbitModel.yAccel = yAccel;
+        jdouble zAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                               method_glonassSatelliteOrbitModelGetZAccel);
+        glonassSatelliteOrbitModel.zAccel = zAccel;
+        jdouble xDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                             method_glonassSatelliteOrbitModelGetXDot);
+        glonassSatelliteOrbitModel.xDot = xDot;
+        jdouble yDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                             method_glonassSatelliteOrbitModelGetYDot);
+        glonassSatelliteOrbitModel.yDot = yDot;
+        jdouble zDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                             method_glonassSatelliteOrbitModelGetZDot);
+        glonassSatelliteOrbitModel.zDot = zDot;
+        glonassSatelliteEphemeris.satelliteOrbitModel = glonassSatelliteOrbitModel;
+        env->DeleteLocalRef(glonassSatelliteOrbitModelObj);
+
+        jint healthState = env->CallIntMethod(glonassSatelliteEphemerisObj,
+                                              method_glonassSatelliteEphemerisGetHealthState);
+        glonassSatelliteEphemeris.svHealth = static_cast<int32_t>(healthState);
+        jint slotNumber = env->CallIntMethod(glonassSatelliteEphemerisObj,
+                                             method_glonassSatelliteEphemerisGetSlotNumber);
+        glonassSatelliteEphemeris.slotNumber = static_cast<int32_t>(slotNumber);
+        jdouble frameTimeSeconds =
+                env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+                                      method_glonassSatelliteEphemerisGetFrameTimeSeconds);
+        glonassSatelliteEphemeris.frameTimeSeconds = frameTimeSeconds;
+        jint updateIntervalMinutes =
+                env->CallIntMethod(glonassSatelliteEphemerisObj,
+                                   method_glonassSatelliteEphemerisGetUpdateIntervalMinutes);
+        glonassSatelliteEphemeris.updateIntervalMinutes =
+                static_cast<int32_t>(updateIntervalMinutes);
+        jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+                                                     method_glonassSatelliteEphemerisGetIsGlonassM);
+        glonassSatelliteEphemeris.isGlonassM = isGlonassM;
+        jboolean isUpdateIntervalOdd =
+                env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+                                       method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd);
+        glonassSatelliteEphemeris.isOddUpdateInterval = isUpdateIntervalOdd;
+        glonassSatelliteEphemerisList.push_back(glonassSatelliteEphemeris);
+        env->DeleteLocalRef(glonassSatelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+                                              GalileoAssistance& galileoAssistance) {
+    jobject galileoAlmanacObj =
+            env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetAlmanac);
+    jobject ionosphericModelObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetIonosphericModel);
+    jobject utcModelObj =
+            env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetUtcModel);
+    jobject leapSecondsModelObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetLeapSecondsModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetSatelliteEphemeris);
+    jobject realTimeIntegrityModelsObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetRealTimeIntegrityModels);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetSatelliteCorrections);
+    setGnssAlmanac(env, galileoAlmanacObj, galileoAssistance.almanac);
+    setGaliloKlobucharIonosphericModel(env, ionosphericModelObj,
+                                       galileoAssistance.ionosphericModel);
+    setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+    setLeapSecondsModel(env, leapSecondsModelObj, galileoAssistance.leapSecondsModel);
+    setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+    setGalileoSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+    setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+                               galileoAssistance.realTimeIntegrityModels);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+    env->DeleteLocalRef(galileoAlmanacObj);
+    env->DeleteLocalRef(ionosphericModelObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(leapSecondsModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(realTimeIntegrityModelsObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGaliloKlobucharIonosphericModel(
+        JNIEnv* env, jobject galileoIonosphericModelObj,
+        GalileoIonosphericModel& ionosphericModel) {
+    if (galileoIonosphericModelObj == nullptr) return;
+    jdouble ai0 =
+            env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi0);
+    ionosphericModel.ai0 = ai0;
+    jdouble ai1 =
+            env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi1);
+    ionosphericModel.ai1 = ai1;
+    jdouble ai2 =
+            env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi2);
+    ionosphericModel.ai2 = ai2;
+}
+
+void GnssAssistanceUtil::setGalileoSatelliteEphemeris(
+        JNIEnv* env, jobject galileoSatelliteEphemerisListObj,
+        std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList) {
+    if (galileoSatelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(galileoSatelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject galileoSatelliteEphemerisObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisListObj, method_listGet, i);
+        GalileoSatelliteEphemeris galileoSatelliteEphemeris;
+        GalileoSvHealth galileoSvHealth;
+        // Set the svid of the satellite.
+        jint svid = env->CallLongMethod(galileoSatelliteEphemerisObj,
+                                        method_galileoSatelliteEphemerisGetSvid);
+        galileoSatelliteEphemeris.svid = svid;
+
+        // Set the satellite clock models.
+        jobject galileoSatelliteClockModelListObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteClockModels);
+        auto size = env->CallIntMethod(galileoSatelliteClockModelListObj, method_listSize);
+        for (uint16_t j = 0; j < size; ++j) {
+            jobject galileoSatelliteClockModelObj =
+                    env->CallObjectMethod(galileoSatelliteClockModelListObj, method_listGet, j);
+            if (galileoSatelliteClockModelObj == nullptr) continue;
+            GalileoSatelliteClockModel galileoSatelliteClockModel;
+            jdouble af0 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                                method_galileoSatelliteClockModelGetAf0);
+            galileoSatelliteClockModel.af0 = af0;
+            jdouble af1 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                                method_galileoSatelliteClockModelGetAf1);
+            galileoSatelliteClockModel.af1 = af1;
+            jdouble af2 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                                method_galileoSatelliteClockModelGetAf2);
+            galileoSatelliteClockModel.af2 = af2;
+            jdouble bgdSeconds =
+                    env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                          method_galileoSatelliteClockModelGetBgdSeconds);
+            galileoSatelliteClockModel.bgdSeconds = bgdSeconds;
+            jint satelliteClockType =
+                    env->CallIntMethod(galileoSatelliteClockModelObj,
+                                       method_galileoSatelliteClockModelGetSatelliteClockType);
+            galileoSatelliteClockModel.satelliteClockType =
+                    static_cast<GalileoSatelliteClockModel::SatelliteClockType>(satelliteClockType);
+            jdouble sisaMeters =
+                    env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                          method_galileoSatelliteClockModelGetSisaMeters);
+            galileoSatelliteClockModel.sisaMeters = sisaMeters;
+            jdouble timeOfClockSeconds =
+                    env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                          method_galileoSatelliteClockModelGetTimeOfClockSeconds);
+            galileoSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+            galileoSatelliteEphemeris.satelliteClockModel.push_back(galileoSatelliteClockModel);
+            env->DeleteLocalRef(galileoSatelliteClockModelObj);
+        }
+        env->DeleteLocalRef(galileoSatelliteClockModelListObj);
+
+        // Set the satelliteOrbitModel of the satellite.
+        jobject satelliteOrbitModelObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteOrbitModel);
+        GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+                                                   galileoSatelliteEphemeris.satelliteOrbitModel);
+        env->DeleteLocalRef(satelliteOrbitModelObj);
+
+        // Set the satellite health of the satellite clock model.
+        jobject galileoSvHealthObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteHealth);
+        jint dataValidityStatusE1b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetDataValidityStatusE1b);
+        galileoSvHealth.dataValidityStatusE1b =
+                static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE1b);
+        jint dataValidityStatusE5a =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetDataValidityStatusE5a);
+        galileoSvHealth.dataValidityStatusE5a =
+                static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5a);
+        jint dataValidityStatusE5b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetDataValidityStatusE5b);
+        galileoSvHealth.dataValidityStatusE5b =
+                static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5b);
+        jint signalHealthStatusE1b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetSignalHealthStatusE1b);
+        galileoSvHealth.signalHealthStatusE1b =
+                static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE1b);
+        jint signalHealthStatusE5a =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetSignalHealthStatusE5a);
+        galileoSvHealth.signalHealthStatusE5a =
+                static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5a);
+        jint signalHealthStatusE5b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetSignalHealthStatusE5b);
+        galileoSvHealth.signalHealthStatusE5b =
+                static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5b);
+        galileoSatelliteEphemeris.svHealth = galileoSvHealth;
+        env->DeleteLocalRef(galileoSvHealthObj);
+
+        // Set the satelliteEphemerisTime of the satellite.
+        jobject satelliteEphemerisTimeObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteEphemerisTime);
+        GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+                                                      galileoSatelliteEphemeris
+                                                              .satelliteEphemerisTime);
+        env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+        galileoSatelliteEphemerisList.push_back(galileoSatelliteEphemeris);
+        env->DeleteLocalRef(galileoSatelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+                                             BeidouAssistance& beidouAssistance) {
+    jobject beidouAlmanacObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetAlmanac);
+    jobject ionosphericModelObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetIonosphericModel);
+    jobject utcModelObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetUtcModel);
+    jobject leapSecondsModelObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetLeapSecondsModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(beidouAssistanceObj,
+                                  method_beidouAssistanceGetSatelliteEphemeris);
+    jobject realTimeIntegrityModelsObj =
+            env->CallObjectMethod(beidouAssistanceObj,
+                                  method_beidouAssistanceGetRealTimeIntegrityModels);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(beidouAssistanceObj,
+                                  method_beidouAssistanceGetSatelliteCorrections);
+    setGnssAlmanac(env, beidouAlmanacObj, beidouAssistance.almanac);
+    setKlobucharIonosphericModel(env, ionosphericModelObj, beidouAssistance.ionosphericModel);
+    setUtcModel(env, utcModelObj, beidouAssistance.utcModel);
+    setLeapSecondsModel(env, leapSecondsModelObj, beidouAssistance.leapSecondsModel);
+    setTimeModels(env, timeModelsObj, beidouAssistance.timeModels);
+    setBeidouSatelliteEphemeris(env, satelliteEphemerisObj, beidouAssistance.satelliteEphemeris);
+    setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+                               beidouAssistance.realTimeIntegrityModels);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, beidouAssistance.satelliteCorrections);
+    env->DeleteLocalRef(beidouAlmanacObj);
+    env->DeleteLocalRef(ionosphericModelObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(leapSecondsModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(realTimeIntegrityModelsObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setBeidouSatelliteEphemeris(
+        JNIEnv* env, jobject beidouSatelliteEphemerisListObj,
+        std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList) {
+    if (beidouSatelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(beidouSatelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject beidouSatelliteEphemerisObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisListObj, method_listGet, i);
+        if (beidouSatelliteEphemerisObj == nullptr) continue;
+        BeidouSatelliteEphemeris beidouSatelliteEphemeris;
+
+        // Set the svid of the satellite.
+        jint svid = env->CallIntMethod(beidouSatelliteEphemerisObj,
+                                       method_beidouSatelliteEphemerisGetSvid);
+        beidouSatelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+        // Set the satelliteClockModel of the satellite.
+        jobject satelliteClockModelObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteClockModel);
+        jdouble af0 = env->CallDoubleMethod(satelliteClockModelObj,
+                                            method_beidouSatelliteClockModelGetAf0);
+        jdouble af1 = env->CallDoubleMethod(satelliteClockModelObj,
+                                            method_beidouSatelliteClockModelGetAf1);
+        jdouble af2 = env->CallDoubleMethod(satelliteClockModelObj,
+                                            method_beidouSatelliteClockModelGetAf2);
+        jdouble tgd1 = env->CallDoubleMethod(satelliteClockModelObj,
+                                             method_beidouSatelliteClockModelGetTgd1);
+        jdouble tgd2 = env->CallDoubleMethod(satelliteClockModelObj,
+                                             method_beidouSatelliteClockModelGetTgd2);
+        jdouble aodc = env->CallDoubleMethod(satelliteClockModelObj,
+                                             method_beidouSatelliteClockModelGetAodc);
+        jlong timeOfClockSeconds =
+                env->CallLongMethod(satelliteClockModelObj,
+                                    method_beidouSatelliteClockModelGetTimeOfClockSeconds);
+        beidouSatelliteEphemeris.satelliteClockModel.af0 = af0;
+        beidouSatelliteEphemeris.satelliteClockModel.af1 = af1;
+        beidouSatelliteEphemeris.satelliteClockModel.af2 = af2;
+        beidouSatelliteEphemeris.satelliteClockModel.tgd1 = tgd1;
+        beidouSatelliteEphemeris.satelliteClockModel.tgd2 = tgd2;
+        beidouSatelliteEphemeris.satelliteClockModel.aodc = aodc;
+        beidouSatelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+        env->DeleteLocalRef(satelliteClockModelObj);
+
+        // Set the satelliteOrbitModel of the satellite.
+        jobject satelliteOrbitModelObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteOrbitModel);
+        GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+                                                   beidouSatelliteEphemeris.satelliteOrbitModel);
+        env->DeleteLocalRef(satelliteOrbitModelObj);
+
+        // Set the satelliteHealth of the satellite.
+        jobject satelliteHealthObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteHealth);
+        jint satH1 = env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSatH1);
+        jint svAccur =
+                env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSvAccur);
+        beidouSatelliteEphemeris.satelliteHealth.satH1 = static_cast<int32_t>(satH1);
+        beidouSatelliteEphemeris.satelliteHealth.svAccur = static_cast<int32_t>(svAccur);
+        env->DeleteLocalRef(satelliteHealthObj);
+
+        // Set the satelliteEphemerisTime of the satellite.
+        jobject satelliteEphemerisTimeObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteEphemerisTime);
+        jint iode = env->CallIntMethod(satelliteEphemerisTimeObj,
+                                       method_beidouSatelliteEphemerisTimeGetIode);
+        jint beidouWeekNumber =
+                env->CallIntMethod(satelliteEphemerisTimeObj,
+                                   method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber);
+        jint toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+                                                method_beidouSatelliteEphemerisTimeGetToeSeconds);
+        beidouSatelliteEphemeris.satelliteEphemerisTime.aode = static_cast<int32_t>(iode);
+        beidouSatelliteEphemeris.satelliteEphemerisTime.weekNumber =
+                static_cast<int32_t>(beidouWeekNumber);
+        beidouSatelliteEphemeris.satelliteEphemerisTime.toeSeconds =
+                static_cast<int32_t>(toeSeconds);
+        env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+        beidouSatelliteEphemerisList.push_back(beidouSatelliteEphemeris);
+        env->DeleteLocalRef(beidouSatelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+                                          GpsAssistance& gpsAssistance) {
+    jobject gnssAlmanacObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetAlmanac);
+    jobject ionosphericModelObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetIonosphericModel);
+    jobject utcModelObj = env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetUtcModel);
+    jobject leapSecondsModelObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetLeapSecondsModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteEphemeris);
+    jobject realTimeIntegrityModelsObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetRealTimeIntegrityModels);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteCorrections);
+
+    setGnssAlmanac(env, gnssAlmanacObj, gpsAssistance.almanac);
+    setKlobucharIonosphericModel(env, ionosphericModelObj, gpsAssistance.ionosphericModel);
+    setUtcModel(env, utcModelObj, gpsAssistance.utcModel);
+    setLeapSecondsModel(env, leapSecondsModelObj, gpsAssistance.leapSecondsModel);
+    setTimeModels(env, timeModelsObj, gpsAssistance.timeModels);
+    setGpsOrQzssSatelliteEphemeris<GpsSatelliteEphemeris>(env, satelliteEphemerisObj,
+                                                          gpsAssistance.satelliteEphemeris);
+    setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+                               gpsAssistance.realTimeIntegrityModels);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, gpsAssistance.satelliteCorrections);
+    env->DeleteLocalRef(gnssAlmanacObj);
+    env->DeleteLocalRef(ionosphericModelObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(leapSecondsModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(realTimeIntegrityModelsObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+/** Set the GPS/QZSS satellite ephemeris list. */
+template <class T>
+void GnssAssistanceUtil::setGpsOrQzssSatelliteEphemeris(JNIEnv* env,
+                                                        jobject satelliteEphemerisListObj,
+                                                        std::vector<T>& satelliteEphemerisList) {
+    if (satelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(satelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject satelliteEphemerisObj =
+                env->CallObjectMethod(satelliteEphemerisListObj, method_listGet, i);
+        if (satelliteEphemerisObj == nullptr) continue;
+        T satelliteEphemeris;
+        // Set the svid of the satellite.
+        jint svid = env->CallIntMethod(satelliteEphemerisObj, method_gpsSatelliteEphemerisGetSvid);
+        satelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+        // Set the gpsL2Params of the satellite.
+        jobject gpsL2ParamsObj = env->CallObjectMethod(satelliteEphemerisObj,
+                                                       method_gpsSatelliteEphemerisGetGpsL2Params);
+        jint l2Code = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Code);
+        jint l2Flag = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Flag);
+        satelliteEphemeris.gpsL2Params.l2Code = static_cast<int32_t>(l2Code);
+        satelliteEphemeris.gpsL2Params.l2Flag = static_cast<int32_t>(l2Flag);
+        env->DeleteLocalRef(gpsL2ParamsObj);
+
+        // Set the satelliteClockModel of the satellite.
+        jobject satelliteClockModelObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteClockModel);
+        jdouble af0 =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf0);
+        jdouble af1 =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf1);
+        jdouble af2 =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf2);
+        jdouble tgd =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetTgd);
+        jint iodc =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetIodc);
+        jlong timeOfClockSeconds =
+                env->CallLongMethod(satelliteClockModelObj,
+                                    method_gpsSatelliteClockModelGetTimeOfClockSeconds);
+        satelliteEphemeris.satelliteClockModel.af0 = af0;
+        satelliteEphemeris.satelliteClockModel.af1 = af1;
+        satelliteEphemeris.satelliteClockModel.af2 = af2;
+        satelliteEphemeris.satelliteClockModel.tgd = tgd;
+        satelliteEphemeris.satelliteClockModel.iodc = static_cast<int32_t>(iodc);
+        satelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+        env->DeleteLocalRef(satelliteClockModelObj);
+
+        // Set the satelliteOrbitModel of the satellite.
+        jobject satelliteOrbitModelObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteOrbitModel);
+        GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+                                                   satelliteEphemeris.satelliteOrbitModel);
+        env->DeleteLocalRef(satelliteOrbitModelObj);
+
+        // Set the satelliteHealth of the satellite.
+        jobject satelliteHealthObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteHealth);
+        jint svHealth =
+                env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvHealth);
+        jdouble svAccur =
+                env->CallDoubleMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvAccur);
+        jdouble fitInt = env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetFitInt);
+        satelliteEphemeris.satelliteHealth.svHealth = static_cast<int32_t>(svHealth);
+        satelliteEphemeris.satelliteHealth.svAccur = svAccur;
+        satelliteEphemeris.satelliteHealth.fitInt = fitInt;
+        env->DeleteLocalRef(satelliteHealthObj);
+
+        // Set the satelliteEphemerisTime of the satellite.
+        jobject satelliteEphemerisTimeObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteEphemerisTime);
+        GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+                                                      satelliteEphemeris.satelliteEphemerisTime);
+        env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+        satelliteEphemerisList.push_back(satelliteEphemeris);
+        env->DeleteLocalRef(satelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setSatelliteCorrections(
+        JNIEnv* env, jobject satelliteCorrectionsObj,
+        std::vector<GnssSatelliteCorrections>& gnssSatelliteCorrectionsList) {
+    if (satelliteCorrectionsObj == nullptr) return;
+    auto len = env->CallIntMethod(satelliteCorrectionsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        GnssSatelliteCorrections gnssSatelliteCorrections;
+        jobject satelliteCorrectionObj =
+                env->CallObjectMethod(satelliteCorrectionsObj, method_listGet, i);
+        if (satelliteCorrectionObj == nullptr) continue;
+        jint svid = env->CallIntMethod(satelliteCorrectionObj, method_satelliteCorrectionGetSvid);
+        gnssSatelliteCorrections.svid = svid;
+        jobject ionosphericCorrectionsObj =
+                env->CallObjectMethod(satelliteCorrectionObj,
+                                      method_satelliteCorrectionGetIonosphericCorrections);
+        env->DeleteLocalRef(satelliteCorrectionObj);
+        auto size = env->CallIntMethod(ionosphericCorrectionsObj, method_listSize);
+        for (uint16_t j = 0; j < size; ++j) {
+            jobject ionosphericCorrectionObj =
+                    env->CallObjectMethod(ionosphericCorrectionsObj, method_listGet, j);
+            if (ionosphericCorrectionObj == nullptr) continue;
+            IonosphericCorrection ionosphericCorrection;
+            jlong carrierFrequencyHz =
+                    env->CallLongMethod(ionosphericCorrectionObj,
+                                        method_ionosphericCorrectionGetCarrierFrequencyHz);
+            ionosphericCorrection.carrierFrequencyHz = carrierFrequencyHz;
+
+            jobject gnssCorrectionComponentObj =
+                    env->CallObjectMethod(ionosphericCorrectionObj,
+                                          method_ionosphericCorrectionGetIonosphericCorrection);
+            env->DeleteLocalRef(ionosphericCorrectionObj);
+
+            jstring sourceKey = static_cast<jstring>(
+                    env->CallObjectMethod(gnssCorrectionComponentObj,
+                                          method_gnssCorrectionComponentGetSourceKey));
+            ScopedJniString jniSourceKey{env, sourceKey};
+            ionosphericCorrection.ionosphericCorrectionComponent.sourceKey =
+                    android::String16(jniSourceKey.c_str());
+
+            jobject pseudorangeCorrectionObj =
+                    env->CallObjectMethod(gnssCorrectionComponentObj,
+                                          method_gnssCorrectionComponentGetPseudorangeCorrection);
+            jdouble correctionMeters =
+                    env->CallDoubleMethod(pseudorangeCorrectionObj,
+                                          method_pseudorangeCorrectionGetCorrectionMeters);
+            jdouble correctionUncertaintyMeters = env->CallDoubleMethod(
+                    pseudorangeCorrectionObj,
+                    method_pseudorangeCorrectionGetCorrectionUncertaintyMeters);
+            jdouble correctionRateMetersPerSecond = env->CallDoubleMethod(
+                    pseudorangeCorrectionObj,
+                    method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond);
+            ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+                    .correctionMeters = correctionMeters;
+            ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+                    .correctionUncertaintyMeters = correctionUncertaintyMeters;
+            ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+                    .correctionRateMetersPerSecond = correctionRateMetersPerSecond;
+            env->DeleteLocalRef(pseudorangeCorrectionObj);
+
+            jobject gnssIntervalObj =
+                    env->CallObjectMethod(gnssCorrectionComponentObj,
+                                          method_gnssCorrectionComponentGetValidityInterval);
+            jdouble startMillisSinceGpsEpoch =
+                    env->CallDoubleMethod(gnssIntervalObj,
+                                          method_gnssIntervalGetStartMillisSinceGpsEpoch);
+            jdouble endMillisSinceGpsEpoch =
+                    env->CallDoubleMethod(gnssIntervalObj,
+                                          method_gnssIntervalGetEndMillisSinceGpsEpoch);
+            ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+                    .startMillisSinceGpsEpoch = startMillisSinceGpsEpoch;
+            ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+                    .endMillisSinceGpsEpoch = endMillisSinceGpsEpoch;
+            env->DeleteLocalRef(gnssIntervalObj);
+
+            env->DeleteLocalRef(gnssCorrectionComponentObj);
+            gnssSatelliteCorrections.ionosphericCorrections.push_back(ionosphericCorrection);
+        }
+        gnssSatelliteCorrectionsList.push_back(gnssSatelliteCorrections);
+        env->DeleteLocalRef(ionosphericCorrectionsObj);
+    }
+}
+
+void GnssAssistanceUtil::setRealTimeIntegrityModels(
+        JNIEnv* env, jobject realTimeIntegrityModelsObj,
+        std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels) {
+    if (realTimeIntegrityModelsObj == nullptr) return;
+    auto len = env->CallIntMethod(realTimeIntegrityModelsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject realTimeIntegrityModelObj =
+                env->CallObjectMethod(realTimeIntegrityModelsObj, method_listGet, i);
+        if (realTimeIntegrityModelObj == nullptr) continue;
+        RealTimeIntegrityModel realTimeIntegrityModel;
+        jint badSvid = env->CallIntMethod(realTimeIntegrityModelObj,
+                                          method_realTimeIntegrityModelGetBadSvid);
+        jobject badSignalTypesObj =
+                env->CallObjectMethod(realTimeIntegrityModelObj,
+                                      method_realTimeIntegrityModelGetBadSignalTypes);
+        auto badSignalTypesSize = env->CallIntMethod(badSignalTypesObj, method_listSize);
+        for (uint16_t j = 0; j < badSignalTypesSize; ++j) {
+            GnssSignalType badSignalType;
+            jobject badSignalTypeObj = env->CallObjectMethod(badSignalTypesObj, method_listGet, j);
+            if (badSignalTypeObj != nullptr) {
+                setGnssSignalType(env, badSignalTypeObj, badSignalType);
+                realTimeIntegrityModel.badSignalTypes.push_back(badSignalType);
+                env->DeleteLocalRef(badSignalTypeObj);
+            }
+        }
+
+        jlong startDateSeconds =
+                env->CallLongMethod(realTimeIntegrityModelObj,
+                                    method_realTimeIntegrityModelGetStartDateSeconds);
+        jlong endDateSeconds = env->CallLongMethod(realTimeIntegrityModelObj,
+                                                   method_realTimeIntegrityModelGetEndDateSeconds);
+        jlong publishDateSeconds =
+                env->CallLongMethod(realTimeIntegrityModelObj,
+                                    method_realTimeIntegrityModelGetPublishDateSeconds);
+        jstring advisoryNumber = static_cast<jstring>(
+                env->CallObjectMethod(realTimeIntegrityModelObj,
+                                      method_realTimeIntegrityModelGetAdvisoryNumber));
+        ScopedJniString jniAdvisoryNumber{env, advisoryNumber};
+        jstring advisoryType = static_cast<jstring>(
+                env->CallObjectMethod(realTimeIntegrityModelObj,
+                                      method_realTimeIntegrityModelGetAdvisoryType));
+        ScopedJniString jniAdvisoryType{env, advisoryType};
+
+        realTimeIntegrityModel.badSvid = badSvid;
+        realTimeIntegrityModel.startDateSeconds = startDateSeconds;
+        realTimeIntegrityModel.endDateSeconds = endDateSeconds;
+        realTimeIntegrityModel.publishDateSeconds = publishDateSeconds;
+        realTimeIntegrityModel.advisoryNumber = android::String16(jniAdvisoryNumber.c_str());
+        realTimeIntegrityModel.advisoryType = android::String16(jniAdvisoryType.c_str());
+        realTimeIntegrityModels.push_back(realTimeIntegrityModel);
+        env->DeleteLocalRef(badSignalTypesObj);
+        env->DeleteLocalRef(realTimeIntegrityModelObj);
+    }
+}
+
+void GnssAssistanceUtil::setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+                                           GnssSignalType& gnssSignalType) {
+    if (gnssSignalTypeObj == nullptr) {
+        ALOGE("gnssSignalTypeObj is null");
+        return;
+    }
+    jint constellationType =
+            env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetConstellationType);
+    jdouble carrierFrequencyHz =
+            env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCarrierFrequencyHz);
+    jstring codeType = static_cast<jstring>(
+            env->CallObjectMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCodeType));
+    ScopedJniString jniCodeType{env, codeType};
+
+    gnssSignalType.constellation = static_cast<GnssConstellationType>(constellationType);
+    gnssSignalType.carrierFrequencyHz = static_cast<int32_t>(carrierFrequencyHz);
+    gnssSignalType.codeType = std::string(jniCodeType.c_str());
+}
+
+void GnssAssistanceUtil::setTimeModels(JNIEnv* env, jobject timeModelsObj,
+                                       std::vector<TimeModel>& timeModels) {
+    if (timeModelsObj == nullptr) return;
+    auto len = env->CallIntMethod(timeModelsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject timeModelObj = env->CallObjectMethod(timeModelsObj, method_listGet, i);
+        TimeModel timeModel;
+        jint toGnss = env->CallIntMethod(timeModelObj, method_timeModelsGetToGnss);
+        jlong timeOfWeek = env->CallLongMethod(timeModelObj, method_timeModelsGetTimeOfWeek);
+        jint weekNumber = env->CallIntMethod(timeModelObj, method_timeModelsGetWeekNumber);
+        jdouble a0 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA0);
+        jdouble a1 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA1);
+        timeModel.toGnss = static_cast<GnssConstellationType>(toGnss);
+        timeModel.timeOfWeek = timeOfWeek;
+        timeModel.weekNumber = static_cast<int32_t>(weekNumber);
+        timeModel.a0 = a0;
+        timeModel.a1 = a1;
+        timeModels.push_back(timeModel);
+        env->DeleteLocalRef(timeModelObj);
+    }
+}
+
+void GnssAssistanceUtil::setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+                                             LeapSecondsModel& leapSecondsModel) {
+    if (leapSecondsModelObj == nullptr) {
+        leapSecondsModel.leapSeconds = -1;
+        return;
+    }
+    jint dayNumberLeapSecondsFuture =
+            env->CallIntMethod(leapSecondsModelObj,
+                               method_leapSecondsModelGetDayNumberLeapSecondsFuture);
+    jint leapSeconds =
+            env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSeconds);
+    jint leapSecondsFuture =
+            env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSecondsFuture);
+    jint weekNumberLeapSecondsFuture =
+            env->CallIntMethod(leapSecondsModelObj,
+                               method_leapSecondsModelGetWeekNumberLeapSecondsFuture);
+    leapSecondsModel.dayNumberLeapSecondsFuture = static_cast<int32_t>(dayNumberLeapSecondsFuture);
+    leapSecondsModel.leapSeconds = static_cast<int32_t>(leapSeconds);
+    leapSecondsModel.leapSecondsFuture = static_cast<int32_t>(leapSecondsFuture);
+    leapSecondsModel.weekNumberLeapSecondsFuture =
+            static_cast<int32_t>(weekNumberLeapSecondsFuture);
+}
+
+void GnssAssistanceUtil::setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+                                                   SatelliteEphemerisTime& satelliteEphemerisTime) {
+    if (satelliteEphemerisTimeObj == nullptr) return;
+    jdouble iode =
+            env->CallDoubleMethod(satelliteEphemerisTimeObj, method_satelliteEphemerisTimeGetIode);
+    jdouble toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+                                               method_satelliteEphemerisTimeGetToeSeconds);
+    jint weekNumber = env->CallIntMethod(satelliteEphemerisTimeObj,
+                                         method_satelliteEphemerisTimeGetWeekNumber);
+    satelliteEphemerisTime.iode = iode;
+    satelliteEphemerisTime.toeSeconds = toeSeconds;
+    satelliteEphemerisTime.weekNumber = weekNumber;
+}
+
+void GnssAssistanceUtil::setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+                                                KeplerianOrbitModel& keplerianOrbitModel) {
+    if (keplerianOrbitModelObj == nullptr) return;
+    jdouble rootA =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetRootA);
+    jdouble eccentricity = env->CallDoubleMethod(keplerianOrbitModelObj,
+                                                 method_keplerianOrbitModelGetEccentricity);
+    jdouble m0 = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetM0);
+    jdouble omega =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmega);
+    jdouble omegaDot =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmegaDot);
+    jdouble deltaN =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetDeltaN);
+    jdouble iDot = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetIDot);
+    jobject secondOrderHarmonicPerturbationObj =
+            env->CallObjectMethod(keplerianOrbitModelObj,
+                                  method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation);
+    jdouble cic = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCic);
+    jdouble cis = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCis);
+    jdouble crs = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCrs);
+    jdouble crc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCrc);
+    jdouble cuc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCuc);
+    jdouble cus = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCus);
+    keplerianOrbitModel.rootA = rootA;
+    keplerianOrbitModel.eccentricity = eccentricity;
+    keplerianOrbitModel.m0 = m0;
+    keplerianOrbitModel.omega = omega;
+    keplerianOrbitModel.omegaDot = omegaDot;
+    keplerianOrbitModel.deltaN = deltaN;
+    keplerianOrbitModel.iDot = iDot;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cic = cic;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cis = cis;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.crs = crs;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.crc = crc;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cuc = cuc;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cus = cus;
+    env->DeleteLocalRef(secondOrderHarmonicPerturbationObj);
+}
+
+void GnssAssistanceUtil::setKlobucharIonosphericModel(
+        JNIEnv* env, jobject klobucharIonosphericModelObj,
+        KlobucharIonosphericModel& klobucharIonosphericModel) {
+    if (klobucharIonosphericModelObj == nullptr) return;
+    jdouble alpha0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha0);
+    jdouble alpha1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha1);
+    jdouble alpha2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha2);
+    jdouble alpha3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha3);
+    jdouble beta0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta0);
+    jdouble beta1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta1);
+    jdouble beta2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta2);
+    jdouble beta3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta3);
+    klobucharIonosphericModel.alpha0 = alpha0;
+    klobucharIonosphericModel.alpha1 = alpha1;
+    klobucharIonosphericModel.alpha2 = alpha2;
+    klobucharIonosphericModel.alpha3 = alpha3;
+    klobucharIonosphericModel.beta0 = beta0;
+    klobucharIonosphericModel.beta1 = beta1;
+    klobucharIonosphericModel.beta2 = beta2;
+    klobucharIonosphericModel.beta3 = beta3;
+}
+
+void GnssAssistanceUtil::setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel) {
+    if (utcModelObj == nullptr) {
+        utcModel.weekNumber = -1;
+        return;
+    }
+    jdouble a0 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA0);
+    jdouble a1 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA1);
+    jlong timeOfWeek = env->CallLongMethod(utcModelObj, method_utcModelGetTimeOfWeek);
+    jint weekNumber = env->CallIntMethod(utcModelObj, method_utcModelGetWeekNumber);
+    utcModel.a0 = a0;
+    utcModel.a1 = a1;
+    utcModel.timeOfWeek = timeOfWeek;
+    utcModel.weekNumber = static_cast<int32_t>(weekNumber);
+}
+
+void GnssAssistanceUtil::setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj,
+                                        GnssAlmanac& gnssAlmanac) {
+    if (gnssAlmanacObj == nullptr) {
+        gnssAlmanac.weekNumber = -1;
+        return;
+    }
+    jlong issueDateMillis =
+            env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetIssueDateMillis);
+    jint ioda = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetIoda);
+    jint weekNumber = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetWeekNumber);
+    jlong toaSeconds = env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetToaSeconds);
+    jboolean isCompleteAlmanacProvided =
+            env->CallBooleanMethod(gnssAlmanacObj, method_gnssAlmanacIsCompleteAlmanacProvided);
+    gnssAlmanac.issueDateMs = issueDateMillis;
+    gnssAlmanac.ioda = ioda;
+    gnssAlmanac.weekNumber = weekNumber;
+    gnssAlmanac.toaSeconds = toaSeconds;
+    gnssAlmanac.isCompleteAlmanacProvided = isCompleteAlmanacProvided;
+
+    jobject satelliteAlmanacsListObj =
+            env->CallObjectMethod(gnssAlmanacObj, method_gnssAlmanacGetSatelliteAlmanacs);
+    auto len = env->CallIntMethod(satelliteAlmanacsListObj, method_listSize);
+    std::vector<GnssSatelliteAlmanac> list(len);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject gnssSatelliteAlmanacObj =
+                env->CallObjectMethod(satelliteAlmanacsListObj, method_listGet, i);
+        if (gnssSatelliteAlmanacObj == nullptr) continue;
+        GnssSatelliteAlmanac gnssSatelliteAlmanac;
+        jint svid = env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvid);
+        jint svHealth =
+                env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvHealth);
+        jdouble af0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf0);
+        jdouble af1 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf1);
+        jdouble eccentricity = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+                                                     method_satelliteAlmanacGetEccentricity);
+        jdouble inclination = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+                                                    method_satelliteAlmanacGetInclination);
+        jdouble m0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetM0);
+        jdouble omega =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega);
+        jdouble omega0 =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega0);
+        jdouble omegaDot =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmegaDot);
+        jdouble rootA =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetRootA);
+        gnssSatelliteAlmanac.svid = static_cast<int32_t>(svid);
+        gnssSatelliteAlmanac.svHealth = static_cast<int32_t>(svHealth);
+        gnssSatelliteAlmanac.af0 = af0;
+        gnssSatelliteAlmanac.af1 = af1;
+        gnssSatelliteAlmanac.eccentricity = eccentricity;
+        gnssSatelliteAlmanac.inclination = inclination;
+        gnssSatelliteAlmanac.m0 = m0;
+        gnssSatelliteAlmanac.omega = omega;
+        gnssSatelliteAlmanac.omega0 = omega0;
+        gnssSatelliteAlmanac.omegaDot = omegaDot;
+        gnssSatelliteAlmanac.rootA = rootA;
+        list.at(i) = gnssSatelliteAlmanac;
+        env->DeleteLocalRef(gnssSatelliteAlmanacObj);
+    }
+    gnssAlmanac.satelliteAlmanacs = list;
+    env->DeleteLocalRef(satelliteAlmanacsListObj);
+}
+
+void GnssAssistanceUtil::setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+                                                 AuxiliaryInformation& auxiliaryInformation) {
+    if (auxiliaryInformationObj == nullptr) {
+        auxiliaryInformation.svid = -1;
+        return;
+    }
+    jint svid = env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSvid);
+    jobject availableSignalTypesObj =
+            env->CallObjectMethod(auxiliaryInformationObj,
+                                  method_auxiliaryInformationGetAvailableSignalTypes);
+    auto size = env->CallIntMethod(availableSignalTypesObj, method_listSize);
+    std::vector<GnssSignalType> availableSignalTypes(size);
+    for (uint16_t i = 0; i < size; ++i) {
+        jobject availableSignalTypeObj =
+                env->CallObjectMethod(availableSignalTypesObj, method_listGet, i);
+        GnssSignalType availableSignalType;
+        setGnssSignalType(env, availableSignalTypeObj, availableSignalType);
+        availableSignalTypes.at(i) = availableSignalType;
+        env->DeleteLocalRef(availableSignalTypeObj);
+    }
+    jint frequencyChannelNumber =
+            env->CallIntMethod(auxiliaryInformationObj,
+                               method_auxiliaryInformationGetFrequencyChannelNumber);
+    jint satType =
+            env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSatType);
+    auxiliaryInformation.svid = static_cast<int32_t>(svid);
+    auxiliaryInformation.availableSignalTypes = availableSignalTypes;
+    auxiliaryInformation.frequencyChannelNumber = static_cast<int32_t>(frequencyChannelNumber);
+    auxiliaryInformation.satType = static_cast<BeidouB1CSatelliteOrbitType>(satType);
+    env->DeleteLocalRef(availableSignalTypesObj);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistance.h b/services/core/jni/gnss/GnssAssistance.h
new file mode 100644
index 0000000..ee97e19
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSSASSITANCE_H
+#define _ANDROID_SERVER_GNSSASSITANCE_H
+
+#include <sys/stat.h>
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceInterface.h>
+#include <log/log.h>
+
+#include "GnssAssistanceCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+using IGnssAssistanceInterface = android::hardware::gnss::gnss_assistance::IGnssAssistanceInterface;
+using IGnssAssistanceCallback = android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
+using BeidouAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::BeidouAssistance;
+using BeidouSatelliteEphemeris = android::hardware::gnss::gnss_assistance::BeidouSatelliteEphemeris;
+using GalileoAssistance =
+        android::hardware::gnss::gnss_assistance::GnssAssistance::GalileoAssistance;
+using GalileoSatelliteEphemeris =
+        android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris;
+using GalileoIonosphericModel = android::hardware::gnss::gnss_assistance::GalileoIonosphericModel;
+using GlonassAssistance =
+        android::hardware::gnss::gnss_assistance::GnssAssistance::GlonassAssistance;
+using GlonassAlmanac = android::hardware::gnss::gnss_assistance::GlonassAlmanac;
+using GlonassSatelliteEphemeris =
+        android::hardware::gnss::gnss_assistance::GlonassSatelliteEphemeris;
+using GnssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
+using GpsAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::GpsAssistance;
+using QzssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::QzssAssistance;
+using GnssAlmanac = android::hardware::gnss::gnss_assistance::GnssAlmanac;
+using GnssSatelliteCorrections =
+        android::hardware::gnss::gnss_assistance::GnssAssistance::GnssSatelliteCorrections;
+using GpsSatelliteEphemeris = android::hardware::gnss::gnss_assistance::GpsSatelliteEphemeris;
+using SatelliteEphemerisTime = android::hardware::gnss::gnss_assistance::SatelliteEphemerisTime;
+using UtcModel = android::hardware::gnss::gnss_assistance::UtcModel;
+using LeapSecondsModel = android::hardware::gnss::gnss_assistance::LeapSecondsModel;
+using KeplerianOrbitModel = android::hardware::gnss::gnss_assistance::KeplerianOrbitModel;
+using KlobucharIonosphericModel =
+        android::hardware::gnss::gnss_assistance::KlobucharIonosphericModel;
+using TimeModel = android::hardware::gnss::gnss_assistance::TimeModel;
+using RealTimeIntegrityModel = android::hardware::gnss::gnss_assistance::RealTimeIntegrityModel;
+using AuxiliaryInformation = android::hardware::gnss::gnss_assistance::AuxiliaryInformation;
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssAssistanceInterface {
+public:
+    GnssAssistanceInterface(const sp<IGnssAssistanceInterface>& iGnssAssistance);
+    jboolean injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj);
+    jboolean setCallback(const sp<IGnssAssistanceCallback>& callback);
+
+private:
+    const sp<IGnssAssistanceInterface> mGnssAssistanceInterface;
+};
+
+struct GnssAssistanceUtil {
+    static void setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+                                     GlonassAssistance& galileoAssistance);
+    static void setGlonassSatelliteEphemeris(
+            JNIEnv* env, jobject glonassSatelliteEphemerisObj,
+            std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList);
+    static void setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+                                  GlonassAlmanac& glonassAlmanac);
+    static void setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+                                    BeidouAssistance& beidouAssistance);
+    static void setBeidouSatelliteEphemeris(
+            JNIEnv* env, jobject beidouSatelliteEphemerisObj,
+            std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList);
+    static void setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+                                     GalileoAssistance& galileoAssistance);
+    static void setGalileoSatelliteEphemeris(
+            JNIEnv* env, jobject galileoSatelliteEphemerisObj,
+            std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList);
+    static void setGaliloKlobucharIonosphericModel(JNIEnv* env, jobject galileoIonosphericModelObj,
+                                                   GalileoIonosphericModel& ionosphericModel);
+    static void setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+                                  GnssAssistance& gnssAssistance);
+    static void setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+                                 GpsAssistance& gpsAssistance);
+    template <class T>
+    static void setGpsOrQzssSatelliteEphemeris(JNIEnv* env, jobject satelliteEphemerisObj,
+                                               std::vector<T>& satelliteEphemeris);
+    static void setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+                                  QzssAssistance& qzssAssistance);
+    static void setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj, GnssAlmanac& gnssAlmanac);
+    static void setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+                                  GnssSignalType& gnssSignalType);
+    static void setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+                                       KeplerianOrbitModel& keplerianOrbitModel);
+    static void setKlobucharIonosphericModel(JNIEnv* env, jobject klobucharIonosphericModelObj,
+                                             KlobucharIonosphericModel& klobucharIonosphericModel);
+    static void setTimeModels(JNIEnv* env, jobject timeModelsObj,
+                              std::vector<TimeModel>& timeModels);
+    static void setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+                                    LeapSecondsModel& leapSecondsModel);
+    static void setRealTimeIntegrityModels(
+            JNIEnv* env, jobject realTimeIntegrityModelsObj,
+            std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels);
+
+    static void setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+                                          SatelliteEphemerisTime& satelliteEphemerisTime);
+    static void setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel);
+    static void setSatelliteCorrections(
+            JNIEnv* env, jobject satelliteCorrectionsObj,
+            std::vector<GnssSatelliteCorrections>& satelliteCorrections);
+    static void setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+                                        AuxiliaryInformation& auxiliaryInformation);
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSSASSITANCE__H
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.cpp b/services/core/jni/gnss/GnssAssistanceCallback.cpp
new file mode 100644
index 0000000..dbb27d7
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "GnssAssistanceCbJni"
+
+#include "GnssAssistanceCallback.h"
+
+#include "Utils.h"
+
+namespace {
+jmethodID method_gnssAssistanceInjectRequest;
+} // anonymous namespace
+
+namespace android::gnss {
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz) {
+    method_gnssAssistanceInjectRequest =
+            env->GetStaticMethodID(clazz, "gnssAssistanceInjectRequest", "()V");
+}
+
+// Implementation of android::hardware::gnss::gnss_assistance::GnssAssistanceCallback.
+
+Status GnssAssistanceCallback::injectRequestCb() {
+    ALOGD("%s.", __func__);
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_gnssAssistanceInjectRequest);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.h b/services/core/jni/gnss/GnssAssistanceCallback.h
new file mode 100644
index 0000000..4c8c5fc
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceCallback.h>
+#include <log/log.h>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz);
+
+/*
+ * GnssAssistanceCallback class implements the callback methods required by the
+ * android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback interface.
+ */
+class GnssAssistanceCallback : public hardware::gnss::gnss_assistance::BnGnssAssistanceCallback {
+public:
+    GnssAssistanceCallback() {}
+    binder::Status injectRequestCb() override;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 42e457c..bc5c427 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -51,7 +51,6 @@
 import android.credentials.PrepareGetCredentialResponseInternal;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.credentials.flags.Flags;
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.IBinder;
@@ -538,34 +537,31 @@
 
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
-            if (Flags.safeguardCandidateCredentialsApiCaller()) {
-                try {
-                    String credentialManagerAutofillCompName = mContext.getResources().getString(
-                            R.string.config_defaultCredentialManagerAutofillService);
-                    ComponentName componentName = ComponentName.unflattenFromString(
-                            credentialManagerAutofillCompName);
-                    if (componentName == null) {
-                        throw new SecurityException(
-                                "Credential Autofill service does not exist on this device.");
-                    }
-                    PackageManager pm = mContext.createContextAsUser(
-                            UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
-                    String callingProcessPackage = pm.getNameForUid(callingUid);
-                    if (callingProcessPackage == null) {
-                        throw new SecurityException(
-                                "Couldn't determine the identity of the caller.");
-                    }
-                    if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
-                        throw new SecurityException(callingProcessPackage
-                                + " is not the device's credential autofill package.");
-                    }
-                } catch (Resources.NotFoundException e) {
+            try {
+                String credentialManagerAutofillCompName = mContext.getResources().getString(
+                        R.string.config_defaultCredentialManagerAutofillService);
+                ComponentName componentName = ComponentName.unflattenFromString(
+                        credentialManagerAutofillCompName);
+                if (componentName == null) {
                     throw new SecurityException(
                             "Credential Autofill service does not exist on this device.");
                 }
+                PackageManager pm = mContext.createContextAsUser(
+                        UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+                String callingProcessPackage = pm.getNameForUid(callingUid);
+                if (callingProcessPackage == null) {
+                    throw new SecurityException(
+                            "Couldn't determine the identity of the caller.");
+                }
+                if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
+                    throw new SecurityException(callingProcessPackage
+                            + " is not the device's credential autofill package.");
+                }
+            } catch (Resources.NotFoundException e) {
+                throw new SecurityException(
+                        "Credential Autofill service does not exist on this device.");
             }
 
-
             // New request session, scoped for this request only.
             final GetCandidateRequestSession session =
                     new GetCandidateRequestSession(
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index de78271..69e856d 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -325,7 +325,8 @@
             }
 
             if (newState != INVALID_DEVICE_STATE_IDENTIFIER && newState != mLastReportedState) {
-                if (Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
+                if (mLastHingeAngleSensorEvent != null
+                        && Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
                     Trace.instant(TRACE_TAG_SYSTEM_SERVER,
                             "[Device state changed] Last hinge sensor event timestamp: "
                                     + mLastHingeAngleSensorEvent.timestamp);
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 9117cc8..a38ecc8 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -3186,6 +3186,32 @@
         assertEquals(profile, ikev2VpnProfile.toVpnProfile());
     }
 
+    @Test
+    public void testStartAlwaysOnVpnOnSafeMode() throws Exception {
+        final Vpn vpn = createVpn(PRIMARY_USER.id);
+        setMockedUsers(PRIMARY_USER);
+
+        // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+        final int uid = Process.myUid() + 1;
+        when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+                .thenReturn(uid);
+        when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+                .thenReturn(mVpnProfile.encode());
+
+        setAndVerifyAlwaysOnPackage(vpn, uid, false);
+        assertTrue(vpn.startAlwaysOnVpn());
+        assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+
+        // Simulate safe mode and restart the always-on VPN to verify the always-on package is not
+        // reset.
+        doReturn(null).when(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+        doReturn(null).when(mPackageManager).queryIntentServicesAsUser(
+                any(), any(), eq(PRIMARY_USER.id));
+        doReturn(true).when(mPackageManager).isSafeMode();
+        assertFalse(vpn.startAlwaysOnVpn());
+        assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+    }
+
     // Make it public and un-final so as to spy it
     public class TestDeps extends Vpn.Dependencies {
         TestDeps() {}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 29f0722..fecbc7c8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.hardware.Sensor;
@@ -396,7 +396,7 @@
         assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
         assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
-        verifyZeroInteractions(mWakelockController);
+        verifyNoMoreInteractions(mWakelockController);
     }
 
     private void setScreenOffBecauseOfPositiveProximityState() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f8b4113..b8a5f34 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -234,6 +234,7 @@
 
         doReturn(true).when(mFlags).isDisplayOffloadEnabled();
         doReturn(true).when(mFlags).isEvenDimmerEnabled();
+        doReturn(true).when(mFlags).isDisplayContentModeManagementEnabled();
         initDisplayOffloadSession();
     }
 
@@ -1468,6 +1469,103 @@
         assertFalse(mDisplayOffloadSession.isActive());
     }
 
+    @Test
+    public void testAllowsContentSwitch_firstDisplay() throws Exception {
+        // Set up a first display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // The first display should be allowed to use the content mode switch
+        DisplayDevice firstDisplayDevice = mListener.addedDisplays.get(0);
+        assertTrue((firstDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_secondaryDisplayPublicAndNotShouldShowOwnContent()
+            throws Exception {
+        // Set up a first display and a secondary display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        setUpDisplay(new FakeDisplay(PORT_B));
+        updateAvailableDisplays();
+
+        // Set the secondary display to be a public display
+        doReturn(new int[0]).when(mMockedResources)
+                .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+        // Disable FLAG_OWN_CONTENT_ONLY for the secondary display
+        doReturn(true).when(mMockedResources)
+                .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // This secondary display should be allowed to use the content mode switch
+        DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+        assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_privateDisplay() throws Exception {
+        // Set up a first display and a secondary display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        setUpDisplay(new FakeDisplay(PORT_B));
+        updateAvailableDisplays();
+
+        // Set the secondary display to be a private display
+        doReturn(new int[]{ PORT_B }).when(mMockedResources)
+                .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // The private display should not be allowed to use the content mode switch
+        DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+        assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_ownContentOnlyDisplay() throws Exception {
+        // Set up a first display and a secondary display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        setUpDisplay(new FakeDisplay(PORT_B));
+        updateAvailableDisplays();
+
+        // Enable FLAG_OWN_CONTENT_ONLY for the secondary display
+        doReturn(false).when(mMockedResources)
+                .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // The secondary display with FLAG_OWN_CONTENT_ONLY enabled should not be allowed to use the
+        // content mode switch
+        DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+        assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_flagShouldShowSystemDecorations() throws Exception {
+        // Set up a display
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        setUpDisplay(display);
+        updateAvailableDisplays();
+
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should not be allowed to use the
+        // content mode switch
+        DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+        int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
+        boolean allowsContentModeSwitch =
+                ((flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+        boolean shouldShowSystemDecorations =
+                ((flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0);
+        assertFalse(allowsContentModeSwitch && shouldShowSystemDecorations);
+    }
+
     private void initDisplayOffloadSession() {
         when(mDisplayOffloader.startOffload()).thenReturn(true);
         when(mDisplayOffloader.allowAutoBrightnessInDoze()).thenReturn(true);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index 019b70e..f067fa1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.hardware.display.DisplayManagerInternal;
 
@@ -210,7 +210,7 @@
 
         // Validate one suspend blocker was released
         assertFalse(mWakelockController.isProximityPositiveAcquired());
-        verifyZeroInteractions(mDisplayPowerCallbacks);
+        verifyNoMoreInteractions(mDisplayPowerCallbacks);
     }
 
     @Test
@@ -238,7 +238,7 @@
 
         // Validate one suspend blocker was released
         assertFalse(mWakelockController.isProximityNegativeAcquired());
-        verifyZeroInteractions(mDisplayPowerCallbacks);
+        verifyNoMoreInteractions(mDisplayPowerCallbacks);
     }
 
     @Test
@@ -265,7 +265,7 @@
 
         // Validate one suspend blocker was released
         assertFalse(mWakelockController.isOnStateChangedPending());
-        verifyZeroInteractions(mDisplayPowerCallbacks);
+        verifyNoMoreInteractions(mDisplayPowerCallbacks);
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 49de801..bf05439 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -422,7 +421,7 @@
         mDisplayBrightnessController.setAutomaticBrightnessController(
                 automaticBrightnessController);
         assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
-        verifyZeroInteractions(automaticBrightnessController);
+        verifyNoMoreInteractions(automaticBrightnessController);
         verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
         verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index d79d884..f0e61ec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -109,7 +109,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -3724,8 +3724,8 @@
         setDeviceConfigInt(KEY_TEMPORARY_QUOTA_BUMP, 0);
 
         mAppStandbyListener.triggerTemporaryQuotaBump(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
-        verifyZeroInteractions(mPackageManagerInternal);
-        verifyZeroInteractions(mService.mHandler);
+        verifyNoMoreInteractions(mPackageManagerInternal);
+        verifyNoMoreInteractions(mService.mHandler);
     }
 
     private void testTemporaryQuota_bumpedAfterDeferral(int standbyBucket) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 7dab1c8..859d2d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -30,7 +30,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -244,7 +244,7 @@
         addAlarmsToStore(simpleAlarm, alarmClock);
 
         mAlarmStore.remove(simpleAlarm::equals);
-        verifyZeroInteractions(onRemoved);
+        verifyNoMoreInteractions(onRemoved);
 
         mAlarmStore.remove(alarmClock::equals);
         verify(onRemoved).run();
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 35ab2d2..acc06d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -74,7 +74,6 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -584,7 +583,7 @@
             if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
                 verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
             } else {
-                verifyZeroInteractions(app.getThread());
+                verifyNoMoreInteractions(app.getThread());
             }
             Mockito.reset(app.getThread());
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index e678acc..987b9c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -21,6 +21,7 @@
 import static com.android.server.am.ActivityManagerService.Injector;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -625,6 +626,60 @@
         assertTrue(startInfo.equals(startInfoFromParcel));
     }
 
+    /** Test that new timestamps are added to the correct record (the most recently created one). */
+    @Test
+    public void testTimestampAddedToCorrectRecord() throws Exception {
+        // Use a different start timestamp for each record so we can identify which was added to.
+        final long startTimeRecord1 = 123L;
+        final long startTimeRecord2 = 456L;
+
+        final long forkTime = 789L;
+
+        // Create a process record to use with all starts.
+        ProcessRecord app = makeProcessRecord(
+                APP_1_PID_1,                     // pid
+                APP_1_UID,                       // uid
+                APP_1_UID,                       // packageUid
+                null,                            // definingUid
+                APP_1_PROCESS_NAME,              // processName
+                APP_1_PACKAGE_NAME);             // packageName
+
+        // Trigger a start info record.
+        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app,
+                buildIntent(COMPONENT), false /* isAlarm */);
+
+        // Wait at least 1 ms for monotonic time to increase.
+        sleep(1);
+
+        // Verify the record was added successfully.
+        ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+        assertEquals(1, list.size());
+        assertEquals(startTimeRecord1, list.get(0).getStartupTimestamps().get(0).longValue());
+
+        // Now trigger another start info record.
+        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app,
+                buildIntent(COMPONENT), false /* isAlarm */);
+
+        // Add a timestamp to the most recent record.
+        mAppStartInfoTracker.addTimestampToStart(
+                app, forkTime, ApplicationStartInfo.START_TIMESTAMP_FORK);
+
+        // Verify the record was added successfully.
+        list.clear();
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+        assertEquals(2, list.size());
+        assertEquals(startTimeRecord2, list.get(0).getStartupTimestamps().get(0).longValue());
+        assertEquals(startTimeRecord1, list.get(1).getStartupTimestamps().get(0).longValue());
+
+        // Verify that the new timestamp is set correctly on the 2nd record that was added and not
+        // on the first.
+        assertEquals(forkTime, list.get(0).getStartupTimestamps()
+                .get(ApplicationStartInfo.START_TIMESTAMP_FORK).longValue());
+        assertFalse(list.get(1).getStartupTimestamps().containsKey(
+                ApplicationStartInfo.START_TIMESTAMP_FORK));
+    }
+
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
         try {
             Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
index ae0452a..b7087c7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.Presubmit;
@@ -105,7 +105,7 @@
         } catch (EOFException expected) {
         }
 
-        verifyZeroInteractions(mOutputStreamMock);
+        verifyNoMoreInteractions(mOutputStreamMock);
         assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
     }
 
@@ -126,7 +126,7 @@
         } catch (EOFException expected) {
         }
 
-        verifyZeroInteractions(mOutputStreamMock);
+        verifyNoMoreInteractions(mOutputStreamMock);
         assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
     }
 
@@ -141,7 +141,7 @@
 
         FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor, mOutputStreamMock);
 
-        verifyZeroInteractions(mOutputStreamMock);
+        verifyNoMoreInteractions(mOutputStreamMock);
         assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index bf7e3a0..346d5f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -32,7 +32,6 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
 import android.app.backup.IBackupManagerMonitor;
@@ -239,7 +238,7 @@
                 mMockPackageManagerInternal, mUserId, mContext);
 
         assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
-        verifyZeroInteractions(mBackupManagerMonitorMock);
+        verifyNoMoreInteractions(mBackupManagerMonitorMock);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 3e87943..2d84887 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1029,6 +1029,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1127,6 +1128,54 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Exempted
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+        expectedStats.expirationTimeElapsed = now + 34 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 5;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 1;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            EXEMPTED_INDEX));
+        }
+
+        // Active
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        // There is only one session in the past active bucket window, the empty time for this
+        // window is the bucket window size - duration of the session.
+        expectedStats.expirationTimeElapsed = now + 54 * MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
      */
@@ -1195,6 +1244,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
         // Set time to 3 minutes after boot.
         advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
@@ -1206,10 +1256,10 @@
         ExecutionStats expectedStats = new ExecutionStats();
 
         // Exempted
-        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
         expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
-        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
-        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
         expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 2;
@@ -1268,6 +1318,49 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning() {
+        // Set time to 3 minutes after boot.
+        advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+        advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Exempted
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+        expectedStats.expirationTimeElapsed = 30 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 2;
+        expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 2;
+        expectedStats.sessionCountInWindow = 1;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            EXEMPTED_INDEX));
+        }
+
+        // Active
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        expectedStats.expirationTimeElapsed = 50 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
      */
@@ -1425,6 +1518,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
         for (int i = 0; i < 20; ++i) {
             mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1581,6 +1675,165 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning() {
+        for (int i = 0; i < 20; ++i) {
+            mQuotaController.saveTimingSession(0, "com.android.test",
+                    createTimingSession(
+                            JobSchedulerService.sElapsedRealtimeClock.millis(),
+                            5 * MINUTE_IN_MILLIS, 5), false);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            for (int j = 0; j < 5; ++j) {
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(),
+                                MINUTE_IN_MILLIS, 2), false);
+                advanceElapsedClock(MINUTE_IN_MILLIS);
+                advanceElapsedClock(54 * SECOND_IN_MILLIS);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+                advanceElapsedClock(500);
+                advanceElapsedClock(400);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+                advanceElapsedClock(100);
+                advanceElapsedClock(5 * SECOND_IN_MILLIS);
+            }
+            advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(16, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(64, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(192, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(320, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(11, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+            assertEquals(44, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+            assertEquals(132, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+            assertEquals(220, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(11, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(44, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(132, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(220, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                5 * SECOND_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(7, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+            assertEquals(28, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+            assertEquals(84, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+            assertEquals(140, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                MINUTE_IN_MILLIS);
+
+        // Only two TimingSessions there for every hour.
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(2, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(8, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(24, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(40, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                5 * MINUTE_IN_MILLIS);
+
+        // Only one TimingSessions there for every hour
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(1, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                15 * MINUTE_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(1, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+        // between an hour and 15 minutes.
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(1, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
      */
@@ -2231,32 +2484,6 @@
         }
     }
 
-    @Test
-    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
-    public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
-                false);
-
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
-        // window size = allowed time, so jobs can essentially run non-stop until they reach the
-        // max execution time.
-        setStandbyBucket(EXEMPTED_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(10 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window.
@@ -2327,6 +2554,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Close to RARE boundary.
@@ -2390,7 +2618,30 @@
 
     @Test
     @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Close to ACTIVE boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
+
+        // ACTIVE window != allowed time.
+        setStandbyBucket(ACTIVE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(17 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(20 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
             Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER})
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetTimeUntilQuotaConsumedLocked_Installer() {
         PackageInfo pi = new PackageInfo();
         pi.packageName = SOURCE_PACKAGE;
@@ -2412,7 +2663,7 @@
         // Far away from FREQUENT boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS -  HOUR_IN_MILLIS),
+                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
                         2 * MINUTE_IN_MILLIS, 5), false);
         // Overlap WORKING_SET boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2422,12 +2673,12 @@
         // Close to ACTIVE boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
+                        now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
                         2 * MINUTE_IN_MILLIS, 5), false);
         // Close to EXEMPTED boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS -  MINUTE_IN_MILLIS),
+                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
                         2 * MINUTE_IN_MILLIS, 5), false);
 
         // No additional quota for the system installer when the app is in RARE, FREQUENT,
@@ -2486,6 +2737,42 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetTimeUntilQuotaConsumedLocked_Installer_Tuning() {
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = SOURCE_PACKAGE;
+        pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES};
+        pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED};
+        pi.applicationInfo = new ApplicationInfo();
+        pi.applicationInfo.uid = mSourceUid;
+        doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
+        doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
+                eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid));
+        mQuotaController.onSystemServicesReady();
+
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Close to EXEMPTED boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+                        2 * MINUTE_IN_MILLIS, 5), false);
+
+        // Additional quota for the system installer when the app is in EXEMPTED bucket.
+        // EXEMPTED window == allowed time.
+        setStandbyBucket(EXEMPTED_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(38 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 2 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
      */
@@ -2637,33 +2924,6 @@
         }
     }
 
-    @Test
-    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
-    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (24 * HOUR_IN_MILLIS),
-                        mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
-                false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
-                false);
-
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
-        // window size != allowed time.
-        setStandbyBucket(EXEMPTED_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(0,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window and the session is rolling out of the window.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 3d0c637..29af7d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.AssertJUnit.assertEquals;
 
@@ -128,7 +128,7 @@
                 AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
         mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
-        verifyZeroInteractions(mNotificationManager);
+        verifyNoMoreInteractions(mNotificationManager);
     }
 
     @Test
@@ -143,7 +143,7 @@
                 Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
         mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
-        verifyZeroInteractions(mNotificationManager);
+        verifyNoMoreInteractions(mNotificationManager);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index dc04b6a..bf3fe8c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -176,9 +176,9 @@
         prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
                 true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
 
-        verifyZeroInteractions(mLightsManager);
-        verifyZeroInteractions(mAppOpsManager);
-        verifyZeroInteractions(mSensorManager);
+        verifyNoMoreInteractions(mLightsManager);
+        verifyNoMoreInteractions(mAppOpsManager);
+        verifyNoMoreInteractions(mSensorManager);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 241ffdc..4f74667 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -33,10 +33,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
@@ -48,7 +46,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -95,7 +92,6 @@
 
     @Mock
     private Resources mResources;
-    private WallpaperCropper mWallpaperCropper;
 
     private static final Point PORTRAIT_ONE = new Point(500, 800);
     private static final Point PORTRAIT_TWO = new Point(400, 1000);
@@ -171,7 +167,6 @@
             return getWallpaperTestDir(userId);
         }).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
 
-        mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
     }
 
     private File getWallpaperTestDir(int userId) {
@@ -738,13 +733,13 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(DEFAULT_DISPLAY));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(100, 100));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY,
-                        wallpaperData)).isTrue();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                DEFAULT_DISPLAY, wallpaperData)).isTrue();
     }
 
     // Test isWallpaperCompatibleForDisplay always return true for the stock wallpaper.
@@ -755,49 +750,67 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ true,
                 new Point(100, 100));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isTrue();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
     }
 
     // Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
     // aspect ratio meets the hard-coded aspect ratio.
     @Test
-    public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+    public void isWallpaperCompatibleForDisplay_wallpaperSizeLargerThanDisplayAndMeetAspectRatio_returnTrue()
             throws Exception {
         final int displayId = 2;
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(4000, 3000));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isTrue();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
     }
 
-    // Test isWallpaperCompatibleForDisplay wallpaper is not suitable for the display and wallpaper
-    // aspect ratio meets the hard-coded aspect ratio.
+    // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+    // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
     @Test
-    public void isWallpaperCompatibleForDisplay_wallpaperSizeNotSuitableForDisplayAndMeetAspectRatio_returnFalse()
+    public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButBeyondThresholdAndMeetAspectRatio_returnTrue()
             throws Exception {
         final int displayId = 2;
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
-                new Point(1000, 500));
+                new Point(2000, 900));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isFalse();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
+    }
+
+    // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+    // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
+    @Test
+    public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButAboveThresholdAndMeetAspectRatio_returnFalse()
+            throws Exception {
+        final int displayId = 2;
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 2560;
+        displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
+        doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+        WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+                new Point(2000, 800));
+
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isFalse();
     }
 
     // Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
@@ -809,13 +822,13 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(2000, 4000));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isFalse();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isFalse();
     }
 
     // Test isWallpaperCompatibleForDisplay, portrait display, wallpaper is suitable for the display
@@ -827,24 +840,13 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 1044;
         displayInfo.logicalHeight = 2560;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(2000, 4000));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isTrue();
-    }
-
-    private void mockDisplay(int displayId, Point displayResolution) {
-        final Display mockDisplay = mock(Display.class);
-        when(mockDisplay.getDisplayInfo(any(DisplayInfo.class))).thenAnswer(invocation -> {
-            DisplayInfo displayInfo = invocation.getArgument(0);
-            displayInfo.displayId = displayId;
-            displayInfo.logicalWidth = displayResolution.x;
-            displayInfo.logicalHeight = displayResolution.y;
-            return true;
-        });
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
     }
 
     private WallpaperData createWallpaperData(boolean isStockWallpaper, Point wallpaperSize)
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 4e56422..94ce723 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -39,9 +39,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -53,8 +53,8 @@
 import android.os.BatteryStatsInternal;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IWakeLockCallback;
 import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -205,7 +205,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verifyZeroInteractions(mVibrator);
+        verifyNoMoreInteractions(mVibrator);
     }
 
     @Test
@@ -238,7 +238,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verifyZeroInteractions(mVibrator);
+        verifyNoMoreInteractions(mVibrator);
     }
 
     @Test
@@ -725,10 +725,11 @@
 
         final int uid = 1234;
         final int pid = 5678;
+
         mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
                 exceptingCallback);
-        verifyZeroInteractions(mWakeLockLog);
+        verifyNoMoreInteractions(mWakeLockLog);
         mTestLooper.dispatchAll();
         verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
         clearInvocations(mBatteryStats);
@@ -790,6 +791,55 @@
     }
 
     @Test
+    public void test_wakeLockLogUsesWorkSource() {
+        createNotifier();
+        clearInvocations(mWakeLockLog);
+        IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+            @Override public void onStateChanged(boolean enabled) throws RemoteException {
+                throw new RemoteException("Just testing");
+            }
+        };
+
+        final int uid = 1234;
+        final int pid = 5678;
+        WorkSource worksource = new WorkSource(1212);
+        WorkSource worksource2 = new WorkSource(3131);
+
+        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+                exceptingCallback);
+        verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+                PowerManager.PARTIAL_WAKE_LOCK, -1);
+
+        // Release the wakelock
+        mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+                "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+                exceptingCallback);
+        verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, -1);
+
+        // clear the handler
+        mTestLooper.dispatchAll();
+
+        // Now test with improveWakelockLatency flag true
+        clearInvocations(mWakeLockLog);
+        when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+
+        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+                exceptingCallback);
+        mTestLooper.dispatchAll();
+        verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+                PowerManager.PARTIAL_WAKE_LOCK, 1);
+
+        // Release the wakelock
+        mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+                "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+                exceptingCallback);
+        mTestLooper.dispatchAll();
+        verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, 1);
+    }
+
+    @Test
     public void
             test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
         when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
@@ -845,7 +895,7 @@
                 exceptingCallback);
 
         // No interaction because we expect that to happen in async
-        verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+        verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
 
         // Progressing the looper, and validating all the interactions
         mTestLooper.dispatchAll();
@@ -944,15 +994,23 @@
         assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
                 PowerManager.PARTIAL_WAKE_LOCK);
         assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+                        PowerManager.DOZE_WAKE_LOCK), -1);
+    }
+
+    @Test
+    public void getWakelockMonitorTypeForLogging_evaluateProximityLevel() {
+        // How proximity wakelock is evaluated depends on boolean configuration. Test both.
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+                .thenReturn(false);
+        createNotifier();
+        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                         PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
                 PowerManager.PARTIAL_WAKE_LOCK);
-        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
-                        PowerManager.DOZE_WAKE_LOCK), -1);
 
         when(mResourcesSpy.getBoolean(
                 com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
                 .thenReturn(true);
-
         createNotifier();
         assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                         PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
@@ -1239,7 +1297,7 @@
                     }
 
                     @Override
-                    public WakeLockLog getWakeLockLog(Context context) {
+                    public @NonNull WakeLockLog getWakeLockLog(Context context) {
                         return mWakeLockLog;
                     }
 
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index c1d7c7b..534337e 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -27,6 +27,8 @@
 import android.os.PowerManager;
 import android.os.Process;
 
+import com.android.server.power.WakeLockLog.TagData;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -62,8 +64,9 @@
     @Test
     public void testAddTwoItems_withNoEventTimeSupplied() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101,
@@ -93,8 +96,9 @@
     @Test
     public void testAddTwoItems() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -117,8 +121,9 @@
     @Test
     public void testAddTwoItemsWithTimeReset() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -136,9 +141,10 @@
 
     @Test
     public void testAddTwoItemsWithTagOverwrite() {
-        final int tagDatabaseSize = 2;
+        final int tagDatabaseSize = 1;
+        final int tagStartingSize = 1;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -157,8 +163,9 @@
     @Test
     public void testAddFourItemsWithRingBufferOverflow() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         // Wake lock 1 acquired - log size = 3
@@ -206,8 +213,9 @@
     @Test
     public void testAddItemWithBadTag() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         // Bad tag means it wont get written
@@ -224,8 +232,9 @@
     @Test
     public void testAddItemWithReducedTagName() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
@@ -242,9 +251,10 @@
 
     @Test
     public void testAddAcquireAndReleaseWithRepeatTagName() {
-        final int tagDatabaseSize = 6;
+        final int tagDatabaseSize = 5;
+        final int tagStartingSize = 5;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -263,8 +273,9 @@
     @Test
     public void testAddAcquireAndReleaseWithTimeTravel() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
@@ -283,8 +294,9 @@
     @Test
     public void testAddSystemWakelock() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -302,8 +314,9 @@
     @Test
     public void testAddItemWithNoPackageName() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
@@ -322,8 +335,9 @@
     @Test
     public void testAddItemWithMultiplePackageNames() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(mPackageManager.getPackagesForUid(101)).thenReturn(
@@ -344,8 +358,9 @@
     @Test
     public void testAddItemsWithRepeatOwnerUid_UsesCache() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -375,8 +390,9 @@
     @Test
     public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -420,6 +436,34 @@
         verify(mPackageManager, times(1)).getPackagesForUid(101);
     }
 
+    @Test
+    public void testTagDatabaseGrowsBeyondStartingSize() {
+        final int tagDatabaseSize = 3;
+        final int tagStartingSize = 1;
+        final int logSize = 10;
+        // start with size = 1 and max size
+        TestInjector injector = new TestInjector(tagDatabaseSize, tagStartingSize, logSize);
+        WakeLockLog.TagDatabase td = new WakeLockLog.TagDatabase(injector);
+
+        // Add one
+        TagData data1 = td.findOrCreateTag("Tagname1", 1001, /* shouldCreate= */ true);
+        assertEquals(0, td.getTagIndex(data1));
+
+        // Check that it grows by adding 1 more
+        TagData data2 = td.findOrCreateTag("Tagname2", 1001, /* shouldCreate= */ true);
+        assertEquals(1, td.getTagIndex(data2));
+
+        // Lets add the last one to fill up the DB to maxSize
+        TagData data3 = td.findOrCreateTag("Tagname3", 1001, /* shouldCreate= */ true);
+        assertEquals(2, td.getTagIndex(data3));
+
+        // Adding a fourth one should replace the oldest one (Tagname1)
+        TagData data4 = td.findOrCreateTag("Tagname4", 1001, /* shouldCreate= */ true);
+        assertEquals(0, td.getTagIndex(data4));
+        assertEquals(tagDatabaseSize, td.getTagIndex(data1));
+
+    }
+
     private String dumpLog(WakeLockLog log, boolean includeTagDb) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
@@ -429,10 +473,12 @@
 
     public static class TestInjector extends WakeLockLog.Injector {
         private final int mTagDatabaseSize;
+        private final int mTagStartingSize;
         private final int mLogSize;
 
-        public TestInjector(int tagDatabaseSize, int logSize) {
+        public TestInjector(int tagDatabaseSize, int tagStartingSize, int logSize) {
             mTagDatabaseSize = tagDatabaseSize;
+            mTagStartingSize = tagStartingSize;
             mLogSize = logSize;
         }
 
@@ -442,6 +488,11 @@
         }
 
         @Override
+        public int getTagDatabaseStartingSize() {
+            return mTagStartingSize;
+        }
+
+        @Override
         public int getLogSize() {
             return mLogSize;
         }
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index 879aa48..2fd316e 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -409,7 +409,7 @@
         final String TAG = "startTestService";
         final CountDownLatch latch = new CountDownLatch(1);
         LocalIntrusionDetectionEventTransport transport =
-                new LocalIntrusionDetectionEventTransport();
+                new LocalIntrusionDetectionEventTransport(mContext);
 
         ServiceConnection serviceConnection = new ServiceConnection() {
             // Called when connection with the service is established.
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
index f0012da..b0b7815 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
@@ -18,8 +18,13 @@
 
 package com.android.coretests.apps.testapp;
 
+import android.app.admin.SecurityLog;
+import android.app.admin.SecurityLog.SecurityEvent;
+import android.content.Context;
+import android.content.Intent;
 import android.security.intrusiondetection.IntrusionDetectionEvent;
 import android.security.intrusiondetection.IntrusionDetectionEventTransport;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,6 +41,44 @@
 public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEventTransport {
     private List<IntrusionDetectionEvent> mEvents = new ArrayList<>();
 
+    private static final String ACTION_SECURITY_EVENT_RECEIVED =
+            "com.android.coretests.apps.testapp.ACTION_SECURITY_EVENT_RECEIVED";
+    private static final String TAG = "LocalIntrusionDetectionEventTransport";
+    private static final String TEST_SECURITY_EVENT_TAG = "test_security_event_tag";
+    private static Context sContext;
+
+    public LocalIntrusionDetectionEventTransport(Context context) {
+        sContext = context;
+    }
+
+    // Broadcast an intent to the CTS test service to indicate that the security
+    // event was received.
+    private static void broadcastSecurityEventReceived() {
+        try {
+            Intent intent = new Intent(ACTION_SECURITY_EVENT_RECEIVED);
+            sContext.sendBroadcast(intent);
+            Log.i(TAG, "LIZ_TESTING: sent broadcast");
+        } catch (Exception e) {
+            Log.e(TAG, "Exception sending broadcast", e);
+        }
+    }
+
+    private static void checkIfSecurityEventReceivedFromCts(List<IntrusionDetectionEvent> events) {
+        // Loop through the events and check if any of them are the security event
+        // that uses the TEST_SECURITY_EVENT_TAG tag, which is set by the CTS test.
+        for (IntrusionDetectionEvent event : events) {
+            if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) {
+                SecurityEvent securityEvent = event.getSecurityEvent();
+                Object[] eventData = (Object[]) securityEvent.getData();
+                if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED
+                        && eventData[1].equals(TEST_SECURITY_EVENT_TAG)) {
+                    broadcastSecurityEventReceived();
+                    return;
+                }
+            }
+        }
+    }
+
     @Override
     public boolean initialize() {
         return true;
@@ -43,6 +86,11 @@
 
     @Override
     public boolean addData(List<IntrusionDetectionEvent> events) {
+        // Our CTS tests will generate a security event. In order to
+        // verify the event is received with the appropriate data, we will
+        // check the events locally and set a property value that can be
+        // read by the test.
+        checkIfSecurityEventReceivedFromCts(events);
         mEvents.addAll(events);
         return true;
     }
@@ -55,4 +103,4 @@
     public List<IntrusionDetectionEvent> getEvents() {
         return mEvents;
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
index e4bf987..9183a75 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
@@ -17,19 +17,20 @@
 package com.android.coretests.apps.testapp;
 
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
-import android.os.Process;
-
-import com.android.internal.infra.AndroidFuture;
-
 
 public class TestLoggingService extends Service {
     private static final String TAG = "TestLoggingService";
     private LocalIntrusionDetectionEventTransport mLocalIntrusionDetectionEventTransport;
 
-    public TestLoggingService() {
-        mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport();
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Context context = getApplicationContext();
+        mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport(context);
     }
 
     // Binder given to clients.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 75df9a8..d254e96 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -35,7 +35,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.Manifest.permission;
@@ -243,7 +242,7 @@
     @Test
     public void testRequestScores_providerNotConnected() throws Exception {
         assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
-        verifyZeroInteractions(mRecommendationProvider);
+        verifyNoMoreInteractions(mRecommendationProvider);
     }
 
     @Test
@@ -604,7 +603,7 @@
         consumer.accept(mNetworkScoreCache, null /*cookie*/);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -618,7 +617,7 @@
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -632,7 +631,7 @@
         consumer.accept(mNetworkScoreCache, -1 /*cookie*/);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -646,7 +645,7 @@
         consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -658,7 +657,7 @@
 
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
 
-        verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -676,7 +675,7 @@
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
 
         verify(mNetworkScoreCache).updateScores(filteredList);
-        verifyZeroInteractions(mScanResultsFilter);
+        verifyNoMoreInteractions(mScanResultsFilter);
     }
 
     @Test
@@ -694,7 +693,7 @@
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
 
         verify(mNetworkScoreCache).updateScores(filteredList);
-        verifyZeroInteractions(mCurrentNetworkFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 42b84bd..c7c8c58 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -64,7 +64,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityService;
@@ -838,7 +837,7 @@
         // ...without secure layers included
         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
         // No error sent to callback
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
     }
 
     @Test
@@ -856,7 +855,7 @@
         // ...with secure layers included
         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
         // No error sent to callback
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
     }
 
     @Test
@@ -889,7 +888,7 @@
         // ...with secure layers included
         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
         // No error sent to callback
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
     }
 
     private void takeScreenshotOfWindow(int windowFlags) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 8253595..2ccd336 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -50,6 +50,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
@@ -66,6 +67,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
@@ -77,10 +79,12 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.input.KeyGestureEvent;
 import android.net.Uri;
@@ -114,6 +118,7 @@
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.TestUtils;
 import com.android.internal.R;
@@ -136,6 +141,8 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import com.google.common.truth.Correspondence;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -188,6 +195,8 @@
             DESCRIPTION,
             TEST_PENDING_INTENT);
 
+    private static final int FAKE_SYSTEMUI_UID = 1000;
+
     private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
     private static final String TARGET_MAGNIFICATION = MAGNIFICATION_CONTROLLER_NAME;
     private static final ComponentName TARGET_ALWAYS_ON_A11Y_SERVICE =
@@ -207,11 +216,12 @@
     @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
     @Mock private PackageManager mMockPackageManager;
+    @Mock
+    private PackageManagerInternal mMockPackageManagerInternal;
     @Mock private WindowManagerInternal mMockWindowManagerService;
     @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
     @Mock private SystemActionPerformer mMockSystemActionPerformer;
     @Mock private AccessibilityWindowManager mMockA11yWindowManager;
-    @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
     @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
     @Mock private UserManagerInternal mMockUserManagerInternal;
     @Mock private IBinder mMockBinder;
@@ -234,6 +244,7 @@
     private TestableLooper mTestableLooper;
     private Handler mHandler;
     private FakePermissionEnforcer mFakePermissionEnforcer;
+    private TestDisplayManagerWrapper mTestDisplayManagerWrapper;
 
     @Before
     public void setUp() throws Exception {
@@ -246,6 +257,7 @@
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(PermissionEnforcer.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(
                 WindowManagerInternal.class, mMockWindowManagerService);
         LocalServices.addService(
@@ -256,6 +268,12 @@
         mInputFilter = mock(FakeInputFilter.class);
         mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
 
+        when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+                new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
+        when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
+                anyInt())).thenReturn(FAKE_SYSTEMUI_UID);
+        LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+
         when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
                 mMockMagnificationConnectionManager);
         when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
@@ -273,15 +291,9 @@
                 eq(UserHandle.USER_CURRENT)))
                 .thenReturn(mTestableContext.getUserId());
 
-        final ArrayList<Display> displays = new ArrayList<>();
-        final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(),
-                Display.DEFAULT_DISPLAY, new DisplayInfo(),
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY,
-                new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        displays.add(defaultDisplay);
-        displays.add(testDisplay);
-        when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays);
+        mTestDisplayManagerWrapper = new TestDisplayManagerWrapper(mTestableContext);
+        mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(Display.TYPE_INTERNAL,
+                Display.TYPE_EXTERNAL);
 
         mA11yms = new AccessibilityManagerService(
                 mTestableContext,
@@ -290,7 +302,7 @@
                 mMockSecurityPolicy,
                 mMockSystemActionPerformer,
                 mMockA11yWindowManager,
-                mMockA11yDisplayListener,
+                mTestDisplayManagerWrapper,
                 mMockMagnificationController,
                 mInputFilter,
                 mProxyManager,
@@ -2309,6 +2321,73 @@
                 mA11yms.getCurrentUserIdLocked())).isEmpty();
     }
 
+    @Test
+    public void displayListReturnsDisplays() {
+        mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(
+                        Display.TYPE_INTERNAL,
+                        Display.TYPE_EXTERNAL,
+                        Display.TYPE_WIFI,
+                        Display.TYPE_OVERLAY,
+                        Display.TYPE_VIRTUAL
+        );
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // In #setUp() we already have TYPE_INTERNAL and TYPE_EXTERNAL. Call the rest.
+            for (int i = 2; i < mTestDisplayManagerWrapper.mDisplays.size(); i++) {
+                mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(
+                        mTestDisplayManagerWrapper.mDisplays.get(i).getDisplayId());
+            }
+        });
+
+        List<Display> displays = mA11yms.getValidDisplayList();
+        assertThat(displays).hasSize(5);
+        assertThat(displays)
+                .comparingElementsUsing(
+                        Correspondence.transforming(Display::getType, "has a type of"))
+                .containsExactly(Display.TYPE_INTERNAL,
+                        Display.TYPE_EXTERNAL,
+                        Display.TYPE_WIFI,
+                        Display.TYPE_OVERLAY,
+                        Display.TYPE_VIRTUAL);
+    }
+
+    @Test
+    public void displayListReturnsDisplays_excludesVirtualPrivate() {
+        // Add a private virtual display whose uid is different from systemui.
+        final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+                Display.TYPE_EXTERNAL);
+        displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID + 100));
+        mTestDisplayManagerWrapper.mDisplays = displays;
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+        });
+
+        List<Display> validDisplays = mA11yms.getValidDisplayList();
+        assertThat(validDisplays).hasSize(2);
+        assertThat(validDisplays)
+                .comparingElementsUsing(
+                        Correspondence.transforming(Display::getType, "has a type of"))
+                .doesNotContain(Display.TYPE_VIRTUAL);
+    }
+
+    @Test
+    public void displayListReturnsDisplays_includesVirtualSystemUIPrivate() {
+        // Add a private virtual display whose uid is systemui.
+        final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+                Display.TYPE_EXTERNAL);
+        displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID));
+        mTestDisplayManagerWrapper.mDisplays = displays;
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+        });
+
+        List<Display> validDisplays = mA11yms.getValidDisplayList();
+        assertThat(validDisplays).hasSize(3);
+        assertThat(validDisplays)
+                .comparingElementsUsing(
+                        Correspondence.transforming(Display::getType, "has a type of"))
+                .contains(Display.TYPE_VIRTUAL);
+    }
+
     private Set<String> readStringsFromSetting(String setting) {
         final Set<String> result = new ArraySet<>();
         mA11yms.readColonDelimitedSettingToSet(
@@ -2422,6 +2501,27 @@
                 });
     }
 
+    private static List<Display> createFakeDisplayList(int... types) {
+        final ArrayList<Display> displays = new ArrayList<>();
+        for (int i = 0; i < types.length; i++) {
+            final DisplayInfo info = new DisplayInfo();
+            info.type = types[i];
+            final Display display = new Display(DisplayManagerGlobal.getInstance(),
+                    i, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+            displays.add(display);
+        }
+        return displays;
+    }
+
+    private static Display createFakeVirtualPrivateDisplay(int displayId, int uid) {
+        final DisplayInfo info = new DisplayInfo();
+        info.type = Display.TYPE_VIRTUAL;
+        info.flags |= Display.FLAG_PRIVATE;
+        info.ownerUid = uid;
+        return new Display(DisplayManagerGlobal.getInstance(),
+                displayId, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+    }
+
     public static class FakeInputFilter extends AccessibilityInputFilter {
         FakeInputFilter(Context context,
                 AccessibilityManagerService service) {
@@ -2506,4 +2606,35 @@
         Set<String> setting = readStringsFromSetting(ShortcutUtils.convertToKey(shortcutType));
         assertThat(setting).containsExactlyElementsIn(value);
     }
+
+    private static class TestDisplayManagerWrapper extends
+            AccessibilityDisplayListener.DisplayManagerWrapper {
+        List<Display> mDisplays;
+        DisplayManager.DisplayListener mRegisteredListener;
+
+        TestDisplayManagerWrapper(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Display[] getDisplays() {
+            return mDisplays.toArray(new Display[0]);
+        }
+
+        @Override
+        public Display getDisplay(int displayId) {
+            for (final Display display : mDisplays) {
+                if (display.getDisplayId() == displayId) {
+                    return display;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+                @Nullable Handler handler) {
+            mRegisteredListener = listener;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 96ae102e5..d0dc2cb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.FingerprintGestureController;
@@ -86,7 +86,7 @@
                 mMockFingerprintGestureCallback);
         mFingerprintGestureController.onGestureDetectionActiveChanged(true);
         mFingerprintGestureController.onGestureDetectionActiveChanged(false);
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
     }
 
     @Test
@@ -118,7 +118,7 @@
         mFingerprintGestureController.onGestureDetectionActiveChanged(true);
         mFingerprintGestureController.onGestureDetectionActiveChanged(false);
         assertFalse(messageCapturingHandler.hasMessages());
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
 
         messageCapturingHandler.removeAllMessages();
     }
@@ -135,7 +135,7 @@
         mFingerprintGestureController.unregisterFingerprintGestureCallback(
                 mMockFingerprintGestureCallback);
         mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
     }
 
     @Test
@@ -159,7 +159,7 @@
                 mMockFingerprintGestureCallback);
         mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
         assertFalse(messageCapturingHandler.hasMessages());
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
 
         messageCapturingHandler.removeAllMessages();
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index 63c572a..3565244 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -36,6 +36,8 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioDevicePort;
 import android.media.AudioManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 
@@ -67,6 +69,8 @@
 public class HearingDevicePhoneCallNotificationControllerTest {
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
 
@@ -118,6 +122,7 @@
                 AudioManager.DEVICE_OUT_BLE_HEADSET);
         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
                 new AudioDeviceInfo[]{hapDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
         when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
 
         mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -132,6 +137,7 @@
                 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
                 new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
         when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(a2dpDeviceInfo));
 
         mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -146,6 +152,7 @@
                 AudioManager.DEVICE_OUT_BLE_HEADSET);
         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
                 new AudioDeviceInfo[]{hapDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
         when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
 
         mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -155,6 +162,51 @@
                 eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH));
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+    public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() {
+        final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+                ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+        AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
+
+        verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+                listenerCaptor.capture());
+        verify(mAudioManager).removeOnCommunicationDeviceChangedListener(
+                eq(listenerCaptor.getValue()));
+    }
+
+
+    @Test
+    @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+    public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() {
+        final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+                ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+        AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLE_HEADSET);
+        AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+        verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+                listenerCaptor.capture());
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{hapDeviceInfo});
+        listenerCaptor.getValue().onCommunicationDeviceChanged(hapDeviceInfo);
+
+        verify(mNotificationManager).notify(
+                eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+    }
+
     private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
         AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
         doReturn(type).when(audioDevicePort).type();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index c3256ca..186f742 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
@@ -159,7 +159,7 @@
                 mFilter1SequenceCaptor.getValue());
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -189,7 +189,7 @@
                 mFilter2SequenceCaptor.getValue());
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -261,7 +261,7 @@
         mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
                 mFilter2SequenceCaptor.getValue());
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -278,7 +278,7 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
@@ -293,7 +293,7 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
@@ -327,7 +327,7 @@
                 mFilter1SequenceCaptor.getValue());
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
@@ -344,7 +344,7 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 9b8e619..367f2d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -37,7 +37,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
 import android.accessibilityservice.GestureDescription.GestureStep;
@@ -223,7 +222,7 @@
         verifyNoMoreInteractions(next);
         reset(next);
 
-        verifyZeroInteractions(mServiceInterface);
+        verifyNoMoreInteractions(mServiceInterface);
 
         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
         verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index a048238..99c922c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -75,12 +75,13 @@
 
     private static class MotionEventCaptor extends BaseEventStreamTransformation {
         public MotionEvent downEvent;
-
+        public int eventCount = 0;
         @Override
         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
                     downEvent = event;
+                    eventCount++;
                     break;
             }
         }
@@ -922,6 +923,41 @@
         mController.onKeyEvent(keyEvent, /* policyFlags= */ 0);
     }
 
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void sendClick_clickType_doubleclick_triggerClickTwice() {
+        MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+        mController.setNext(motionEventCaptor);
+
+        injectFakeMouseActionHoverMoveEvent();
+        // Set delay to zero so click is scheduled to run immediately.
+        mController.mClickScheduler.updateDelay(0);
+
+        // Set click type to double click.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK);
+        AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+        mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+        // Send hover move event.
+        MotionEvent hoverMove = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify left click sent.
+        assertThat(motionEventCaptor.downEvent).isNotNull();
+        assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+                MotionEvent.BUTTON_PRIMARY);
+        assertThat(motionEventCaptor.eventCount).isEqualTo(2);
+    }
+
     private MotionEvent getFakeMotionHoverMoveEvent() {
         return MotionEvent.obtain(
                 /* downTime= */ 0,
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3475c8f..20a95e9 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -34,7 +34,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
 import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
@@ -196,7 +195,7 @@
     @Test
     public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
         mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
-        verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 7a77033..f4e8717 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.hardware.biometrics.BiometricFaceConstants;
@@ -172,7 +172,7 @@
         client.onInteractionDetected();
         client.stopHalOperation();
 
-        verifyZeroInteractions(mVibrator);
+        verifyNoMoreInteractions(mVibrator);
     }
 
     private FaceDetectClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 67fc564..2e07cd8a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -22,18 +22,25 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.sensor.IVirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.content.AttributionSource;
 import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -49,6 +56,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -65,6 +73,9 @@
 
     private static final int VIRTUAL_SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER;
 
+    private static final float[] ADDITIONAL_INFO_VALUES_1 = new float[] {1.2f, 3.4f};
+    private static final float[] ADDITIONAL_INFO_VALUES_2 = new float[] {5.6f, 7.8f};
+
     @Mock
     private SensorManagerInternal mSensorManagerInternalMock;
     @Mock
@@ -155,6 +166,53 @@
     }
 
     @Test
+    public void sendSensorAdditionalInfo_invalidToken_throwsException() throws Exception {
+        SensorController sensorController = doCreateSensorSuccessfully();
+
+        final VirtualSensorAdditionalInfo info =
+                new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+                        .addValues(ADDITIONAL_INFO_VALUES_1)
+                        .addValues(ADDITIONAL_INFO_VALUES_2)
+                        .build();
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> sensorController.sendSensorAdditionalInfo(
+                        new Binder("invalidSensorToken"), info));
+    }
+
+    @Test
+    public void sendSensorAdditionalInfo_success() throws Exception {
+        SensorController sensorController = doCreateSensorSuccessfully();
+
+        clearInvocations(mSensorManagerInternalMock);
+        when(mSensorManagerInternalMock.sendSensorAdditionalInfo(
+                anyInt(), anyInt(), anyInt(), anyLong(), any()))
+                .thenReturn(true);
+        IBinder token = Iterables.getOnlyElement(sensorController.getSensorDescriptors().keySet());
+
+        final VirtualSensorAdditionalInfo info =
+                new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+                        .addValues(ADDITIONAL_INFO_VALUES_1)
+                        .addValues(ADDITIONAL_INFO_VALUES_2)
+                        .build();
+        sensorController.sendSensorAdditionalInfo(token, info);
+
+        InOrder inOrder = inOrder(mSensorManagerInternalMock);
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_BEGIN),
+                /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+                /*serial=*/ eq(0), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_1));
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+                /*serial=*/ eq(1), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_2));
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_END),
+                /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+    }
+
+    @Test
     public void close_unregistersSensors() throws Exception {
         SensorController sensorController = doCreateSensorSuccessfully();
 
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index b445226..4fa75b9 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -23,7 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -132,8 +132,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -147,7 +147,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager, never()).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -157,8 +157,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -172,7 +172,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager, never()).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -187,7 +187,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager, never()).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -203,7 +203,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -216,8 +216,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -230,8 +230,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -248,7 +248,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -265,7 +265,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -513,7 +513,7 @@
 
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
@@ -528,7 +528,7 @@
 
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1);
         assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
index 195ab68..9d37b99 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
@@ -98,10 +98,10 @@
     @Test
     public void constructor() {
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -110,10 +110,10 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -126,8 +126,8 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor, never()).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -142,8 +142,8 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor, never()).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -153,11 +153,11 @@
         mContentProtectionAllowlistManager.stop();
 
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
         verify(mMockPackageMonitor, never()).register(any(), any(), any());
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -169,11 +169,11 @@
         mContentProtectionAllowlistManager.stop();
 
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
         verify(mMockPackageMonitor, never()).register(any(), any(), any());
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -188,8 +188,8 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -205,8 +205,8 @@
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -223,8 +223,8 @@
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
         verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -232,10 +232,10 @@
         boolean actual = mContentProtectionAllowlistManager.isAllowed(FIRST_PACKAGE_NAME);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -248,9 +248,9 @@
         boolean actual = manager.isAllowed(SECOND_PACKAGE_NAME);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
@@ -263,9 +263,9 @@
         boolean actual = manager.isAllowed(FIRST_PACKAGE_NAME);
 
         assertThat(actual).isTrue();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
@@ -276,8 +276,8 @@
         manager.mPackageMonitor.onSomePackagesChanged();
 
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -291,7 +291,7 @@
 
         verify(mMockRemoteContentProtectionService)
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -309,7 +309,7 @@
         // Does not rethrow
         verify(mMockRemoteContentProtectionService)
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -321,8 +321,8 @@
         manager.mPackageMonitor.onSomePackagesChanged();
 
         verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -338,7 +338,7 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockRemoteContentProtectionService)
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -355,7 +355,7 @@
         verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
         verify(mMockRemoteContentProtectionService, times(2))
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index b012aaa..cd36a18 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.admin.DevicePolicyCache;
@@ -112,8 +112,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -125,8 +125,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -138,8 +138,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -152,7 +152,7 @@
 
         assertThat(actual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -166,7 +166,7 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -179,7 +179,7 @@
 
         assertThat(actual).isFalse();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -192,7 +192,7 @@
 
         assertThat(actual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -289,8 +289,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -302,8 +302,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -316,7 +316,7 @@
 
         assertThat(actual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -339,7 +339,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -362,7 +362,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -385,7 +385,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -408,7 +408,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     private void putGlobalSettings(String key, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
index 6a7e286..563a679 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
@@ -20,7 +20,7 @@
 
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -87,7 +87,7 @@
     @Test
     public void doesNotAutoConnect() {
         assertThat(mConnectCallCount).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionService);
+        verifyNoMoreInteractions(mMockContentProtectionService);
     }
 
     @Test
@@ -124,7 +124,7 @@
         mRemoteContentProtectionService.onServiceConnectionStatusChanged(
                 mMockContentProtectionService, /* isConnected= */ true);
 
-        verifyZeroInteractions(mMockContentProtectionService);
+        verifyNoMoreInteractions(mMockContentProtectionService);
         assertThat(mConnectCallCount).isEqualTo(0);
     }
 
@@ -133,7 +133,7 @@
         mRemoteContentProtectionService.onServiceConnectionStatusChanged(
                 mMockContentProtectionService, /* isConnected= */ false);
 
-        verifyZeroInteractions(mMockContentProtectionService);
+        verifyNoMoreInteractions(mMockContentProtectionService);
         assertThat(mConnectCallCount).isEqualTo(0);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 0f3f27a..9e98af3 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -207,7 +207,7 @@
                 ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
                 "unsupportedKey", providerPendingIntentResponse);
 
-        verifyZeroInteractions(mGetRequestSession);
+        verifyNoMoreInteractions(mGetRequestSession);
     }
 
     @Test
@@ -216,7 +216,7 @@
                 ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
                 ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY, null);
 
-        verifyZeroInteractions(mGetRequestSession);
+        verifyNoMoreInteractions(mGetRequestSession);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 01bcc25..c50c623 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -88,7 +88,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 import static org.testng.Assert.assertThrows;
@@ -5305,7 +5304,7 @@
         // both the user restriction and the policy were set by the PO.
         verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
                 MANAGED_PROFILE_USER_ID);
-        verifyZeroInteractions(getServices().recoverySystem);
+        verifyNoMoreInteractions(getServices().recoverySystem);
     }
 
     @Test
@@ -5339,7 +5338,7 @@
         // not wiped.
         verify(getServices().userManagerInternal, never())
                 .removeUserEvenWhenDisallowed(anyInt());
-        verifyZeroInteractions(getServices().recoverySystem);
+        verifyNoMoreInteractions(getServices().recoverySystem);
     }
 
     @Test
@@ -5380,7 +5379,7 @@
         dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
 
         // DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped.
-        verifyZeroInteractions(getServices().recoverySystem);
+        verifyNoMoreInteractions(getServices().recoverySystem);
         verify(getServices().userManagerInternal, never())
                 .removeUserEvenWhenDisallowed(anyInt());
     }
@@ -7535,7 +7534,7 @@
         verify(getServices().notificationManager, never())
                 .notify(anyInt(), any(Notification.class));
         // Apps shouldn't be suspended.
-        verifyZeroInteractions(getServices().ipackageManager);
+        verifyNoMoreInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
 
         setUserUnlocked(CALLER_USER_HANDLE, false);
@@ -7548,7 +7547,7 @@
         verify(getServices().notificationManager, never())
                 .notify(anyInt(), any(Notification.class));
         // Apps shouldn't be suspended.
-        verifyZeroInteractions(getServices().ipackageManager);
+        verifyNoMoreInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
 
         // Pretend the alarm went off.
@@ -7561,7 +7560,7 @@
         verify(getServices().notificationManager, times(1))
                 .notifyAsUser(any(), anyInt(), any(), any());
         // Apps shouldn't be suspended yet.
-        verifyZeroInteractions(getServices().ipackageManager);
+        verifyNoMoreInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
         clearInvocations(getServices().notificationManager);
 
@@ -7570,7 +7569,7 @@
         sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
 
         // Verify the alarm was not set.
-        verifyZeroInteractions(getServices().alarmManager);
+        verifyNoMoreInteractions(getServices().alarmManager);
         // Now the user should see a notification about suspended apps.
         verify(getServices().notificationManager, times(1))
                 .notifyAsUser(any(), anyInt(), any(), any());
@@ -8754,7 +8753,7 @@
         sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);
 
         // Verify that EuiccManager was not called to delete the subscription.
-        verifyZeroInteractions(getServices().euiccManager);
+        verifyNoMoreInteractions(getServices().euiccManager);
     }
 
     private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index e20f1e7..a39f071 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -31,7 +31,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.ActivityManagerInternal;
 import android.content.Context;
@@ -226,7 +226,7 @@
 
         assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
         // getApplicationLocales should be never be invoked if not a system app.
-        verifyZeroInteractions(mMockActivityTaskManager);
+        verifyNoMoreInteractions(mMockActivityTaskManager);
         // Broadcast should be never sent if not a system app.
         verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
         // It shouldn't write to the file if not a system app.
@@ -244,7 +244,7 @@
                 Binder.getCallingUid());
 
         // getApplicationLocales should be never be invoked if not installer is not present.
-        verifyZeroInteractions(mMockActivityTaskManager);
+        verifyNoMoreInteractions(mMockActivityTaskManager);
         // Broadcast should be never sent if installer is not present.
         verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
         // It shouldn't write to file if no installer present.
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index a58a9cd..4a05ea6 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -48,7 +48,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -488,7 +488,7 @@
 
         projection.stop(StopReason.STOP_UNKNOWN);
 
-        verifyZeroInteractions(mMediaProjectionMetricsLogger);
+        verifyNoMoreInteractions(mMediaProjectionMetricsLogger);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index d55f967..2ed2704 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -23,7 +23,7 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
@@ -273,7 +273,7 @@
 
         assertThat(mMessagesForUid).isEmpty();
         assertThat(mWriteTriggered).isFalse();
-        verifyZeroInteractions(mPM);
+        verifyNoMoreInteractions(mPM);
     }
 
     @Test
diff --git a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
index c8afb78..630a7e4 100644
--- a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
@@ -23,7 +23,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
@@ -126,7 +126,7 @@
         locationListener.onLocationChanged(location);
 
         verify(mMockLocationManager).removeUpdates(locationListener);
-        verifyZeroInteractions(mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockTimeDetectorInternal);
         verify(mMockAlarmManager).set(
                 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                 anyLong(),
@@ -150,7 +150,7 @@
 
         // Verify the service returned to location listening.
         verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
-        verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
     }
 
     // Tests what happens when a call is made to startGnssListeningInternal() when service is
@@ -172,7 +172,7 @@
         // listening again.
         verify(mMockAlarmManager).cancel(alarmListenerCaptor.getValue());
         verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
-        verifyZeroInteractions(mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockTimeDetectorInternal);
     }
 
     private void advanceServiceToSleepingState(
@@ -190,7 +190,7 @@
                 any(), any(), any(), locationListenerCaptor.capture());
         LocationListener locationListener = locationListenerCaptor.getValue();
         Location location = new Location(LocationManager.GPS_PROVIDER);
-        verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
 
         locationListener.onLocationChanged(location);
 
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 66d7611..d34d74d 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -11,13 +11,8 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-android_test {
-    name: "FrameworksUiServicesTests",
-
-    // Include test java files
-    srcs: [
-        "src/**/*.java",
-    ],
+java_defaults {
+    name: "FrameworksUiServicesTests-defaults",
 
     static_libs: [
         "compatibility-device-util-axt-minus-dexmaker",
@@ -95,12 +90,72 @@
     javacflags: ["-parameters"],
 }
 
-test_module_config {
-    name: "FrameworksUiServicesTests_notification",
-    base: "FrameworksUiServicesTests",
-    test_suites: [
-        "automotive-tests",
-        "device-tests",
+// Utility files used by multiple tests
+filegroup {
+    name: "shared-srcs",
+    srcs: [
+        "src/android/app/ExampleActivity.java",
+        "src/android/app/NotificationSystemUtil.java",
+        "src/com/android/frameworks/tests/uiservices/DummyProvider.java",
+        "src/com/android/internal/logging/InstanceIdSequenceFake.java",
+        "src/com/android/server/UiServiceTestCase.java",
+        "src/com/android/server/notification/ZenChangeOrigin.java",
+        "src/com/android/server/notification/ZenModeEventLoggerFake.java",
     ],
-    exclude_annotations: ["androidx.test.filters.LargeTest"],
+    visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "notification-srcs",
+    srcs: [
+        "src/**/Notification*.java",
+        "src/com/android/server/notification/*.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "notification-zen-srcs",
+    srcs: [
+        "src/android/app/NotificationManagerZenTest.java",
+        "src/com/android/server/notification/Zen*Test.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+android_test {
+    name: "FrameworksUiServicesTests",
+
+    // Include test java files but not the notification & zen ones which are separated
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    exclude_srcs: [
+        ":notification-srcs",
+        ":notification-zen-srcs",
+    ],
+
+    defaults: ["FrameworksUiServicesTests-defaults"],
+}
+
+android_test {
+    name: "FrameworksUiServicesNotificationTests",
+    srcs: [
+        ":notification-srcs",
+        ":shared-srcs",
+    ],
+    exclude_srcs: [":notification-zen-srcs"],
+    defaults: ["FrameworksUiServicesTests-defaults"],
+    test_config: "notification-tests.xml",
+}
+
+android_test {
+    name: "FrameworksUiServicesZenTests",
+    srcs: [
+        ":notification-zen-srcs",
+        ":shared-srcs",
+    ],
+    defaults: ["FrameworksUiServicesTests-defaults"],
+    test_config: "notification-zen-tests.xml",
 }
diff --git a/services/tests/uiservicestests/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
index 11e8f09..93c8c72 100644
--- a/services/tests/uiservicestests/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -15,6 +15,7 @@
 -->
 <configuration description="Runs Frameworks UI Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="FrameworksUiServicesTests.apk" />
     </target_preparer>
 
diff --git a/services/tests/uiservicestests/notification-tests.xml b/services/tests/uiservicestests/notification-tests.xml
new file mode 100644
index 0000000..acfd844
--- /dev/null
+++ b/services/tests/uiservicestests/notification-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (notifications subset).">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksUiServicesNotificationTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="FrameworksUiServicesNotificationTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
+        <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/uiservicestests/notification-zen-tests.xml b/services/tests/uiservicestests/notification-zen-tests.xml
new file mode 100644
index 0000000..01d8aab
--- /dev/null
+++ b/services/tests/uiservicestests/notification-zen-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (zen mode subset).">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksUiServicesZenTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="FrameworksUiServicesZenTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
+        <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 9930c9f..7b1ce44 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -62,7 +62,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -992,7 +991,7 @@
                 () -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
         verify(mContext, never()).enforceCallingPermission(
                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
-        verifyZeroInteractions(mBinder);
+        verifyNoMoreInteractions(mBinder);
         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
     }
 
@@ -1008,7 +1007,7 @@
                 () -> mService.requestProjection(mBinder, multipleProjectionTypes, PACKAGE_NAME));
         verify(mContext, never()).enforceCallingPermission(
                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
-        verifyZeroInteractions(mBinder);
+        verifyNoMoreInteractions(mBinder);
         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index 6b989cb..b332331 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -38,7 +38,6 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.Condition;
 
-import com.android.internal.R;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -47,8 +46,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.List;
-
 public class ConditionProvidersTest extends UiServiceTestCase {
 
     private ConditionProviders mProviders;
@@ -172,15 +169,4 @@
 
         assertTrue(mProviders.getApproved(userId, true).isEmpty());
     }
-
-    @Test
-    public void getDefaultDndAccessPackages_returnsPackages() {
-        mContext.getOrCreateTestableResources().addOverride(
-                R.string.config_defaultDndAccessPackages,
-                "com.example.a:com.example.b::::com.example.c");
-
-        List<String> packages = ConditionProviders.getDefaultDndAccessPackages(mContext);
-
-        assertThat(packages).containsExactly("com.example.a", "com.example.b", "com.example.c");
-    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 5ce9a3e..8023bdd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -36,7 +36,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.KeyguardManager;
@@ -184,7 +183,7 @@
         mApplier.apply(noEffects, ORIGIN_USER_IN_SYSTEMUI);
 
         verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
-        verifyZeroInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
+        verifyNoMoreInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
     }
 
     @Test
@@ -252,8 +251,8 @@
         // Wallpaper dimming was undone, Grayscale was applied, nothing else was touched.
         verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
         verify(mColorDisplayManager).setSaturationLevel(eq(0));
-        verifyZeroInteractions(mPowerManager);
-        verifyZeroInteractions(mUiModeManager);
+        verifyNoMoreInteractions(mPowerManager);
+        verifyNoMoreInteractions(mUiModeManager);
     }
 
     @Test
@@ -269,7 +268,7 @@
                 ORIGIN_APP);
 
         // Effect was not yet applied, but a broadcast receiver was registered.
-        verifyZeroInteractions(mUiModeManager);
+        verifyNoMoreInteractions(mUiModeManager);
         verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
                 intentFilterCaptor.capture(), anyInt());
         assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF);
@@ -337,7 +336,7 @@
                 origin.value());
 
         // Effect was not applied, will be on next screen-off.
-        verifyZeroInteractions(mUiModeManager);
+        verifyNoMoreInteractions(mUiModeManager);
         verify(mContext).registerReceiver(any(),
                 argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
                 anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 46be9a5..1114365 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -60,7 +60,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.SuppressLint;
@@ -313,7 +313,7 @@
                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
                 false);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -327,7 +327,7 @@
         }
         mGroupHelper.onNotificationPosted(
             getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -340,7 +340,7 @@
         }
         mGroupHelper.onNotificationPosted(
             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -353,7 +353,7 @@
         mGroupHelper.onNotificationPosted(
             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a", false),
             false);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1744,7 +1744,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1759,7 +1759,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1775,7 +1775,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1791,7 +1791,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1811,7 +1811,7 @@
                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
         notificationList.add(r);
         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1830,7 +1830,7 @@
                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.of(7), "testGrp", true);
         notificationList.add(r);
         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1853,7 +1853,7 @@
             String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
         notificationList.add(child);
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1877,7 +1877,7 @@
         notificationList.add(child);
         summaryByGroup.put(summary.getGroupKey(), summary);
         mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -2209,7 +2209,7 @@
             childrenToRemove.add(child);
         }
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         // Remove all child notifications from the valid group => summary without children
         Mockito.reset(mCallback);
@@ -2273,7 +2273,7 @@
             }
         }
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         // Remove some child notifications from the valid group, transform into a singleton group
         Mockito.reset(mCallback);
@@ -2329,7 +2329,7 @@
             notificationList.add(child);
         }
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         // Remove all child notifications from the valid group => summary without children
         Mockito.reset(mCallback);
@@ -2343,7 +2343,7 @@
         mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
                 summaryByGroup);
         // Check that nothing was force grouped
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -3837,7 +3837,7 @@
             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
 
@@ -3861,7 +3861,7 @@
             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
         }
         // FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS is disabled => don't force group
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -4498,7 +4498,7 @@
         mGroupHelper.onNotificationPostedWithDelay(extra, notifList, summaryByGroupKey);
 
         // no autogrouping should have occurred
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index a02f628..43228f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -96,7 +96,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -6372,7 +6372,7 @@
         NotificationChannel same = new NotificationChannel("id", "Bah", IMPORTANCE_DEFAULT);
         mHelper.createNotificationChannel(PKG_P, 0, same, true, false, 0, false);
 
-        verifyZeroInteractions(mHandler);
+        verifyNoMoreInteractions(mHandler);
     }
 
     @Test
@@ -6398,7 +6398,7 @@
 
         mHelper.updateNotificationChannel(PKG_P, 0, same, false, 0, false);
 
-        verifyZeroInteractions(mHandler);
+        verifyNoMoreInteractions(mHandler);
     }
 
     @Test
@@ -6412,7 +6412,7 @@
     public void setShowBadge_same_doesNotRequestSort() {
         mHelper.setShowBadge(PKG_P, 0, true); // true == DEFAULT_SHOW_BADGE
 
-        verifyZeroInteractions(mHandler);
+        verifyNoMoreInteractions(mHandler);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
deleted file mode 100644
index 154a905..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ZenConfigTrimmerTest extends UiServiceTestCase {
-
-    private static final String TRUSTED_PACKAGE = "com.trust.me";
-    private static final int ONE_PERCENT = 1_500;
-
-    private ZenConfigTrimmer mTrimmer;
-
-    @Before
-    public void setUp() {
-        mContext.getOrCreateTestableResources().addOverride(
-                R.string.config_defaultDndAccessPackages, TRUSTED_PACKAGE);
-
-        mTrimmer = new ZenConfigTrimmer(mContext);
-    }
-
-    @Test
-    public void trimToMaximumSize_belowMax_untouched() {
-        ZenModeConfig config = new ZenModeConfig();
-        addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-
-        mTrimmer.trimToMaximumSize(config);
-
-        assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "4", "5");
-    }
-
-    @Test
-    public void trimToMaximumSize_exceedsMax_removesAllRulesOfLargestPackages() {
-        ZenModeConfig config = new ZenModeConfig();
-        addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-        addZenRule(config, "7", "pkg4", 38 * ONE_PERCENT);
-
-        mTrimmer.trimToMaximumSize(config);
-
-        assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "6");
-        assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
-                .containsExactly("pkg1", "pkg3");
-    }
-
-    @Test
-    public void trimToMaximumSize_keepsRulesFromTrustedPackages() {
-        ZenModeConfig config = new ZenModeConfig();
-        addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "4", TRUSTED_PACKAGE, 60 * ONE_PERCENT);
-        addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-
-        mTrimmer.trimToMaximumSize(config);
-
-        assertThat(config.automaticRules.keySet()).containsExactly("4", "5");
-        assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
-                .containsExactly(TRUSTED_PACKAGE, "pkg2");
-    }
-
-    /**
-     * Create a ZenRule that, when serialized to a Parcel, will take <em>approximately</em>
-     * {@code desiredSize} bytes (within 100 bytes). Try to make the tests not rely on a very tight
-     * fit.
-     */
-    private static void addZenRule(ZenModeConfig config, String id, String pkg, int desiredSize) {
-        ZenRule rule = new ZenRule();
-        rule.id = id;
-        rule.pkg = pkg;
-        config.automaticRules.put(id, rule);
-
-        // Make the ZenRule as large as desired. Not to the exact byte, because otherwise this
-        // test would have to be adjusted whenever we change the parceling of ZenRule in any way.
-        // (Still might need adjustment if we change the serialization _significantly_).
-        int nameLength = desiredSize - id.length() - pkg.length() - 232;
-        rule.name = "A".repeat(nameLength);
-
-        Parcel verification = Parcel.obtain();
-        try {
-            verification.writeParcelable(rule, 0);
-            assertThat(verification.dataSize()).isWithin(100).of(desiredSize);
-        } finally {
-            verification.recycle();
-        }
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 5377102..bfce647 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -90,7 +90,6 @@
 import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
 import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
 import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static com.android.server.notification.Flags.FLAG_LIMIT_ZEN_CONFIG_SIZE;
 import static com.android.server.notification.Flags.FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING;
 import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -238,7 +237,6 @@
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
 @RunWith(ParameterizedAndroidJunit4.class)
-@EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE) // Should be parameterization, but off path does nothing.
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
@@ -7485,45 +7483,6 @@
         assertThat(getZenRule(ruleId).lastActivation).isNull();
     }
 
-    @Test
-    @EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE)
-    public void addAutomaticZenRule_trimsConfiguration() {
-        mZenModeHelper.mConfig.automaticRules.clear();
-        AutomaticZenRule smallRule = new AutomaticZenRule.Builder("Reasonable", CONDITION_ID)
-                .setConfigurationActivity(new ComponentName(mPkg, "cls"))
-                .build();
-        AutomaticZenRule systemRule = new AutomaticZenRule.Builder("System", CONDITION_ID)
-                .setOwner(new ComponentName("android", "ScheduleConditionProvider"))
-                .build();
-
-        AutomaticZenRule bigRule = new AutomaticZenRule.Builder("Yuge", CONDITION_ID)
-                .setConfigurationActivity(new ComponentName("evil.package", "cls"))
-                .setTriggerDescription("0123456789".repeat(6000)) // ~60k bytes utf16.
-                .build();
-
-        String systemRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android",
-                systemRule, ORIGIN_SYSTEM, "add", SYSTEM_UID);
-        String smallRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, smallRule,
-                ORIGIN_APP, "add", CUSTOM_PKG_UID);
-        String bigRuleId1 =  mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
-                bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
-                systemRuleId, smallRuleId, bigRuleId1);
-
-        String bigRuleId2 =  mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
-                bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
-                systemRuleId, smallRuleId, bigRuleId1, bigRuleId2);
-
-        // This should go over the threshold
-        String bigRuleId3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
-                bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-
-        // Rules from evil.package are gone.
-        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
-                systemRuleId, smallRuleId);
-    }
-
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 79e272b..01698b5 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -33,7 +33,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -133,7 +132,7 @@
         mVibratorControlService.registerVibratorController(controller1);
         mVibratorControlService.unregisterVibratorController(controller2);
 
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
         assertThat(controller1.isLinkedToDeath).isTrue();
     }
 
@@ -187,7 +186,7 @@
         verify(mStatsLoggerMock).logVibrationParamResponseIgnored();
         verifyNoMoreInteractions(mStatsLoggerMock);
 
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -242,7 +241,7 @@
                 mFakeVibratorController);
 
         verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -280,7 +279,7 @@
                 mFakeVibratorController);
 
         verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
     }
 
     @Test
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
index 9a59ede..011971d 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.media.soundtrigger.RecognitionStatus;
 import android.media.soundtrigger_middleware.RecognitionEventSys;
@@ -76,7 +75,7 @@
         assertEquals(event.halEventReceivedMillis, -1);
         assertEquals(event.recognitionEvent.status, RecognitionStatus.ABORTED);
         assertFalse(event.recognitionEvent.recognitionStillActive);
-        verifyZeroInteractions(mGlobalCallback);
+        verifyNoMoreInteractions(mGlobalCallback);
         clearInvocations(callback, mUnderlying);
 
         mNotifier.setActive(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8992c2e..7f242de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2021,8 +2021,6 @@
         display.setFixedRotationLaunchingAppUnchecked(activity);
         displayRotation.updateRotationUnchecked(true /* forceUpdate */);
 
-        assertTrue(displayRotation.isRotatingSeamlessly());
-
         // The launching rotated app should not be cleared when waiting for remote rotation.
         display.continueUpdateOrientationForDiffOrienLaunchingApp();
         assertTrue(display.isFixedRotationLaunchingApp(activity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 48731cb..00b617e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -383,7 +383,7 @@
         spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
         doReturn(true).when(
                         mActivity.mAppCompatController.getAspectRatioOverrides())
-                .isUserFullscreenOverrideEnabled();
+                .hasFullscreenOverride();
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -411,7 +411,7 @@
         spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
         doReturn(true).when(
                         mActivity.mAppCompatController.getAspectRatioOverrides())
-                .isSystemOverrideToFullscreenEnabled();
+                .hasFullscreenOverride();
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -1170,6 +1170,32 @@
     @Test
     @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
             Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+    public void testOptionsBoundsSet_flexibleLaunchSizeWithFullscreenOverride_noModifications() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchBounds(new Rect(
+                        DISPLAY_STABLE_BOUNDS.left,
+                        DISPLAY_STABLE_BOUNDS.top,
+                        /* right = */ 500,
+                        /* bottom = */ 500))
+                .setFlexibleLaunchSize(true);
+        spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
+        doReturn(true).when(
+                        mActivity.mAppCompatController.getAspectRatioOverrides())
+                .hasFullscreenOverride();
+
+        assertEquals(RESULT_DONE,
+                new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+        assertEquals(options.getLaunchBounds(), mResult.mBounds);
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
     public void testOptionsBoundsSet_flexibleLaunchSize_boundsSizeModified() {
         setupDesktopModeLaunchParamsModifier();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 0af41ea..89aa3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -56,7 +56,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ActivityInfo;
@@ -174,7 +174,7 @@
         da1.reduceOnAllTaskDisplayAreas(callback2, 0);
         da1.getItemFromTaskDisplayAreas(callback3);
 
-        verifyZeroInteractions(da2);
+        verifyNoMoreInteractions(da2);
 
         // Traverse the child if the current DA has type ANY
         final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3");
@@ -207,7 +207,7 @@
         da5.reduceOnAllTaskDisplayAreas(callback2, 0);
         da5.getItemFromTaskDisplayAreas(callback3);
 
-        verifyZeroInteractions(da6);
+        verifyNoMoreInteractions(da6);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4c81f73..ed00a9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,7 +30,10 @@
 import static android.os.Build.VERSION_CODES.P;
 import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
 import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.Display.FLAG_TRUSTED;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.Surface.ROTATION_0;
@@ -1706,8 +1709,6 @@
         app.setVisible(true);
         doReturn(false).when(app).inTransition();
         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
-        mStatusBarWindow.finishSeamlessRotation(t);
-        mNavBarWindow.finishSeamlessRotation(t);
 
         // The fixed rotation should be cleared and the new rotation is applied to display.
         assertFalse(app.hasFixedRotationTransform());
@@ -2925,6 +2926,63 @@
         assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
     }
 
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_shouldShowSystemDecorationsDisplay() {
+        // Set up a non-default display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_notAllowContentModeSwitchDisplay() {
+        // Set up a non-default display without FLAG_ALLOWS_CONTENT_MODE_SWITCH enabled
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = FLAG_TRUSTED;
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_untrustedDisplay() {
+        // Set up a non-default display without FLAG_TRUSTED enabled
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = (FLAG_ALLOWS_CONTENT_MODE_SWITCH | FLAG_TRUSTED);
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        spyOn(dc.mDisplay);
+        doReturn(false).when(dc.mDisplay).canHostTasks();
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+
+        doReturn(true).when(dc.mDisplay).canHostTasks();
+        dc.onDisplayInfoChangeApplied();
+        assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
     @EnableFlags(FLAG_ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS)
     @Test
     public void testForcedDensityRatioSetForExternalDisplays_persistDensityScaleFlagEnabled() {
@@ -2993,23 +3051,6 @@
         assertEquals(320, displayContent.mBaseDisplayDensity);
     }
 
-    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
-    @Test
-    public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
-        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
-        displayInfo.displayId = DEFAULT_DISPLAY + 1;
-        final DisplayContent dc = createNewDisplay(displayInfo);
-
-        spyOn(dc.mDisplay);
-        doReturn(false).when(dc.mDisplay).canHostTasks();
-        dc.onDisplayInfoChangeApplied();
-        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-
-        doReturn(true).when(dc.mDisplay).canHostTasks();
-        dc.onDisplayInfoChangeApplied();
-        assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-    }
-
     private void removeRootTaskTests(Runnable runnable) {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index eb6d5cf..e293c2f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -19,6 +19,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.NO_SURFACE;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -244,4 +247,33 @@
         verify(displayWindowInsetsController, times(1)).setImeInputTargetRequestedVisibility(
                 eq(true), any());
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+    public void testOnPostLayout_resetServerVisibilityWhenImeIsNotDrawn() {
+        final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
+        final WindowState inputTarget = newWindowBuilder("app", TYPE_APPLICATION).build();
+        makeWindowVisibleAndDrawn(ime);
+        mImeProvider.setWindowContainer(ime, null, null);
+        mImeProvider.setServerVisible(true);
+        mImeProvider.setClientVisible(true);
+        mImeProvider.updateVisibility();
+        mImeProvider.updateControlForTarget(inputTarget, true /* force */, null /* statsToken */);
+
+        // Calling onPostLayout, as the drawn state is initially false.
+        mImeProvider.onPostLayout();
+        assertTrue(mImeProvider.isSurfaceVisible());
+
+        // Reset window's drawn state
+        ime.mWinAnimator.mDrawState = NO_SURFACE;
+        mImeProvider.onPostLayout();
+        assertFalse(mImeProvider.isServerVisible());
+        assertFalse(mImeProvider.isSurfaceVisible());
+
+        // Set it back to drawn
+        ime.mWinAnimator.mDrawState = HAS_DRAWN;
+        mImeProvider.onPostLayout();
+        assertTrue(mImeProvider.isServerVisible());
+        assertTrue(mImeProvider.isSurfaceVisible());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8a7e743..2c6884e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -52,7 +52,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -1293,7 +1293,7 @@
 
         // Add secondTask to top again
         mRecentTasks.add(secondTask);
-        verifyZeroInteractions(controller);
+        verifyNoMoreInteractions(controller);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 59ee2f5..5624677 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -23,9 +23,6 @@
 import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsSource.ID_IME;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
@@ -88,7 +85,6 @@
 import android.content.ContentResolver;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -673,56 +669,6 @@
     }
 
     @Test
-    public void testSeamlesslyRotateWindow() {
-        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
-        final SurfaceControl.Transaction t = spy(StubTransaction.class);
-
-        makeWindowVisible(app);
-        app.mSurfaceControl = mock(SurfaceControl.class);
-        final Rect frame = app.getFrame();
-        frame.set(10, 20, 60, 80);
-        app.updateSurfacePosition(t);
-        assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
-        app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true /* requested */);
-        assertTrue(app.mSeamlesslyRotated);
-
-        // Verify we un-rotate the window state surface.
-        final Matrix matrix = new Matrix();
-        // Un-rotate 90 deg.
-        matrix.setRotate(270);
-        // Translate it back to origin.
-        matrix.postTranslate(0, mDisplayInfo.logicalWidth);
-        verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
-
-        // Verify we update the position as well.
-        final float[] curSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
-        matrix.mapPoints(curSurfacePos);
-        verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-
-        app.finishSeamlessRotation(t);
-        assertFalse(app.mSeamlesslyRotated);
-        assertNull(app.mPendingSeamlessRotate);
-
-        // Simulate the case with deferred layout and animation.
-        app.resetSurfacePositionForAnimationLeash(t);
-        clearInvocations(t);
-        mWm.mWindowPlacerLocked.deferLayout();
-        app.updateSurfacePosition(t);
-        // Because layout is deferred, the position should keep the reset value.
-        assertTrue(app.mLastSurfacePosition.equals(0, 0));
-
-        app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_270, true /* requested */);
-        // The last position must be updated so the surface can be unrotated properly.
-        assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
-        matrix.setRotate(90);
-        matrix.postTranslate(mDisplayInfo.logicalHeight, 0);
-        curSurfacePos[0] = frame.left;
-        curSurfacePos[1] = frame.top;
-        matrix.mapPoints(curSurfacePos);
-        verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-    }
-
-    @Test
     public void testVisibilityChangeSwitchUser() {
         final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
         window.mHasSurface = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index a02c3db..8907a72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -35,16 +37,19 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
+import android.graphics.Matrix;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
 import android.view.WindowInsets;
 import android.window.WindowContext;
 
@@ -335,6 +340,31 @@
     }
 
     @Test
+    public void testSeamlesslyRotate() {
+        final SurfaceControl.Transaction t = mTransaction;
+        final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+        token.mLastSurfacePosition.x = 10;
+        token.mLastSurfacePosition.y = 20;
+        final SeamlessRotator rotator = new SeamlessRotator(ROTATION_0, ROTATION_90,
+                mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */);
+        clearInvocations(t);
+        rotator.unrotate(t, token);
+
+        // Verify surface is un-rotated.
+        final Matrix matrix = new Matrix();
+        // Un-rotate 90 deg.
+        matrix.setRotate(270);
+        // Translate it back to origin.
+        matrix.postTranslate(0, mDisplayInfo.logicalWidth);
+        verify(t).setMatrix(eq(token.mSurfaceControl), eq(matrix), any(float[].class));
+
+        final float[] curSurfacePos = {token.mLastSurfacePosition.x, token.mLastSurfacePosition.y};
+        matrix.mapPoints(curSurfacePos);
+        verify(t).setPosition(eq(token.mSurfaceControl),
+                eq(curSurfacePos[0]), eq(curSurfacePos[1]));
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
     public void onDisplayChanged_differentDisplay_reparented() {
         final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index a1d35a7..0749c0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -22,7 +22,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -118,7 +118,7 @@
     @Test
     public void trace_discared_whenNotTracing() {
         mWindowTracing.logState("where");
-        verifyZeroInteractions(mWmMock);
+        verifyNoMoreInteractions(mWmMock);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 9367941..3da279b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -22,7 +22,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -128,7 +128,7 @@
     @Test
     public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
         sWindowTracing.logState("where");
-        verifyZeroInteractions(sWmMock);
+        verifyNoMoreInteractions(sWmMock);
     }
 
     @Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 50c5a6b6..1491510 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3918,6 +3918,22 @@
             "5g_icon_display_secondary_grace_period_string";
 
     /**
+     * When an NR advanced connection is lost and a Physical Cell ID (PCI) change occurs within
+     * the primary timer{@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING}, delay updating the network
+     * icon.
+     *
+     * <p>This delay is implemented because a rapid PCI change often indicates the device is
+     * switching to a nearby cell tower to quickly restore the NR advanced connection. Displaying
+     * an intermediate network icon (like 4G/LTE) might be misleading if the 5G connection is
+     * restored shortly after. This value sets the delay in seconds; 0 disables the feature.</p>
+     *
+     * @hide
+     */
+    public static final String KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT =
+            "nr_advanced_pci_change_secondary_timer_seconds_int";
+
+
+    /**
      * The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
      * to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
      *
@@ -11222,6 +11238,7 @@
                         + "not_restricted_rrc_con:5G");
         sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
         sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+        sDefaults.putInt(KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT, 0);
         sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index f528263..6e23edf 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -614,7 +614,7 @@
 
     /** @hide */
     public static int convertRssiAsuToDBm(int rssiAsu) {
-        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN || rssiAsu == Integer.MAX_VALUE) {
             return CellInfo.UNAVAILABLE;
         }
         if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e6515f13..850ce3e 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -39,7 +38,6 @@
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.Qos;
 
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.lang.annotation.Retention;
@@ -89,35 +87,30 @@
      * Unsupported. The unsupported state is used when the data network cannot support the network
      * validation function for the current data connection state.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;
 
     /**
      * Not Requested. The not requested status is used when the data network supports the network
      * validation function, but no network validation is being performed yet.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;
 
     /**
      * In progress. The in progress state is used when the network validation process for the data
      * network is in progress. This state is followed by either success or failure.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;
 
     /**
      * Success. The Success status is used when network validation has been completed for the data
      * network and the result is successful.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_SUCCESS = 3;
 
     /**
      * Failure. The Failure status is used when network validation has been completed for the data
      * network and the result is failure.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_FAILURE = 4;
 
     /**
@@ -360,7 +353,6 @@
      *
      * @return the network validation status of the data call
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public @NetworkValidationStatus int getNetworkValidationStatus() {
         return mNetworkValidationStatus;
     }
@@ -615,7 +607,6 @@
          * @param networkValidationStatus the network validation status of the data call
          * @return The builder
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public @NonNull Builder setNetworkValidationStatus(
                 @NetworkValidationStatus int networkValidationStatus) {
             mNetworkValidationStatus = networkValidationStatus;
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index 7356cdc..42d09cf 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -31,7 +31,6 @@
 import android.telephony.ims.ImsManager;
 import android.telephony.satellite.SatelliteManager;
 
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.Preconditions;
 
 
@@ -77,9 +76,6 @@
     // also check through Compatibility framework a few lines below).
     @SuppressWarnings("AndroidFrameworkCompatChange")
     private static boolean hasSystemFeature(Context context, String feature) {
-        // Check release status of this change in behavior.
-        if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true;
-
         // Check SDK version of the vendor partition.
         final int vendorApiLevel = SystemProperties.getInt(
                 "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 14d567d..2983e44 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -691,7 +691,7 @@
             case UNKNOWN:
                 modemCount = 1;
                 // check for voice and data support, 0 if not supported
-                if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+                if (!isDeviceVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
                     modemCount = 0;
                 }
                 break;
@@ -2814,7 +2814,7 @@
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getPhoneType() {
-        if (!isVoiceCapable() && !isDataCapable()) {
+        if (!isDeviceVoiceCapable() && !isDataCapable()) {
             return PHONE_TYPE_NONE;
         }
         return getCurrentPhoneType();
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index b6f9e1f..0e70306 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -17,7 +17,6 @@
 
 package android.telephony.data;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -32,7 +31,6 @@
 import android.telephony.data.ApnSetting.ProtocolType;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -455,7 +453,6 @@
      *
      * @return The network validation status of data connection.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
         return mNetworkValidationStatus;
     }
@@ -936,7 +933,6 @@
          * @param status The network validation status.
          * @return The same instance of the builder.
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public @NonNull Builder setNetworkValidationStatus(
                 @PreciseDataConnectionState.NetworkValidationStatus int status) {
             mNetworkValidationStatus = status;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f04e1c9..5baf463 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -17,7 +17,6 @@
 package android.telephony.data;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -40,7 +39,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.FunctionalUtils;
 import com.android.telephony.Rlog;
 
@@ -414,7 +412,6 @@
          * @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
          *     request validation to the DataService and checks if the request has been submitted.
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public void requestNetworkValidation(int cid,
                 @NonNull @CallbackExecutor Executor executor,
                 @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index f775de6..c42b29c1 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -17,7 +17,6 @@
 package android.telephony.data;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -41,7 +40,6 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.flags.FeatureFlagsImpl;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.FunctionalUtils;
 import com.android.telephony.Rlog;
 
@@ -290,7 +288,6 @@
          * @param resultCodeCallback A callback to determine whether the request was successfully
          *     submitted or not.
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public void requestNetworkValidation(
                 @NetCapability int networkCapability,
                 @NonNull @CallbackExecutor Executor executor,
@@ -298,15 +295,6 @@
             Objects.requireNonNull(executor, "executor cannot be null");
             Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
 
-            if (!sFeatureFlag.networkValidation()) {
-                loge("networkValidation feature is disabled");
-                executor.execute(
-                        () ->
-                                resultCodeCallback.accept(
-                                        DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
-                return;
-            }
-
             IIntegerConsumer callback = new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 0c3c7e2..e868a6c 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -34,7 +34,7 @@
 import org.junit.Test
 import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
-import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
 
 /**
  * Test for testing revokeTrust & grantTrust for non-renewable trust.
@@ -120,7 +120,7 @@
         trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
         await()
 
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
     }
 
     companion object {
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
index f5d4b0c..cc7eebc 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
@@ -33,7 +33,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -120,7 +120,7 @@
 
         verify(mUsbPortManager).enableUsbData(TEST_PORT_ID,
                 enable, TEST_TRANSACTION_ID, mCallback, null);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         clearInvocations(mUsbPortManager);
         clearInvocations(mCallback);
@@ -131,7 +131,7 @@
         assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable,
                 TEST_TRANSACTION_ID, mCallback, requester, isInternalRequest));
 
-        verifyZeroInteractions(mUsbPortManager);
+        verifyNoMoreInteractions(mUsbPortManager);
         verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
 
         clearInvocations(mUsbPortManager);
@@ -188,7 +188,7 @@
         mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID,
                 mCallback, TEST_SECOND_CALLER_ID, false);
 
-        verifyZeroInteractions(mUsbPortManager);
+        verifyNoMoreInteractions(mUsbPortManager);
         verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
     }
 
@@ -203,7 +203,7 @@
 
         verify(mUsbPortManager).enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID,
                         mCallback, null);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     /**