Merge "Logs BundlePreferences on pkg data pull" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 16c7017..ae5542b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3405,12 +3405,12 @@
 
   public final class VirtualDeviceManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
-    method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
-    method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @Nullable public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String);
+    method @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
+    method @Nullable public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String);
     field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2
     field public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; // 0x1
     field public static final int LAUNCH_SUCCESS = 0; // 0x0
-    field @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
+    field public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
   }
 
   public static interface VirtualDeviceManager.ActivityListener {
@@ -3432,7 +3432,7 @@
 
   public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
     method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
-    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
     method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
     method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
     method public void close();
@@ -3448,7 +3448,7 @@
     method @Deprecated @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
     method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
-    method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
+    method @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
     method @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
     method @Deprecated @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method public int getDeviceId();
@@ -3458,10 +3458,10 @@
     method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
     method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
-    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
     method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
     method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
-    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void setDevicePolicy(int, int);
+    method public void setDevicePolicy(int, int);
     method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void setDevicePolicy(int, int, int);
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public void setDisplayImePolicy(int, int);
     method public void setShowPointerIcon(boolean);
@@ -3481,7 +3481,7 @@
     method @Deprecated public int getDefaultNavigationPolicy();
     method public int getDevicePolicy(int);
     method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getDimDuration();
-    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
+    method @Nullable public android.content.ComponentName getHomeComponent();
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent();
     method public int getLockState();
     method @Nullable public String getName();
@@ -3498,11 +3498,11 @@
     field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
     field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
     field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
-    field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
+    field public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
     field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
     field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6; // 0x6
     field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5
-    field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
+    field public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
     field @FlaggedApi("android.companion.virtualdevice.flags.default_device_camera_access_policy") public static final int POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS = 7; // 0x7
     field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
     field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
@@ -3520,7 +3520,7 @@
     method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
     method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDimDuration(@NonNull java.time.Duration);
-    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
+    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
     method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
@@ -5332,13 +5332,13 @@
 
   public final class VirtualDisplayConfig implements android.os.Parcelable {
     method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
-    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+    method public boolean isHomeSupported();
     method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
   }
 
   public static final class VirtualDisplayConfig.Builder {
     method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
-    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+    method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
     method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
   }
 
@@ -5970,13 +5970,13 @@
     method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setScrollAmount(@FloatRange(from=-1.0F, to=1.0f) float);
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable {
+  public class VirtualStylus implements java.io.Closeable {
     method public void close();
     method public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
     method public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent);
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusButtonEvent implements android.os.Parcelable {
+  public final class VirtualStylusButtonEvent implements android.os.Parcelable {
     method public int describeContents();
     method public int getAction();
     method public int getButtonCode();
@@ -5989,7 +5989,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualStylusButtonEvent> CREATOR;
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusButtonEvent.Builder {
+  public static final class VirtualStylusButtonEvent.Builder {
     ctor public VirtualStylusButtonEvent.Builder();
     method @NonNull public android.hardware.input.VirtualStylusButtonEvent build();
     method @NonNull public android.hardware.input.VirtualStylusButtonEvent.Builder setAction(int);
@@ -5997,7 +5997,7 @@
     method @NonNull public android.hardware.input.VirtualStylusButtonEvent.Builder setEventTimeNanos(long);
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
+  public final class VirtualStylusConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getHeight();
     method public int getWidth();
@@ -6005,12 +6005,12 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualStylusConfig> CREATOR;
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualStylusConfig.Builder> {
+  public static final class VirtualStylusConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualStylusConfig.Builder> {
     ctor public VirtualStylusConfig.Builder(@IntRange(from=1) int, @IntRange(from=1) int);
     method @NonNull public android.hardware.input.VirtualStylusConfig build();
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusMotionEvent implements android.os.Parcelable {
+  public final class VirtualStylusMotionEvent implements android.os.Parcelable {
     method public int describeContents();
     method public int getAction();
     method public long getEventTimeNanos();
@@ -6029,7 +6029,7 @@
     field public static final int TOOL_TYPE_STYLUS = 2; // 0x2
   }
 
-  @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusMotionEvent.Builder {
+  public static final class VirtualStylusMotionEvent.Builder {
     ctor public VirtualStylusMotionEvent.Builder();
     method @NonNull public android.hardware.input.VirtualStylusMotionEvent build();
     method @NonNull public android.hardware.input.VirtualStylusMotionEvent.Builder setAction(int);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index af6978a..82c746a 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1846,6 +1846,7 @@
     }
 
     /** @hide */
+    @WindowConfiguration.WindowingMode
     public int getLaunchWindowingMode() {
         return mLaunchWindowingMode;
     }
@@ -1855,7 +1856,7 @@
      * @hide
      */
     @TestApi
-    public void setLaunchWindowingMode(int windowingMode) {
+    public void setLaunchWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
         mLaunchWindowingMode = windowingMode;
     }
 
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 311e24b..3ef78af 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -32,7 +32,6 @@
 import android.companion.virtual.camera.VirtualCamera;
 import android.companion.virtual.camera.VirtualCameraConfig;
 import android.companion.virtual.sensor.VirtualSensor;
-import android.companion.virtualdevice.flags.Flags;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -473,14 +472,12 @@
             @Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) {
         if (mVirtualAudioDevice == null) {
             try {
-                Context context = mContext;
-                if (Flags.deviceAwareRecordAudioPermission()) {
-                    // When using a default policy for audio device-aware RECORD_AUDIO permission
-                    // should not take effect, thus register policies with the default context.
-                    if (mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM) {
-                        context = mContext.createDeviceContext(getDeviceId());
-                    }
-                }
+                // When using a default policy for audio, the device-aware RECORD_AUDIO permission
+                // should not take effect, thus register policies with the default context.
+                final Context context =
+                        mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM
+                                ? mContext.createDeviceContext(getDeviceId())
+                                : mContext;
                 mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display,
                         executor, callback, () -> mVirtualAudioDevice = null);
             } catch (RemoteException e) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 73ea9f0..91ea673 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -170,7 +170,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
     public static final String PERSISTENT_DEVICE_ID_DEFAULT =
             "default:" + Context.DEVICE_ID_DEFAULT;
 
@@ -393,7 +392,6 @@
      * @hide
      */
     // TODO(b/315481938): Link @see VirtualDevice#getPersistentDeviceId()
-    @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
     @SystemApi
     @Nullable
     public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String persistentDeviceId) {
@@ -416,7 +414,6 @@
      * @hide
      */
     // TODO(b/315481938): Link @see VirtualDevice#getPersistentDeviceId()
-    @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
     @SystemApi
     @NonNull
     public Set<String> getAllPersistentDeviceIds() {
@@ -780,7 +777,6 @@
          * @see VirtualDeviceParams#POLICY_TYPE_RECENTS
          * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
          */
-        @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
         public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
                 @VirtualDeviceParams.DevicePolicy int devicePolicy) {
             mVirtualDeviceInternal.setDevicePolicy(policyType, devicePolicy);
@@ -802,7 +798,6 @@
          * @see #removeActivityPolicyExemption(ComponentName)
          * @see #setDevicePolicy
          */
-        @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
         public void addActivityPolicyExemption(@NonNull ComponentName componentName) {
             addActivityPolicyExemption(new ActivityPolicyExemption.Builder()
                     .setComponentName(componentName)
@@ -825,7 +820,6 @@
          * @see #addActivityPolicyExemption(ComponentName)
          * @see #setDevicePolicy
          */
-        @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
         public void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
             removeActivityPolicyExemption(new ActivityPolicyExemption.Builder()
                     .setComponentName(componentName)
@@ -1037,9 +1031,7 @@
          * @param config the touchscreen configurations for the virtual stylus.
          */
         @NonNull
-        @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
-        public VirtualStylus createVirtualStylus(
-                @NonNull VirtualStylusConfig config) {
+        public VirtualStylus createVirtualStylus(@NonNull VirtualStylusConfig config) {
             return mVirtualDeviceInternal.createVirtualStylus(config);
         }
 
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 2be27da..761e75b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -248,7 +248,6 @@
      */
     // TODO(b/333443509): Update the documentation of custom policy and link to the new policy
     // POLICY_TYPE_BLOCKED_ACTIVITY
-    @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
     public static final int POLICY_TYPE_ACTIVITY = 3;
 
     /**
@@ -264,7 +263,6 @@
      *
      * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
      */
-    @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD)
     public static final int POLICY_TYPE_CLIPBOARD = 4;
 
     /**
@@ -431,7 +429,6 @@
      * @see Builder#setHomeComponent
      * @see VirtualDisplayConfig#isHomeSupported()
      */
-    @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
     @Nullable
     public ComponentName getHomeComponent() {
         return mHomeComponent;
@@ -926,7 +923,6 @@
          *
          * @see VirtualDisplayConfig#isHomeSupported()
          */
-        @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
         @NonNull
         public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
             mHomeComponent = homeComponent;
@@ -1282,33 +1278,31 @@
                         mVirtualSensorDirectChannelCallback);
             }
 
-            if (Flags.dynamicPolicy()) {
-                switch (mDevicePolicies.get(POLICY_TYPE_ACTIVITY, -1)) {
-                    case DEVICE_POLICY_DEFAULT:
-                        if (mDefaultActivityPolicyConfigured
-                                && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
-                            throw new IllegalArgumentException(
-                                    "DEVICE_POLICY_DEFAULT is explicitly configured for "
-                                            + "POLICY_TYPE_ACTIVITY, which is exclusive with "
-                                            + "setAllowedActivities.");
-                        }
-                        break;
-                    case DEVICE_POLICY_CUSTOM:
-                        if (mDefaultActivityPolicyConfigured
-                                && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED) {
-                            throw new IllegalArgumentException(
-                                    "DEVICE_POLICY_CUSTOM is explicitly configured for "
-                                            + "POLICY_TYPE_ACTIVITY, which is exclusive with "
-                                            + "setBlockedActivities.");
-                        }
-                        break;
-                    default:
-                        if (mDefaultActivityPolicyConfigured
-                                && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
-                            mDevicePolicies.put(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM);
-                        }
-                        break;
-                }
+            switch (mDevicePolicies.get(POLICY_TYPE_ACTIVITY, -1)) {
+                case DEVICE_POLICY_DEFAULT:
+                    if (mDefaultActivityPolicyConfigured
+                            && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
+                        throw new IllegalArgumentException(
+                                "DEVICE_POLICY_DEFAULT is explicitly configured for "
+                                        + "POLICY_TYPE_ACTIVITY, which is exclusive with "
+                                        + "setAllowedActivities.");
+                    }
+                    break;
+                case DEVICE_POLICY_CUSTOM:
+                    if (mDefaultActivityPolicyConfigured
+                            && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED) {
+                        throw new IllegalArgumentException(
+                                "DEVICE_POLICY_CUSTOM is explicitly configured for "
+                                        + "POLICY_TYPE_ACTIVITY, which is exclusive with "
+                                        + "setBlockedActivities.");
+                    }
+                    break;
+                default:
+                    if (mDefaultActivityPolicyConfigured
+                            && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
+                        mDevicePolicies.put(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM);
+                    }
+                    break;
             }
 
             if (mDimDuration.compareTo(mScreenOffTimeout) > 0) {
@@ -1319,10 +1313,6 @@
                 mScreenOffTimeout = INFINITE_TIMEOUT;
             }
 
-            if (!Flags.crossDeviceClipboard()) {
-                mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);
-            }
-
             if (!Flags.virtualCamera()) {
                 mDevicePolicies.delete(POLICY_TYPE_CAMERA);
             }
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 84af840..6da2a07 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -19,14 +19,6 @@
 
 flag {
      namespace: "virtual_devices"
-     name: "device_aware_record_audio_permission"
-     description: "Enable device-aware RECORD_AUDIO permission"
-     bug: "291737188"
-     is_fixed_read_only: true
-}
-
-flag {
-     namespace: "virtual_devices"
      name: "media_projection_keyguard_restrictions"
      description: "Auto-stop MP when the device locks"
      bug: "348335290"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 01e24d81..885a2db 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4215,6 +4215,17 @@
     public static final String ACTION_USER_INFO_CHANGED =
             "android.intent.action.USER_INFO_CHANGED";
 
+
+    /**
+     * Broadcast sent to the system when a user's information changes. Carries an extra
+     * {@link #EXTRA_USER_HANDLE} to indicate which user's information changed.
+     * This is only sent to permission protected manifest receivers. It is sent to all users.
+     * @hide
+     */
+    @BroadcastBehavior(includeBackground = true)
+    public static final String ACTION_USER_INFO_CHANGED_BACKGROUND =
+            "android.intent.action.USER_INFO_CHANGED_BACKGROUND";
+
     /**
      * Broadcast sent to the primary user when an associated managed profile is added (the profile
      * was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies
@@ -5460,7 +5471,7 @@
     /**
      * Activities that can be safely invoked from a browser must support this
      * category.  For example, if the user is viewing a web page or an e-mail
-     * and clicks on a link in the text, the Intent generated execute that
+     * and clicks on a link in the text, the Intent generated to execute that
      * link will require the BROWSABLE category, so that only activities
      * supporting this category will be considered as possible actions.  By
      * supporting this category, you are promising that there is nothing
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index a0c0f12..1724d9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1932,6 +1932,9 @@
      * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
      * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
+     * <p>This callback will also receive changes to the {@link LauncherUserInfo#getUserConfig()},
+     * allowing clients to monitor updates to the user-specific configuration.
+     *
      * @param callback The callback to register.
      */
     // Alternatively, a system app can access this api for private profile if they've been granted
@@ -1950,6 +1953,9 @@
      * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
      * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
+     * <p>This callback will also receive changes to the {@link LauncherUserInfo#getUserConfig()},
+     * allowing clients to monitor updates to the user-specific configuration.
+     *
      * @param callback The callback to register.
      * @param handler that should be used to post callbacks on, may be null.
      */
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 6230f4d..44d662f 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -38,4 +38,16 @@
     description: "Enables Rear Display Mode V2, where the inner display shows the user a UI affordance for exiting the state"
     bug: "372486634"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "device_state_configuration_flag"
+    is_exported: true
+    namespace: "windowing_sdk"
+    description: "Re-add flag parsing for device_state_configuration.xml configuration for devices that didn't update vendor images."
+    bug: "388366842"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 7257055..2a9ee7f 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -237,10 +237,9 @@
      * @see Builder#setHomeSupported
      * @hide
      */
-    @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
     @SystemApi
     public boolean isHomeSupported() {
-        return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported;
+        return mIsHomeSupported;
     }
 
     /**
@@ -605,7 +604,6 @@
          * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          * @hide
          */
-        @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
         @SystemApi
         @NonNull
         public Builder setHomeSupported(boolean isHomeSupported) {
diff --git a/core/java/android/hardware/input/VirtualStylus.java b/core/java/android/hardware/input/VirtualStylus.java
index 4b79bc4..32aac2e 100644
--- a/core/java/android/hardware/input/VirtualStylus.java
+++ b/core/java/android/hardware/input/VirtualStylus.java
@@ -16,11 +16,9 @@
 
 package android.hardware.input;
 
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
-import android.companion.virtual.flags.Flags;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -34,7 +32,6 @@
  *
  * @hide
  */
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
 @SystemApi
 public class VirtualStylus extends VirtualInputDevice {
     /** @hide */
diff --git a/core/java/android/hardware/input/VirtualStylusButtonEvent.java b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
index 8fcf561b..9fe725a 100644
--- a/core/java/android/hardware/input/VirtualStylusButtonEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
@@ -16,11 +16,9 @@
 
 package android.hardware.input;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -35,7 +33,6 @@
  *
  * @hide
  */
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
 @SystemApi
 public final class VirtualStylusButtonEvent implements Parcelable {
     /** @hide */
@@ -128,7 +125,6 @@
     /**
      * Builder for {@link VirtualStylusButtonEvent}.
      */
-    @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
     public static final class Builder {
 
         @Action
diff --git a/core/java/android/hardware/input/VirtualStylusConfig.java b/core/java/android/hardware/input/VirtualStylusConfig.java
index 64cf1f5..3c56023f 100644
--- a/core/java/android/hardware/input/VirtualStylusConfig.java
+++ b/core/java/android/hardware/input/VirtualStylusConfig.java
@@ -16,11 +16,9 @@
 
 package android.hardware.input;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,7 +27,6 @@
  *
  * @hide
  */
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
 @SystemApi
 public final class VirtualStylusConfig extends VirtualTouchDeviceConfig implements Parcelable {
 
@@ -68,7 +65,6 @@
     /**
      * Builder for creating a {@link VirtualStylusConfig}.
      */
-    @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
     public static final class Builder extends VirtualTouchDeviceConfig.Builder<Builder> {
 
         /**
diff --git a/core/java/android/hardware/input/VirtualStylusMotionEvent.java b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
index 0ac6f3a..fa0ff4f 100644
--- a/core/java/android/hardware/input/VirtualStylusMotionEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
@@ -16,12 +16,10 @@
 
 package android.hardware.input;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -38,7 +36,6 @@
  *
  * @hide
  */
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
 @SystemApi
 public final class VirtualStylusMotionEvent implements Parcelable {
     private static final int TILT_MIN = -90;
@@ -209,7 +206,6 @@
     /**
      * Builder for {@link VirtualStylusMotionEvent}.
      */
-    @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
     public static final class Builder {
 
         @ToolType
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 0125905..2fe4871 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -70,6 +70,13 @@
 
     private static final String TAG = "Looper";
 
+    private static class NoImagePreloadHolder {
+        // Enable/Disable verbose logging with a system prop. e.g.
+        // adb shell 'setprop log.looper.slow.verbose false && stop && start'
+        private static final boolean sVerboseLogging =
+                SystemProperties.getBoolean("log.looper.slow.verbose", false);
+    }
+
     // sThreadLocal.get() will return null unless you've called prepare().
     @UnsupportedAppUsage
     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@@ -246,17 +253,21 @@
             }
         }
         if (logSlowDelivery) {
+            boolean slow = false;
+
+            if (!me.mSlowDeliveryDetected || NoImagePreloadHolder.sVerboseLogging) {
+                slow = showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart,
+                        "delivery", msg);
+            }
             if (me.mSlowDeliveryDetected) {
-                if ((dispatchStart - msg.when) <= 10) {
+                if (!slow && (dispatchStart - msg.when) <= 10) {
                     Slog.w(TAG, "Drained");
                     me.mSlowDeliveryDetected = false;
                 }
-            } else {
-                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
-                        msg)) {
-                    // Once we write a slow delivery log, suppress until the queue drains.
-                    me.mSlowDeliveryDetected = true;
-                }
+            } else if (slow) {
+                // A slow delivery is detected, suppressing further logs unless verbose logging
+                // is enabled.
+                me.mSlowDeliveryDetected = true;
             }
         }
         if (logSlowDispatch) {
@@ -322,6 +333,23 @@
 
     @android.ravenwood.annotation.RavenwoodReplace
     private static int getThresholdOverride() {
+        // Allow overriding the threshold for all processes' main looper with a system prop.
+        // e.g. adb shell 'setprop log.looper.any.main.slow 1 && stop && start'
+        if (myLooper() == getMainLooper()) {
+            final int globalOverride = SystemProperties.getInt("log.looper.any.main.slow", -1);
+            if (globalOverride >= 0) {
+                return globalOverride;
+            }
+        }
+
+        // Allow overriding the threshold for all threads within a process with a system prop.
+        // e.g. adb shell 'setprop log.looper.1000.any.slow 1 && stop && start'
+        final int processOverride = SystemProperties.getInt("log.looper."
+                + Process.myUid() + ".any.slow", -1);
+        if (processOverride >= 0) {
+            return processOverride;
+        }
+
         return SystemProperties.getInt("log.looper."
                 + Process.myUid() + "."
                 + Thread.currentThread().getName()
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index febbfca..9d0e221 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -344,11 +344,7 @@
                 || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return mHintManager.getCpuHeadroomMinIntervalMillis();
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        return mHintManagerClientData.supportInfo.headroom.cpuMinIntervalMillis;
     }
 
     /**
@@ -366,11 +362,7 @@
                 || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return mHintManager.getGpuHeadroomMinIntervalMillis();
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        return mHintManagerClientData.supportInfo.headroom.gpuMinIntervalMillis;
     }
 
     /**
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 4fead2a..6decd6d 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -112,6 +112,7 @@
     private Insets mPendingInsets;
     private float mPendingFraction;
     private boolean mFinished;
+    private boolean mCancelling;
     private boolean mCancelled;
     private boolean mShownOnFinish;
     private float mCurrentAlpha = 1.0f;
@@ -371,7 +372,7 @@
         mPendingInsets = mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                 ? mShownInsets : mHiddenInsets;
         mPendingAlpha = 1f;
-        mPendingFraction = 1f;
+        mCancelling = true;
         applyChangeInsets(null);
         mCancelled = true;
         mListener.onCancelled(mReadyDispatched ? this : null);
@@ -488,15 +489,15 @@
             return;
         }
 
-        final boolean visible = mPendingFraction == 0
-                // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
-                // animated from the hidden state.
-                ? mAnimationType != ANIMATION_TYPE_SHOW
-                : mPendingFraction < 1f || (mFinished
-                        ? mShownOnFinish
-                        // If the animation is cancelled, mFinished and mShownOnFinish are not set.
+        final boolean visible = mFinished
+                ? mShownOnFinish
+                : (mCancelling
+                        // If the animation is being cancelled, mShownOnFinish is not valid.
                         // Here uses mLayoutInsetsDuringAnimation to decide if it should be visible.
-                        : mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
+                        ? mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+                        // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
+                        // animated from the hidden state.
+                        : (mAnimationType != ANIMATION_TYPE_SHOW || mPendingFraction != 0));
 
         // TODO: Implement behavior when inset spans over multiple types
         for (int i = controls.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e665c08..d7cf3e8 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4865,7 +4865,7 @@
         /**
          * @hide
          */
-        public Transaction setDesintationFrame(SurfaceControl sc, @NonNull Rect destinationFrame) {
+        public Transaction setDestinationFrame(SurfaceControl sc, @NonNull Rect destinationFrame) {
             checkPreconditions(sc);
             nativeSetDestinationFrame(mNativeObject, sc.mNativeObject,
                     destinationFrame.left, destinationFrame.top, destinationFrame.right,
@@ -4876,7 +4876,7 @@
         /**
          * @hide
          */
-        public Transaction setDesintationFrame(SurfaceControl sc, int width, int height) {
+        public Transaction setDestinationFrame(SurfaceControl sc, int width, int height) {
             checkPreconditions(sc);
             nativeSetDestinationFrame(mNativeObject, sc.mNativeObject, 0, 0, width, height);
             return this;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b0051ce..780e761 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1125,7 +1125,7 @@
                     }
                 }
 
-                surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
+                surfaceUpdateTransaction.setDestinationFrame(mBlastSurfaceControl, mSurfaceWidth,
                             mSurfaceHeight);
 
                 if (isHardwareAccelerated()) {
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 904b73f..1b77020 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -836,11 +836,16 @@
             } else if (part instanceof Point point) {
                 final float pointWidth = 2 * pointRadius;
                 float start = x - pointRadius;
-                if (start < 0) start = 0;
-                float end = start + pointWidth;
-                if (end > totalWidth) {
+                float end = x + pointRadius;
+                // Only shift the points right at the start/end.
+                // For the points close to the start/end, the segment minimum width requirement
+                // would take care of shifting them to be within the bounds.
+                if (x == 0) {
+                    start = 0;
+                    end = pointWidth;
+                } else if (x == totalWidth) {
+                    start = totalWidth - pointWidth;
                     end = totalWidth;
-                    if (totalWidth > pointWidth) start = totalWidth - pointWidth;
                 }
 
                 drawableParts.add(new DrawablePoint(start, end, point.mColor));
@@ -853,7 +858,7 @@
     private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
             float startX) {
         if (!(prevPart instanceof Point)) return 0F;
-        final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
+        final float pointOffset = (startX == 0) ? pointRadius : 0;
         return pointOffset + pointRadius + segPointGap;
     }
 
@@ -869,9 +874,7 @@
             return segSegGap;
         }
 
-        final float pointWidth = 2 * pointRadius;
-        final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
-                ? (endX + pointRadius - totalWidth) : 0;
+        final float pointOffset = (endX == totalWidth) ? pointRadius : 0;
         return segPointGap + pointRadius + pointOffset;
     }
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8372aec..8bf61bb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7590,7 +7590,7 @@
         <!-- Minimum required drawing width. The drawing width refers to the width after
          the original segments have been adjusted for the neighboring Points and gaps. This is
          enforced by stretching the segments that are too short. -->
-        <attr name="minWidth" format="dimension" />
+        <attr name="minWidth" />
         <!-- Height of the solid segments. -->
         <attr name="height" />
         <!-- Height of the faded segments. -->
diff --git a/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java b/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java
new file mode 100644
index 0000000..e093e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.CpuHeadroomResult;
+import android.hardware.power.GpuHeadroomResult;
+import android.hardware.power.SupportInfo;
+import android.os.health.SystemHealthManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.app.IBatteryStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class SystemHealthManagerUnitTest {
+    @Mock
+    private IBatteryStats mBatteryStats;
+    @Mock
+    private IPowerStatsService mPowerStats;
+    @Mock
+    private IHintManager mHintManager;
+    private SystemHealthManager mSystemHealthManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        IHintManager.HintManagerClientData clientData = new IHintManager.HintManagerClientData();
+        clientData.supportInfo = new SupportInfo();
+        clientData.maxCpuHeadroomThreads = 10;
+        clientData.supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+        clientData.supportInfo.headroom.isCpuSupported = true;
+        clientData.supportInfo.headroom.isGpuSupported = true;
+        clientData.supportInfo.headroom.cpuMinCalculationWindowMillis = 45;
+        clientData.supportInfo.headroom.cpuMaxCalculationWindowMillis = 9999;
+        clientData.supportInfo.headroom.gpuMinCalculationWindowMillis = 46;
+        clientData.supportInfo.headroom.gpuMaxCalculationWindowMillis = 9998;
+        clientData.supportInfo.headroom.cpuMinIntervalMillis = 999;
+        clientData.supportInfo.headroom.gpuMinIntervalMillis = 998;
+        when(mHintManager.getClientData()).thenReturn(clientData);
+        mSystemHealthManager = new SystemHealthManager(mBatteryStats, mPowerStats, mHintManager);
+    }
+
+    @Test
+    public void testHeadroomParamsValueRange() {
+        assertEquals(999, mSystemHealthManager.getCpuHeadroomMinIntervalMillis());
+        assertEquals(998, mSystemHealthManager.getGpuHeadroomMinIntervalMillis());
+        assertEquals(45, (int) mSystemHealthManager.getCpuHeadroomCalculationWindowRange().first);
+        assertEquals(9999,
+                (int) mSystemHealthManager.getCpuHeadroomCalculationWindowRange().second);
+        assertEquals(46, (int) mSystemHealthManager.getGpuHeadroomCalculationWindowRange().first);
+        assertEquals(9998,
+                (int) mSystemHealthManager.getGpuHeadroomCalculationWindowRange().second);
+        assertEquals(10, (int) mSystemHealthManager.getMaxCpuHeadroomTidsSize());
+    }
+
+    @Test
+    public void testGetCpuHeadroom() throws RemoteException, InterruptedException {
+        final CpuHeadroomParams params1 = null;
+        final CpuHeadroomParamsInternal internalParams1 = new CpuHeadroomParamsInternal();
+
+        final CpuHeadroomParams params2 = new CpuHeadroomParams.Builder()
+                .setCalculationWindowMillis(100)
+                .build();
+        final CpuHeadroomParamsInternal internalParams2 = new CpuHeadroomParamsInternal();
+        internalParams2.calculationWindowMillis = 100;
+
+        final CpuHeadroomParams params3 = new CpuHeadroomParams.Builder()
+                .setCalculationType(CpuHeadroomParams.CPU_HEADROOM_CALCULATION_TYPE_AVERAGE)
+                .build();
+        final CpuHeadroomParamsInternal internalParams3 = new CpuHeadroomParamsInternal();
+        internalParams3.calculationType =
+                (byte) CpuHeadroomParams.CPU_HEADROOM_CALCULATION_TYPE_AVERAGE;
+
+        final CpuHeadroomParams params4 = new CpuHeadroomParams.Builder()
+                .setTids(1000, 1001)
+                .build();
+        final CpuHeadroomParamsInternal internalParams4 = new CpuHeadroomParamsInternal();
+        internalParams4.tids = new int[]{1000, 1001};
+
+        when(mHintManager.getCpuHeadroom(internalParams1)).thenReturn(
+                CpuHeadroomResult.globalHeadroom(99f));
+        when(mHintManager.getCpuHeadroom(internalParams2)).thenReturn(
+                CpuHeadroomResult.globalHeadroom(98f));
+        when(mHintManager.getCpuHeadroom(internalParams3)).thenReturn(
+                CpuHeadroomResult.globalHeadroom(97f));
+        when(mHintManager.getCpuHeadroom(internalParams4)).thenReturn(null);
+
+        assertEquals(99f, mSystemHealthManager.getCpuHeadroom(params1), 0.001f);
+        assertEquals(98f, mSystemHealthManager.getCpuHeadroom(params2), 0.001f);
+        assertEquals(97f, mSystemHealthManager.getCpuHeadroom(params3), 0.001f);
+        assertTrue(Float.isNaN(mSystemHealthManager.getCpuHeadroom(params4)));
+        verify(mHintManager, times(1)).getCpuHeadroom(internalParams1);
+        verify(mHintManager, times(1)).getCpuHeadroom(internalParams2);
+        verify(mHintManager, times(1)).getCpuHeadroom(internalParams3);
+        verify(mHintManager, times(1)).getCpuHeadroom(internalParams4);
+    }
+
+    @Test
+    public void testGetGpuHeadroom() throws RemoteException, InterruptedException {
+        final GpuHeadroomParams params1 = null;
+        final GpuHeadroomParamsInternal internalParams1 = new GpuHeadroomParamsInternal();
+        final GpuHeadroomParams params2 = new GpuHeadroomParams.Builder()
+                .setCalculationWindowMillis(100)
+                .build();
+        final GpuHeadroomParamsInternal internalParams2 = new GpuHeadroomParamsInternal();
+        internalParams2.calculationWindowMillis = 100;
+        final GpuHeadroomParams params3 = new GpuHeadroomParams.Builder()
+                .setCalculationType(GpuHeadroomParams.GPU_HEADROOM_CALCULATION_TYPE_AVERAGE)
+                .build();
+        final GpuHeadroomParamsInternal internalParams3 = new GpuHeadroomParamsInternal();
+        internalParams3.calculationType =
+                (byte) GpuHeadroomParams.GPU_HEADROOM_CALCULATION_TYPE_AVERAGE;
+
+        when(mHintManager.getGpuHeadroom(internalParams1)).thenReturn(
+                GpuHeadroomResult.globalHeadroom(99f));
+        when(mHintManager.getGpuHeadroom(internalParams2)).thenReturn(
+                GpuHeadroomResult.globalHeadroom(98f));
+        when(mHintManager.getGpuHeadroom(internalParams3)).thenReturn(null);
+
+        assertEquals(99f, mSystemHealthManager.getGpuHeadroom(params1), 0.001f);
+        assertEquals(98f, mSystemHealthManager.getGpuHeadroom(params2), 0.001f);
+        assertTrue(Float.isNaN(mSystemHealthManager.getGpuHeadroom(params3)));
+        verify(mHintManager, times(1)).getGpuHeadroom(internalParams1);
+        verify(mHintManager, times(1)).getGpuHeadroom(internalParams2);
+        verify(mHintManager, times(1)).getGpuHeadroom(internalParams3);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 5df2c12..9818e19 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -336,10 +336,14 @@
                 progress, progressMax);
 
         List<Part> expectedParts = new ArrayList<>(
-                List.of(new Segment(0.15f, Color.BLUE), new Point(Color.RED),
-                        new Segment(0.10f, Color.BLUE), new Point(Color.BLUE),
-                        new Segment(0.35f, Color.BLUE), new Point(Color.BLUE),
-                        new Segment(0.15f, Color.BLUE), new Point(Color.YELLOW),
+                List.of(new Segment(0.15f, Color.BLUE),
+                        new Point(Color.RED),
+                        new Segment(0.10f, Color.BLUE),
+                        new Point(Color.BLUE),
+                        new Segment(0.35f, Color.BLUE),
+                        new Point(Color.BLUE),
+                        new Segment(0.15f, Color.BLUE),
+                        new Point(Color.YELLOW),
                         new Segment(0.25f, Color.BLUE)));
 
         assertThat(parts).isEqualTo(expectedParts);
@@ -408,11 +412,16 @@
                 progress, progressMax);
 
         List<Part> expectedParts = new ArrayList<>(
-                List.of(new Segment(0.15f, Color.RED), new Point(Color.RED),
-                        new Segment(0.10f, Color.RED), new Point(Color.BLUE),
-                        new Segment(0.25f, Color.RED), new Segment(0.10f, Color.GREEN),
-                        new Point(Color.BLUE), new Segment(0.15f, Color.GREEN),
-                        new Point(Color.YELLOW), new Segment(0.25f, Color.GREEN)));
+                List.of(new Segment(0.15f, Color.RED),
+                        new Point(Color.RED),
+                        new Segment(0.10f, Color.RED),
+                        new Point(Color.BLUE),
+                        new Segment(0.25f, Color.RED),
+                        new Segment(0.10f, Color.GREEN),
+                        new Point(Color.BLUE),
+                        new Segment(0.15f, Color.GREEN),
+                        new Point(Color.YELLOW),
+                        new Segment(0.25f, Color.GREEN)));
 
         assertThat(parts).isEqualTo(expectedParts);
 
@@ -464,6 +473,158 @@
     }
 
     @Test
+    public void processAndConvertToParts_multipleSegmentsWithPointsAtStartAndEnd() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(0).setColor(Color.RED));
+        points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(100).setColor(Color.YELLOW));
+        int progress = 60;
+        int progressMax = 100;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(
+                List.of(new Point(Color.RED),
+                        new Segment(0.25f, Color.RED),
+                        new Point(Color.BLUE),
+                        new Segment(0.25f, Color.RED),
+                        new Segment(0.10f, Color.GREEN),
+                        new Point(Color.BLUE),
+                        new Segment(0.4f, Color.GREEN),
+                        new Point(Color.YELLOW)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+        List<DrawablePart> drawableParts = NotificationProgressBar.processAndConvertToDrawableParts(
+                parts, drawableWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon);
+
+        List<DrawablePart> expectedDrawableParts = new ArrayList<>(
+                List.of(new DrawablePoint(0, 12, Color.RED),
+                        new DrawableSegment(16, 65, Color.RED),
+                        new DrawablePoint(69, 81, Color.BLUE),
+                        new DrawableSegment(85, 146, Color.RED),
+                        new DrawableSegment(150, 170, Color.GREEN),
+                        new DrawablePoint(174, 186, Color.BLUE),
+                        new DrawableSegment(190, 284, Color.GREEN),
+                        new DrawablePoint(288, 300, Color.YELLOW)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = true;
+
+        Pair<List<DrawablePart>, Float> p = NotificationProgressBar.maybeStretchAndRescaleSegments(
+                parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
+                300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        // Colors with 40% opacity
+        int fadedGreen = 0x6600FF00;
+        int fadedYellow = 0x66FFFF00;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new DrawablePoint(0, 12, Color.RED),
+                        new DrawableSegment(16, 65, Color.RED),
+                        new DrawablePoint(69, 81, Color.BLUE),
+                        new DrawableSegment(85, 146, Color.RED),
+                        new DrawableSegment(150, 170, Color.GREEN),
+                        new DrawablePoint(174, 186, Color.BLUE),
+                        new DrawableSegment(190, 284, fadedGreen, true),
+                        new DrawablePoint(288, 300, fadedYellow)));
+
+        assertThat(p.second).isEqualTo(180);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
+    }
+
+    // The points are so close to start/end that they would go out of bounds without the minimum
+    // segment width requirement.
+    @Test
+    public void processAndConvertToParts_multipleSegmentsWithPointsNearStartAndEnd() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(1).setColor(Color.RED));
+        points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(99).setColor(Color.YELLOW));
+        int progress = 60;
+        int progressMax = 100;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(
+                List.of(new Segment(0.01f, Color.RED),
+                        new Point(Color.RED),
+                        new Segment(0.24f, Color.RED),
+                        new Point(Color.BLUE),
+                        new Segment(0.25f, Color.RED),
+                        new Segment(0.10f, Color.GREEN),
+                        new Point(Color.BLUE),
+                        new Segment(0.39f, Color.GREEN),
+                        new Point(Color.YELLOW),
+                        new Segment(0.01f, Color.GREEN)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+        List<DrawablePart> drawableParts = NotificationProgressBar.processAndConvertToDrawableParts(
+                parts, drawableWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon);
+
+        List<DrawablePart> expectedDrawableParts = new ArrayList<>(
+                List.of(new DrawableSegment(0, -7, Color.RED),
+                        new DrawablePoint(-3, 9, Color.RED),
+                        new DrawableSegment(13, 65, Color.RED),
+                        new DrawablePoint(69, 81, Color.BLUE),
+                        new DrawableSegment(85, 146, Color.RED),
+                        new DrawableSegment(150, 170, Color.GREEN),
+                        new DrawablePoint(174, 186, Color.BLUE),
+                        new DrawableSegment(190, 287, Color.GREEN),
+                        new DrawablePoint(291, 303, Color.YELLOW),
+                        new DrawableSegment(307, 300, Color.GREEN)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = true;
+
+        Pair<List<DrawablePart>, Float> p = NotificationProgressBar.maybeStretchAndRescaleSegments(
+                parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
+                300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        // Colors with 40% opacity
+        int fadedGreen = 0x6600FF00;
+        int fadedYellow = 0x66FFFF00;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new DrawableSegment(0, 16, Color.RED),
+                        new DrawablePoint(20, 32, Color.RED),
+                        new DrawableSegment(36, 78.02409F, Color.RED),
+                        new DrawablePoint(82.02409F, 94.02409F, Color.BLUE),
+                        new DrawableSegment(98.02409F, 146.55421F, Color.RED),
+                        new DrawableSegment(150.55421F, 169.44579F, Color.GREEN),
+                        new DrawablePoint(173.44579F, 185.44579F, Color.BLUE),
+                        new DrawableSegment(189.44579F, 264, fadedGreen, true),
+                        new DrawablePoint(268, 280, fadedYellow),
+                        new DrawableSegment(284, 300, fadedGreen, true)));
+
+        assertThat(p.second).isEqualTo(179.44579F);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
+    }
+
+    @Test
     public void processAndConvertToParts_multipleSegmentsWithPoints_notStyledByProgress() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 7823277..3403bbf 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -134,4 +134,9 @@
     <install-in-user-type package="com.android.avatarpicker">
         <install-in user-type="FULL" />
     </install-in-user-type>
+
+    <!-- Users Widget (Users widget)-->
+    <install-in-user-type package="com.android.multiuser">
+        <install-in user-type="FULL" />
+    </install-in-user-type>
 </config>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
index 8cd7b0f..82ef00e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.appzoomout;
 
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.ActivityManager;
@@ -100,6 +101,7 @@
 
         mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
         mDisplayController.addDisplayChangingController(this);
+        updateDisplayLayout(mContext.getDisplayId());
 
         mDisplayAreaOrganizer.registerOrganizer();
     }
@@ -135,7 +137,9 @@
     public void onDisplayChange(int displayId, int fromRotation, int toRotation,
             @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
         // TODO: verify if there is synchronization issues.
-        mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+        if (toRotation != ROTATION_UNDEFINED) {
+            mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
new file mode 100644
index 0000000..67592e6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.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.wm.shell.common
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
+import com.android.wm.shell.ShellTaskOrganizer
+
+/** Utils to obtain [ComponentName]s. */
+object ComponentUtils {
+    /** Retrieves the package name from an [Intent].  */
+    @JvmStatic
+    fun getPackageName(intent: Intent?): String? = intent?.component?.packageName
+
+    /** Retrieves the package name from a [PendingIntent].  */
+    @JvmStatic
+    fun getPackageName(pendingIntent: PendingIntent?): String? =
+        getPackageName(pendingIntent?.intent)
+
+    /** Retrieves the package name from a [taskId].  */
+    @JvmStatic
+    fun getPackageName(taskId: Int, taskOrganizer: ShellTaskOrganizer): String? {
+        val taskInfo = taskOrganizer.getRunningTaskInfo(taskId)
+        return getPackageName(taskInfo?.baseIntent)
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 9113c0a..83e5e31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -27,14 +27,10 @@
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 
 import android.app.ActivityManager;
-import android.app.PendingIntent;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
 
-import androidx.annotation.Nullable;
-
 import com.android.internal.util.ArrayUtils;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -65,31 +61,6 @@
                 && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
     }
 
-    /** Retrieve package name from an intent */
-    @Nullable
-    public static String getPackageName(Intent intent) {
-        if (intent == null || intent.getComponent() == null) {
-            return null;
-        }
-        return intent.getComponent().getPackageName();
-    }
-
-    /** Retrieve package name from a PendingIntent */
-    @Nullable
-    public static String getPackageName(PendingIntent pendingIntent) {
-        if (pendingIntent == null || pendingIntent.getIntent() == null) {
-            return null;
-        }
-        return getPackageName(pendingIntent.getIntent());
-    }
-
-    /** Retrieve package name from a taskId */
-    @Nullable
-    public static String getPackageName(int taskId, ShellTaskOrganizer taskOrganizer) {
-        final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
-        return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
-    }
-
     /** Retrieve user id from a taskId */
     public static int getUserId(int taskId, ShellTaskOrganizer taskOrganizer) {
         final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
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 e8add56..ac510f8 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
@@ -946,8 +946,7 @@
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopModeUiEventLogger desktopModeUiEventLogger,
-            WindowDecorTaskResourceLoader taskResourceLoader,
-            RecentsTransitionHandler recentsTransitionHandler
+            WindowDecorTaskResourceLoader taskResourceLoader
     ) {
         if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
             return Optional.empty();
@@ -963,7 +962,7 @@
                 desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
                 windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
                 focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
-                taskResourceLoader, recentsTransitionHandler));
+                taskResourceLoader));
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index bd676ce..bba778d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -68,12 +68,12 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipMenuController;
 import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.shared.pip.PipContentOverlay;
@@ -1359,7 +1359,7 @@
     public boolean isPackageActiveInPip(@Nullable String packageName) {
         final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
         return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
-                && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+                && packageName.equals(ComponentUtils.getPackageName(inPipTask.baseIntent));
     }
 
     private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 38015ca..0a42c71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -57,12 +57,12 @@
 import com.android.internal.util.Preconditions;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipMenuController;
 import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.desktopmode.DesktopRepository;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
@@ -1008,6 +1008,6 @@
     public boolean isPackageActiveInPip(@Nullable String packageName) {
         final TaskInfo inPipTask = mPipTransitionState.getPipTaskInfo();
         return packageName != null && inPipTask != null && mPipTransitionState.isInPip()
-                && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+                && packageName.equals(ComponentUtils.getPackageName(inPipTask.baseIntent));
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
index 8cdb8c4..32c79a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
@@ -17,10 +17,9 @@
 package com.android.wm.shell.recents;
 
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.view.RemoteAnimationTarget;
 import android.window.TaskSnapshot;
-import android.window.TransitionInfo;
+import android.os.Bundle;
 
 import com.android.wm.shell.recents.IRecentsAnimationController;
 
@@ -58,8 +57,7 @@
      */
     void onAnimationStart(in IRecentsAnimationController controller,
             in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
-            in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras,
-            in TransitionInfo info) = 2;
+            in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras) = 2;
 
     /**
      * Called when the task of an activity that has been started while the recents animation
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index d6f9183..975b650 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -103,6 +103,7 @@
     private final RecentTasksImpl mImpl = new RecentTasksImpl();
     private final ActivityTaskManager mActivityTaskManager;
     private final TaskStackTransitionObserver mTaskStackTransitionObserver;
+    private final RecentsShellCommandHandler mRecentsShellCommandHandler;
     private RecentsTransitionHandler mTransitionHandler = null;
     private IRecentTasksListener mListener;
     private final boolean mPcFeatureEnabled;
@@ -167,6 +168,7 @@
         mDesktopUserRepositories = desktopUserRepositories;
         mTaskStackTransitionObserver = taskStackTransitionObserver;
         mMainExecutor = mainExecutor;
+        mRecentsShellCommandHandler = new RecentsShellCommandHandler(this);
         shellInit.addInitCallback(this::onInit, this);
     }
 
@@ -183,6 +185,7 @@
         mShellController.addExternalInterface(IRecentTasks.DESCRIPTOR,
                 this::createExternalInterface, this);
         mShellCommandHandler.addDumpCallback(this::dump, this);
+        mShellCommandHandler.addCommandCallback("recents", mRecentsShellCommandHandler, this);
         mUserId = ActivityManager.getCurrentUser();
         mDesktopUserRepositories.ifPresent(
                 desktopUserRepositories ->
@@ -656,6 +659,11 @@
         return mActivityTaskManager.removeTask(taskId);
     }
 
+    /** Removes all recent tasks that are visible. */
+    public void removeAllVisibleRecentTasks() throws RemoteException {
+        ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+    }
+
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt
new file mode 100644
index 0000000..f786e07
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.recents
+
+import android.os.RemoteException
+import com.android.wm.shell.sysui.ShellCommandHandler.ShellCommandActionHandler
+import java.io.PrintWriter
+
+class RecentsShellCommandHandler(
+    private val recentTasksController: RecentTasksController
+) : ShellCommandActionHandler {
+    override fun onShellCommand(args: Array<out String>, pw: PrintWriter): Boolean {
+        when (args[0]) {
+            "clearAll" -> return runClearAll(pw)
+            else -> {
+                pw.println("Invalid command: " + args[0])
+                return false
+            }
+        }
+    }
+
+    override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
+        pw.println("${prefix}clearAll")
+        pw.println("$prefix  Clears all visible recent tasks.")
+    }
+
+    private fun runClearAll(pw: PrintWriter): Boolean {
+        try {
+            recentTasksController.removeAllVisibleRecentTasks()
+        } catch (e: RemoteException) {
+            pw.println("Exception while removing visible recent tasks:")
+            e.printStackTrace(pw)
+            return false
+        }
+        return true
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index aeccd86..db582aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -587,8 +587,7 @@
                 mListener.onAnimationStart(this,
                         apps.toArray(new RemoteAnimationTarget[apps.size()]),
                         new RemoteAnimationTarget[0],
-                        new Rect(0, 0, 0, 0), new Rect(), new Bundle(),
-                        null);
+                        new Rect(0, 0, 0, 0), new Rect(), new Bundle());
                 for (int i = 0; i < mStateListeners.size(); i++) {
                     mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING);
                 }
@@ -819,7 +818,7 @@
                 mListener.onAnimationStart(this,
                         apps.toArray(new RemoteAnimationTarget[apps.size()]),
                         wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
-                        new Rect(0, 0, 0, 0), new Rect(), b, info);
+                        new Rect(0, 0, 0, 0), new Rect(), b);
                 for (int i = 0; i < mStateListeners.size(); i++) {
                     mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING);
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c724135..9e88a26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -82,6 +82,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -682,7 +683,7 @@
         final String packageName1 = shortcutInfo.getPackage();
         // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
         //       recents that hasn't launched and is not being organized
-        final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+        final String packageName2 = ComponentUtils.getPackageName(taskId, mTaskOrganizer);
         final int userId1 = shortcutInfo.getUserId();
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
@@ -727,10 +728,10 @@
             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         Intent fillInIntent = null;
-        final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+        final String packageName1 = ComponentUtils.getPackageName(pendingIntent);
         // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
         //       recents that hasn't launched and is not being organized
-        final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+        final String packageName2 = ComponentUtils.getPackageName(taskId, mTaskOrganizer);
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
@@ -766,8 +767,8 @@
             InstanceId instanceId) {
         Intent fillInIntent1 = null;
         Intent fillInIntent2 = null;
-        final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
-        final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+        final String packageName1 = ComponentUtils.getPackageName(pendingIntent1);
+        final String packageName2 = ComponentUtils.getPackageName(pendingIntent2);
         final ActivityOptions activityOptions1 = options1 != null
                 ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
         final ActivityOptions activityOptions2 = options2 != null
@@ -835,7 +836,7 @@
         if (fillInIntent == null) fillInIntent = new Intent();
         fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
 
-        final String packageName1 = SplitScreenUtils.getPackageName(intent);
+        final String packageName1 = ComponentUtils.getPackageName(intent);
         final String packageName2 = getPackageName(reverseSplitPosition(position), hideTaskToken);
         final int userId2 = getUserId(reverseSplitPosition(position), hideTaskToken);
         final ComponentName component = intent.getIntent().getComponent();
@@ -900,7 +901,7 @@
             }
         }
 
-        return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
+        return taskInfo != null ? ComponentUtils.getPackageName(taskInfo.baseIntent) : null;
     }
 
     /**
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 b6bd879..c27ef29 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
@@ -131,6 +131,7 @@
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -140,7 +141,6 @@
 import com.android.wm.shell.common.split.OffscreenTouchZone;
 import com.android.wm.shell.common.split.SplitDecorManager;
 import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.common.split.SplitState;
 import com.android.wm.shell.common.split.SplitWindowManager;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -3536,12 +3536,12 @@
         if (mSplitRequest.mActivateTaskId == taskInfo.taskId) {
             return mSplitRequest.mActivatePosition;
         }
-        final String packageName1 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent);
-        final String basePackageName = SplitScreenUtils.getPackageName(taskInfo.baseIntent);
+        final String packageName1 = ComponentUtils.getPackageName(mSplitRequest.mStartIntent);
+        final String basePackageName = ComponentUtils.getPackageName(taskInfo.baseIntent);
         if (packageName1 != null && packageName1.equals(basePackageName)) {
             return mSplitRequest.mActivatePosition;
         }
-        final String packageName2 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent2);
+        final String packageName2 = ComponentUtils.getPackageName(mSplitRequest.mStartIntent2);
         if (packageName2 != null && packageName2.equals(basePackageName)) {
             return mSplitRequest.mActivatePosition;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 8dd1498..5c7dd07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -536,9 +536,12 @@
             }
             return;
         }
+        // Cache it to avoid NPE and make sure to remove it from recents history.
+        // mTaskToken can be cleared in onTaskVanished() when the task is removed.
+        final WindowContainerToken taskToken = mTaskToken;
         mShellExecutor.execute(() -> {
             WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.removeTask(mTaskToken);
+            wct.removeTask(taskToken);
             mTaskViewTransitions.closeTaskView(wct, this);
         });
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 41c0a11..2177986 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -43,7 +43,7 @@
 import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
-import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.ComponentUtils;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.pip.PipTransitionController;
@@ -645,7 +645,7 @@
         // task enter split.
         if (mPipHandler != null) {
             return mPipHandler
-                    .isPackageActiveInPip(SplitScreenUtils.getPackageName(intent.getIntent()));
+                    .isPackageActiveInPip(ComponentUtils.getPackageName(intent.getIntent()));
         }
         return false;
     }
@@ -657,7 +657,7 @@
         // task enter split.
         if (mPipHandler != null) {
             return mPipHandler.isPackageActiveInPip(
-                    SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer));
+                    ComponentUtils.getPackageName(taskId, shellTaskOrganizer));
         }
         return false;
     }
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 429e056..9fbda46 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
@@ -126,8 +126,6 @@
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
 import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
-import com.android.wm.shell.recents.RecentsTransitionStateListener;
 import com.android.wm.shell.shared.FocusTransitionListener;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -159,10 +157,8 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -251,7 +247,6 @@
     private final DesktopModeEventLogger mDesktopModeEventLogger;
     private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
     private final WindowDecorTaskResourceLoader mTaskResourceLoader;
-    private final RecentsTransitionHandler mRecentsTransitionHandler;
 
     public DesktopModeWindowDecorViewModel(
             Context context,
@@ -287,8 +282,7 @@
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopModeUiEventLogger desktopModeUiEventLogger,
-            WindowDecorTaskResourceLoader taskResourceLoader,
-            RecentsTransitionHandler recentsTransitionHandler) {
+            WindowDecorTaskResourceLoader taskResourceLoader) {
         this(
                 context,
                 shellExecutor,
@@ -329,8 +323,7 @@
                 focusTransitionObserver,
                 desktopModeEventLogger,
                 desktopModeUiEventLogger,
-                taskResourceLoader,
-                recentsTransitionHandler);
+                taskResourceLoader);
     }
 
     @VisibleForTesting
@@ -374,8 +367,7 @@
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopModeUiEventLogger desktopModeUiEventLogger,
-            WindowDecorTaskResourceLoader taskResourceLoader,
-            RecentsTransitionHandler recentsTransitionHandler) {
+            WindowDecorTaskResourceLoader taskResourceLoader) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -444,7 +436,6 @@
         mDesktopModeEventLogger = desktopModeEventLogger;
         mDesktopModeUiEventLogger = desktopModeUiEventLogger;
         mTaskResourceLoader = taskResourceLoader;
-        mRecentsTransitionHandler = recentsTransitionHandler;
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -459,10 +450,6 @@
                 new DesktopModeOnTaskResizeAnimationListener());
         mDesktopTasksController.setOnTaskRepositionAnimationListener(
                 new DesktopModeOnTaskRepositionAnimationListener());
-        if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
-            mRecentsTransitionHandler.addTransitionStateListener(
-                    new DesktopModeRecentsTransitionStateListener());
-        }
         mDisplayController.addDisplayChangingController(mOnDisplayChangingListener);
         try {
             mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
@@ -1872,38 +1859,6 @@
         }
     }
 
-    private class DesktopModeRecentsTransitionStateListener
-            implements RecentsTransitionStateListener {
-        final Set<Integer> mAnimatingTaskIds = new HashSet<>();
-
-        @Override
-        public void onTransitionStateChanged(int state) {
-            switch (state) {
-                case RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED:
-                    for (int n = 0; n < mWindowDecorByTaskId.size(); n++) {
-                        int taskId = mWindowDecorByTaskId.keyAt(n);
-                        mAnimatingTaskIds.add(taskId);
-                        setIsRecentsTransitionRunningForTask(taskId, true);
-                    }
-                    return;
-                case RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING:
-                    // No Recents transition running - clean up window decorations
-                    for (int taskId : mAnimatingTaskIds) {
-                        setIsRecentsTransitionRunningForTask(taskId, false);
-                    }
-                    mAnimatingTaskIds.clear();
-                    return;
-                default:
-            }
-        }
-
-        private void setIsRecentsTransitionRunningForTask(int taskId, boolean isRecentsRunning) {
-            final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
-            if (decoration == null) return;
-            decoration.setIsRecentsTransitionRunning(isRecentsRunning);
-        }
-    }
-
     private class DragEventListenerImpl
             implements DragPositioningCallbackUtility.DragEventListener {
         @Override
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 39a989c..4ac8954 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
@@ -204,7 +204,6 @@
     private final MultiInstanceHelper mMultiInstanceHelper;
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final DesktopUserRepositories mDesktopUserRepositories;
-    private boolean mIsRecentsTransitionRunning = false;
 
     private Runnable mLoadAppInfoRunnable;
     private Runnable mSetAppInfoRunnable;
@@ -499,7 +498,7 @@
                 applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
                 mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
                 mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
-                displayExclusionRegion, mIsRecentsTransitionRunning);
+                displayExclusionRegion);
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -870,8 +869,7 @@
             boolean inFullImmersiveMode,
             @NonNull InsetsState displayInsetsState,
             boolean hasGlobalFocus,
-            @NonNull Region displayExclusionRegion,
-            boolean shouldIgnoreCornerRadius) {
+            @NonNull Region displayExclusionRegion) {
         final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
         final boolean isAppHeader =
                 captionLayoutId == R.layout.desktop_mode_app_header;
@@ -1008,19 +1006,13 @@
         relayoutParams.mWindowDecorConfig = windowDecorConfig;
 
         if (DesktopModeStatus.useRoundedCorners()) {
-            relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
-                    getCornerRadius(context, relayoutParams.mLayoutResId);
+            relayoutParams.mCornerRadius = taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+                    ? loadDimensionPixelSize(context.getResources(),
+                    R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+                    : INVALID_CORNER_RADIUS;
         }
     }
 
-    private static int getCornerRadius(@NonNull Context context, int layoutResId) {
-        if (layoutResId == R.layout.desktop_mode_app_header) {
-            return loadDimensionPixelSize(context.getResources(),
-                    R.dimen.desktop_windowing_freeform_rounded_corner_radius);
-        }
-        return INVALID_CORNER_RADIUS;
-    }
-
     /**
      * If task has focused window decor, return the caption id of the fullscreen caption size
      * resource. Otherwise, return ID_NULL and caption width be set to task width.
@@ -1748,17 +1740,6 @@
     }
 
     /**
-     * Declares whether a Recents transition is currently active.
-     *
-     * <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.
-     */
-    void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
-        mIsRecentsTransitionRunning = isRecentsTransitionRunning;
-    }
-
-    /**
      * Called when there is a {@link MotionEvent#ACTION_HOVER_EXIT} on the maximize window button.
      */
     void onMaximizeButtonHoverExit() {
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 fa7183ad..5d1bedb 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
@@ -967,4 +967,4 @@
             return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt
new file mode 100644
index 0000000..2b26bbf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.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.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopWithDragExistingWindows
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [EnterDesktopWithDragExistingWindows]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopWithDragExistingWindowsTest : EnterDesktopWithDragExistingWindows()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt
new file mode 100644
index 0000000..2de0830
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithAppHandleMenuExistingWindows {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val imeApp = ImeAppHelper(instrumentation)
+    private val newTaskApp = NewTasksAppHelper(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopMode(wmHelper, device)
+        imeApp.launchViaIntent(wmHelper)
+        newTaskApp.launchViaIntent(wmHelper)
+        testApp.launchViaIntent(wmHelper)
+        testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+    }
+
+    @Test
+    open fun reenterDesktopWithAppHandleMenu() {
+        testApp.enterDesktopModeFromAppHandleMenu(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        newTaskApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt
new file mode 100644
index 0000000..814478a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.scenarios
+
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class EnterDesktopWithDragExistingWindows
+constructor(
+    val rotation: Rotation = Rotation.ROTATION_0,
+    isResizeable: Boolean = true,
+    isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+    private val imeApp = ImeAppHelper(instrumentation)
+    private val newTaskApp = NewTasksAppHelper(instrumentation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+        tapl.enableTransientTaskbar(false)
+
+        testApp.enterDesktopMode(wmHelper, device)
+        imeApp.launchViaIntent(wmHelper)
+        newTaskApp.launchViaIntent(wmHelper)
+        testApp.launchViaIntent(wmHelper)
+        testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+    }
+
+    @Test
+    open fun reenterDesktopWithDrag() {
+        // By default this method uses drag to desktop
+        testApp.enterDesktopMode(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        newTaskApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
index ba46542..31d89f9 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
@@ -24,6 +24,7 @@
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
@@ -61,7 +62,6 @@
 
     @After
     fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
+        RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
index d774a31..1af6cac 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -24,6 +24,7 @@
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
@@ -74,7 +75,6 @@
 
     @After
     fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
+        RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
index 5aa1619..8ad8c7b 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
@@ -24,6 +24,7 @@
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
@@ -60,7 +61,6 @@
 
     @After
     fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
+        RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
index 668f367..da0ace4 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
@@ -24,6 +24,7 @@
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
@@ -61,7 +62,6 @@
 
     @After
     fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
+        RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
new file mode 100644
index 0000000..aa262f9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.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.wm.shell.flicker.utils
+
+import android.app.Instrumentation
+
+object RecentTasksUtils {
+    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
+        instrumentation.uiAutomation.executeShellCommand(
+            "dumpsys activity service SystemUIService WMShell recents clearAll"
+        )
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index ab43119..894d238 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -169,7 +169,7 @@
         final IResultReceiver finishCallback = mock(IResultReceiver.class);
 
         final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
-        verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any(), any());
+        verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
 
         // Finish and verify no transition remains and that the provided finish callback is called
         mRecentsTransitionHandler.findController(transition).finish(true /* toHome */,
@@ -184,7 +184,7 @@
         final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class);
 
         final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
-        verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any(), any());
+        verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
 
         mRecentsTransitionHandler.findController(transition).cancel("test");
         mMainExecutor.flushAll();
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 79e9b9c..ffe8e71 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
@@ -59,12 +59,11 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
 import com.android.wm.shell.desktopmode.DesktopImmersiveController
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
-import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
-import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
 import com.android.wm.shell.splitscreen.SplitScreenController
@@ -540,8 +539,7 @@
         onLeftSnapClickListenerCaptor.value.invoke()
 
         verify(mockDesktopTasksController, never())
-            .snapToHalfScreen(
-                eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
+            .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
                 eq(ResizeTrigger.MAXIMIZE_BUTTON),
                 eq(InputMethod.UNKNOWN_INPUT_METHOD),
                 eq(decor),
@@ -618,12 +616,11 @@
         onRightSnapClickListenerCaptor.value.invoke()
 
         verify(mockDesktopTasksController, never())
-            .snapToHalfScreen(
-                eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
+            .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
                 eq(ResizeTrigger.MAXIMIZE_BUTTON),
                 eq(InputMethod.UNKNOWN_INPUT_METHOD),
                 eq(decor),
-            )
+        )
     }
 
     @Test
@@ -1226,49 +1223,6 @@
         verify(task2, never()).onExclusionRegionChanged(newRegion)
     }
 
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
-    fun testRecentsTransitionStateListener_requestedState_setsTransitionRunning() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration = setUpMockDecorationForTask(task)
-        onTaskOpening(task, SurfaceControl())
-
-        desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
-            RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-
-        verify(decoration).setIsRecentsTransitionRunning(true)
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
-    fun testRecentsTransitionStateListener_nonRunningState_setsTransitionNotRunning() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration = setUpMockDecorationForTask(task)
-        onTaskOpening(task, SurfaceControl())
-        desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
-            RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-
-        desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
-            RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING)
-
-        verify(decoration).setIsRecentsTransitionRunning(false)
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
-    fun testRecentsTransitionStateListener_requestedAndAnimating_setsTransitionRunningOnce() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration = setUpMockDecorationForTask(task)
-        onTaskOpening(task, SurfaceControl())
-
-        desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
-            RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-        desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
-            RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING)
-
-        verify(decoration, times(1)).setIsRecentsTransitionRunning(true)
-    }
-
     private fun createOpenTaskDecoration(
         @WindowingMode windowingMode: Int,
         taskSurface: SurfaceControl = SurfaceControl(),
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 8af8285..b5e8ceb 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
@@ -40,7 +40,6 @@
 import android.view.WindowInsets.Type.statusBars
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
@@ -66,8 +65,6 @@
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController
 import com.android.wm.shell.desktopmode.education.AppToWebEducationController
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
-import com.android.wm.shell.recents.RecentsTransitionHandler
-import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -154,7 +151,6 @@
     protected val mockFocusTransitionObserver = mock<FocusTransitionObserver>()
     protected val mockCaptionHandleRepository = mock<WindowDecorCaptionHandleRepository>()
     protected val mockDesktopRepository: DesktopRepository = mock<DesktopRepository>()
-    protected val mockRecentsTransitionHandler = mock<RecentsTransitionHandler>()
     protected val motionEvent = mock<MotionEvent>()
     val displayLayout = mock<DisplayLayout>()
     protected lateinit var spyContext: TestableContext
@@ -168,7 +164,6 @@
     protected lateinit var mockitoSession: StaticMockitoSession
     protected lateinit var shellInit: ShellInit
     internal lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
-    protected lateinit var desktopModeRecentsTransitionStateListener: RecentsTransitionStateListener
     protected lateinit var displayChangingListener:
             DisplayChangeController.OnDisplayChangingListener
     internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener
@@ -224,8 +219,7 @@
             mockFocusTransitionObserver,
             desktopModeEventLogger,
             mock<DesktopModeUiEventLogger>(),
-            mock<WindowDecorTaskResourceLoader>(),
-            mockRecentsTransitionHandler,
+            mock<WindowDecorTaskResourceLoader>()
         )
         desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
         whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -262,13 +256,6 @@
         verify(displayInsetsController)
             .addGlobalInsetsChangedListener(insetsChangedCaptor.capture())
         desktopModeOnInsetsChangedListener = insetsChangedCaptor.firstValue
-        val recentsTransitionStateListenerCaptor = argumentCaptor<RecentsTransitionStateListener>()
-        if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
-            verify(mockRecentsTransitionHandler)
-                .addTransitionStateListener(recentsTransitionStateListenerCaptor.capture())
-            desktopModeRecentsTransitionStateListener =
-                recentsTransitionStateListenerCaptor.firstValue
-        }
         val keyguardChangedCaptor =
             argumentCaptor<DesktopModeKeyguardChangeListener>()
         verify(mockShellController).addKeyguardChangeListener(keyguardChangedCaptor.capture())
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 9ea5fd6..6b02aef 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
@@ -169,7 +169,6 @@
     private static final boolean DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED = false;
     private static final boolean DEFAULT_IS_IN_FULL_IMMERSIVE_MODE = false;
     private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
-    private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
@@ -397,31 +396,6 @@
     }
 
     @Test
-    public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
-        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
-        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        fillRoundedCornersResources(/* fillValue= */ 30);
-        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,
-                new InsetsState(),
-                DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                /* shouldIgnoreCornerRadius= */ true);
-
-        assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
-    }
-
-    @Test
     @EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
     public void updateRelayoutParams_appHeader_usesTaskDensity() {
         final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
@@ -660,8 +634,7 @@
                 /* inFullImmersiveMode */ true,
                 insetsState,
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         // Takes status bar inset as padding, ignores caption bar inset.
         assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -686,8 +659,7 @@
                 /* inFullImmersiveMode */ true,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsInsetSource).isFalse();
     }
@@ -711,8 +683,7 @@
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         // Header is always shown because it's assumed the status bar is always visible.
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -736,8 +707,7 @@
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
     }
@@ -760,8 +730,7 @@
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -784,8 +753,7 @@
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -809,8 +777,7 @@
                 /* inFullImmersiveMode */ true,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
 
@@ -826,8 +793,7 @@
                 /* inFullImmersiveMode */ true,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -851,8 +817,7 @@
                 /* inFullImmersiveMode */ true,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -1515,8 +1480,7 @@
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
-                mExclusionRegion,
-                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+                mExclusionRegion);
     }
 
     private DesktopModeWindowDecoration createWindowDecoration(
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 191b938..aeb028c 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -54,14 +54,17 @@
     private final IMediaQualityManager mService;
     private final Context mContext;
     private final UserHandle mUserHandle;
-    private final Object mLock = new Object();
-    // @GuardedBy("mLock")
+    private final Object mPpLock = new Object();
+    private final Object mSpLock = new Object();
+    private final Object mAbLock = new Object();
+    private final Object mApLock = new Object();
+    // @GuardedBy("mPpLock")
     private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
-    // @GuardedBy("mLock")
+    // @GuardedBy("mSpLock")
     private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
-    // @GuardedBy("mLock")
+    // @GuardedBy("mAbLock")
     private final List<AmbientBacklightCallbackRecord> mAbCallbackRecords = new ArrayList<>();
-    // @GuardedBy("mLock")
+    // @GuardedBy("mApLock")
     private final List<ActiveProcessingPictureListenerRecord> mApListenerRecords =
             new ArrayList<>();
 
@@ -82,7 +85,7 @@
         IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
             @Override
             public void onPictureProfileAdded(String profileId, PictureProfile profile) {
-                synchronized (mLock) {
+                synchronized (mPpLock) {
                     for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
                         // TODO: filter callback record
                         record.postPictureProfileAdded(profileId, profile);
@@ -91,7 +94,7 @@
             }
             @Override
             public void onPictureProfileUpdated(String profileId, PictureProfile profile) {
-                synchronized (mLock) {
+                synchronized (mPpLock) {
                     for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
                         // TODO: filter callback record
                         record.postPictureProfileUpdated(profileId, profile);
@@ -100,7 +103,7 @@
             }
             @Override
             public void onPictureProfileRemoved(String profileId, PictureProfile profile) {
-                synchronized (mLock) {
+                synchronized (mPpLock) {
                     for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
                         // TODO: filter callback record
                         record.postPictureProfileRemoved(profileId, profile);
@@ -110,7 +113,7 @@
             @Override
             public void onParameterCapabilitiesChanged(
                     String profileId, List<ParameterCapability> caps) {
-                synchronized (mLock) {
+                synchronized (mPpLock) {
                     for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
                         // TODO: filter callback record
                         record.postParameterCapabilitiesChanged(profileId, caps);
@@ -119,7 +122,7 @@
             }
             @Override
             public void onError(String profileId, int err) {
-                synchronized (mLock) {
+                synchronized (mPpLock) {
                     for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
                         // TODO: filter callback record
                         record.postError(profileId, err);
@@ -130,7 +133,7 @@
         ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
             @Override
             public void onSoundProfileAdded(String profileId, SoundProfile profile) {
-                synchronized (mLock) {
+                synchronized (mSpLock) {
                     for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
                         // TODO: filter callback record
                         record.postSoundProfileAdded(profileId, profile);
@@ -139,7 +142,7 @@
             }
             @Override
             public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
-                synchronized (mLock) {
+                synchronized (mSpLock) {
                     for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
                         // TODO: filter callback record
                         record.postSoundProfileUpdated(profileId, profile);
@@ -148,7 +151,7 @@
             }
             @Override
             public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
-                synchronized (mLock) {
+                synchronized (mSpLock) {
                     for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
                         // TODO: filter callback record
                         record.postSoundProfileRemoved(profileId, profile);
@@ -158,7 +161,7 @@
             @Override
             public void onParameterCapabilitiesChanged(
                     String profileId, List<ParameterCapability> caps) {
-                synchronized (mLock) {
+                synchronized (mSpLock) {
                     for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
                         // TODO: filter callback record
                         record.postParameterCapabilitiesChanged(profileId, caps);
@@ -167,7 +170,7 @@
             }
             @Override
             public void onError(String profileId, int err) {
-                synchronized (mLock) {
+                synchronized (mSpLock) {
                     for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
                         // TODO: filter callback record
                         record.postError(profileId, err);
@@ -178,7 +181,7 @@
         IAmbientBacklightCallback abCallback = new IAmbientBacklightCallback.Stub() {
             @Override
             public void onAmbientBacklightEvent(AmbientBacklightEvent event) {
-                synchronized (mLock) {
+                synchronized (mAbLock) {
                     for (AmbientBacklightCallbackRecord record : mAbCallbackRecords) {
                         record.postAmbientBacklightEvent(event);
                     }
@@ -205,7 +208,7 @@
             @NonNull PictureProfileCallback callback) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(executor);
-        synchronized (mLock) {
+        synchronized (mPpLock) {
             mPpCallbackRecords.add(new PictureProfileCallbackRecord(callback, executor));
         }
     }
@@ -215,7 +218,7 @@
      */
     public void unregisterPictureProfileCallback(@NonNull final PictureProfileCallback callback) {
         Preconditions.checkNotNull(callback);
-        synchronized (mLock) {
+        synchronized (mPpLock) {
             for (Iterator<PictureProfileCallbackRecord> it = mPpCallbackRecords.iterator();
                     it.hasNext(); ) {
                 PictureProfileCallbackRecord record = it.next();
@@ -416,7 +419,7 @@
             @NonNull SoundProfileCallback callback) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(executor);
-        synchronized (mLock) {
+        synchronized (mSpLock) {
             mSpCallbackRecords.add(new SoundProfileCallbackRecord(callback, executor));
         }
     }
@@ -426,7 +429,7 @@
      */
     public void unregisterSoundProfileCallback(@NonNull final SoundProfileCallback callback) {
         Preconditions.checkNotNull(callback);
-        synchronized (mLock) {
+        synchronized (mSpLock) {
             for (Iterator<SoundProfileCallbackRecord> it = mSpCallbackRecords.iterator();
                     it.hasNext(); ) {
                 SoundProfileCallbackRecord record = it.next();
@@ -785,7 +788,7 @@
             @NonNull AmbientBacklightCallback callback) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(executor);
-        synchronized (mLock) {
+        synchronized (mAbLock) {
             mAbCallbackRecords.add(new AmbientBacklightCallbackRecord(callback, executor));
         }
     }
@@ -797,7 +800,7 @@
     public void unregisterAmbientBacklightCallback(
             @NonNull final AmbientBacklightCallback callback) {
         Preconditions.checkNotNull(callback);
-        synchronized (mLock) {
+        synchronized (mAbLock) {
             for (Iterator<AmbientBacklightCallbackRecord> it = mAbCallbackRecords.iterator();
                     it.hasNext(); ) {
                 AmbientBacklightCallbackRecord record = it.next();
@@ -1128,7 +1131,7 @@
             @NonNull Consumer<List<ActiveProcessingPicture>> listener) {
         Preconditions.checkNotNull(listener);
         Preconditions.checkNotNull(executor);
-        synchronized (mLock) {
+        synchronized (mApLock) {
             mApListenerRecords.add(
                     new ActiveProcessingPictureListenerRecord(listener, executor, false));
         }
@@ -1147,7 +1150,7 @@
             @NonNull Consumer<List<ActiveProcessingPicture>> listener) {
         Preconditions.checkNotNull(listener);
         Preconditions.checkNotNull(executor);
-        synchronized (mLock) {
+        synchronized (mApLock) {
             mApListenerRecords.add(
                     new ActiveProcessingPictureListenerRecord(listener, executor, true));
         }
@@ -1160,7 +1163,7 @@
     public void removeActiveProcessingPictureListener(
             @NonNull Consumer<List<ActiveProcessingPicture>> listener) {
         Preconditions.checkNotNull(listener);
-        synchronized (mLock) {
+        synchronized (mApLock) {
             for (Iterator<ActiveProcessingPictureListenerRecord> it = mApListenerRecords.iterator();
                     it.hasNext(); ) {
                 ActiveProcessingPictureListenerRecord record = it.next();
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b30b779..49cbd71 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -421,6 +421,7 @@
 LIBANDROID_PLATFORM {
   global:
     AThermal_setIThermalServiceForTesting;
+    ASystemHealth_setIHintManagerForTesting;
     APerformanceHint_setIHintManagerForTesting;
     APerformanceHint_sendHint;
     APerformanceHint_getThreadIds;
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
index 5c07ac7..1b43e71 100644
--- a/native/android/system_health.cpp
+++ b/native/android/system_health.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "system_health"
+
 #include <aidl/android/hardware/power/CpuHeadroomParams.h>
 #include <aidl/android/hardware/power/GpuHeadroomParams.h>
 #include <aidl/android/os/CpuHeadroomParamsInternal.h>
@@ -23,6 +25,17 @@
 #include <android/system_health.h>
 #include <binder/IServiceManager.h>
 #include <binder/Status.h>
+#include <system_health_private.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+#include "android-base/thread_annotations.h"
+#include "utils/SystemClock.h"
 
 using namespace android;
 using namespace aidl::android::os;
@@ -55,9 +68,20 @@
     IHintManager::HintManagerClientData mClientData;
 };
 
+static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
+static std::shared_ptr<ASystemHealthManager> gSystemHealthManagerForTesting = nullptr;
+
 ASystemHealthManager* ASystemHealthManager::getInstance() {
     static std::once_flag creationFlag;
     static ASystemHealthManager* instance = nullptr;
+    if (gSystemHealthManagerForTesting) {
+        return gSystemHealthManagerForTesting.get();
+    }
+    if (gIHintManagerForTesting) {
+        gSystemHealthManagerForTesting =
+                std::shared_ptr<ASystemHealthManager>(create(*gIHintManagerForTesting));
+        return gSystemHealthManagerForTesting.get();
+    }
     std::call_once(creationFlag, []() { instance = create(nullptr); });
     return instance;
 }
@@ -121,7 +145,8 @@
         }
         return EPIPE;
     }
-    *outHeadroom = res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>();
+    *outHeadroom = res ? res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>()
+                       : std::numeric_limits<float>::quiet_NaN();
     return OK;
 }
 
@@ -155,37 +180,20 @@
         }
         return EPIPE;
     }
-    *outHeadroom = res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>();
+    *outHeadroom = res ? res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>()
+                       : std::numeric_limits<float>::quiet_NaN();
     return OK;
 }
 
 int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
     if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP;
-    int64_t minIntervalMillis = 0;
-    ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
-    if (!ret.isOk()) {
-        ALOGE("ASystemHealth_getCpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
-        if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
-            return ENOTSUP;
-        }
-        return EPIPE;
-    }
-    *outMinIntervalMillis = minIntervalMillis;
+    *outMinIntervalMillis = mClientData.supportInfo.headroom.cpuMinIntervalMillis;
     return OK;
 }
 
 int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
     if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
-    int64_t minIntervalMillis = 0;
-    ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
-    if (!ret.isOk()) {
-        ALOGE("ASystemHealth_getGpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
-        if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
-            return ENOTSUP;
-        }
-        return EPIPE;
-    }
-    *outMinIntervalMillis = minIntervalMillis;
+    *outMinIntervalMillis = mClientData.supportInfo.headroom.gpuMinIntervalMillis;
     return OK;
 }
 
@@ -298,7 +306,6 @@
                                 size_t tidsSize) {
     LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
     params->tids.resize(tidsSize);
-    params->tids.clear();
     for (int i = 0; i < (int)tidsSize; ++i) {
         LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
                             tids[i]);
@@ -355,3 +362,10 @@
 void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) {
     delete params;
 }
+
+void ASystemHealth_setIHintManagerForTesting(void* iManager) {
+    if (iManager == nullptr) {
+        gSystemHealthManagerForTesting = nullptr;
+    }
+    gIHintManagerForTesting = static_cast<std::shared_ptr<IHintManager>*>(iManager);
+}
diff --git a/native/android/tests/system_health/Android.bp b/native/android/tests/system_health/Android.bp
new file mode 100644
index 0000000..30aeb77
--- /dev/null
+++ b/native/android/tests/system_health/Android.bp
@@ -0,0 +1,66 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_test {
+    name: "NativeSystemHealthUnitTestCases",
+
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    srcs: ["NativeSystemHealthUnitTest.cpp"],
+
+    shared_libs: [
+        "libandroid",
+        "libbinder",
+        "libbinder_ndk",
+        "liblog",
+        "libpowermanager",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libbase",
+        "libgmock",
+        "libgtest",
+    ],
+    stl: "c++_shared",
+
+    test_suites: [
+        "device-tests",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: [
+        "libandroid_headers_private",
+    ],
+}
diff --git a/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp b/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp
new file mode 100644
index 0000000..3f08fc6
--- /dev/null
+++ b/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 "NativeSystemHealthUnitTest"
+
+#include <aidl/android/os/IHintManager.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <android/system_health.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <system_health_private.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+using namespace std::chrono_literals;
+namespace hal = aidl::android::hardware::power;
+using aidl::android::os::CpuHeadroomParamsInternal;
+using aidl::android::os::GpuHeadroomParamsInternal;
+using aidl::android::os::IHintManager;
+using aidl::android::os::IHintSession;
+using aidl::android::os::SessionCreationConfig;
+using ndk::ScopedAStatus;
+using ndk::SpAIBinder;
+
+using namespace android;
+using namespace testing;
+
+class MockIHintManager : public IHintManager {
+public:
+    MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
+                (const SpAIBinder& token, hal::SessionTag tag,
+                 const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
+                 IHintManager::SessionCreationReturn* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
+                (const std::shared_ptr<IHintSession>& _, const ::std::vector<int32_t>& tids),
+                (override));
+    MOCK_METHOD(ScopedAStatus, getHintSessionThreadIds,
+                (const std::shared_ptr<IHintSession>& _, ::std::vector<int32_t>* tids), (override));
+    MOCK_METHOD(ScopedAStatus, getSessionChannel,
+                (const ::ndk::SpAIBinder& in_token,
+                 std::optional<hal::ChannelConfig>* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+    MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+                (const CpuHeadroomParamsInternal& _,
+                 std::optional<hal::CpuHeadroomResult>* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t*), (override));
+    MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+                (const GpuHeadroomParamsInternal& _,
+                 std::optional<hal::GpuHeadroomResult>* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
+    MOCK_METHOD(ScopedAStatus, registerClient,
+                (const std::shared_ptr<aidl::android::os::IHintManager::IHintManagerClient>& _,
+                 aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
+                (override));
+    MOCK_METHOD(ScopedAStatus, getClientData,
+                (aidl::android::os::IHintManager::HintManagerClientData * _aidl_return),
+                (override));
+    MOCK_METHOD(SpAIBinder, asBinder, (), (override));
+    MOCK_METHOD(bool, isRemote, (), (override));
+};
+
+class NativeSystemHealthUnitTest : public Test {
+public:
+    void SetUp() override {
+        mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
+        ASystemHealth_setIHintManagerForTesting(&mMockIHintManager);
+        ON_CALL(*mMockIHintManager, getClientData(_))
+                .WillByDefault(
+                        DoAll(SetArgPointee<0>(mClientData), [] { return ScopedAStatus::ok(); }));
+    }
+
+    void TearDown() override {
+        ASystemHealth_setIHintManagerForTesting(nullptr);
+    }
+
+    IHintManager::HintManagerClientData mClientData{
+            .powerHalVersion = 6,
+            .maxCpuHeadroomThreads = 10,
+            .supportInfo{.headroom{
+                    .isCpuSupported = true,
+                    .isGpuSupported = true,
+                    .cpuMinIntervalMillis = 999,
+                    .gpuMinIntervalMillis = 998,
+                    .cpuMinCalculationWindowMillis = 45,
+                    .cpuMaxCalculationWindowMillis = 9999,
+                    .gpuMinCalculationWindowMillis = 46,
+                    .gpuMaxCalculationWindowMillis = 9998,
+            }},
+    };
+
+    std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr;
+};
+
+TEST_F(NativeSystemHealthUnitTest, headroomParamsValueRange) {
+    int64_t minIntervalMillis = 0;
+    int minCalculationWindowMillis = 0;
+    int maxCalculationWindowMillis = 0;
+    ASSERT_EQ(OK, ASystemHealth_getCpuHeadroomMinIntervalMillis(&minIntervalMillis));
+    ASSERT_EQ(OK,
+              ASystemHealth_getCpuHeadroomCalculationWindowRange(&minCalculationWindowMillis,
+                                                                 &maxCalculationWindowMillis));
+    ASSERT_EQ(minIntervalMillis, mClientData.supportInfo.headroom.cpuMinIntervalMillis);
+    ASSERT_EQ(minCalculationWindowMillis,
+              mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis);
+    ASSERT_EQ(maxCalculationWindowMillis,
+              mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis);
+
+    ASSERT_EQ(OK, ASystemHealth_getGpuHeadroomMinIntervalMillis(&minIntervalMillis));
+    ASSERT_EQ(OK,
+              ASystemHealth_getGpuHeadroomCalculationWindowRange(&minCalculationWindowMillis,
+                                                                 &maxCalculationWindowMillis));
+    ASSERT_EQ(minIntervalMillis, mClientData.supportInfo.headroom.gpuMinIntervalMillis);
+    ASSERT_EQ(minCalculationWindowMillis,
+              mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis);
+    ASSERT_EQ(maxCalculationWindowMillis,
+              mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis);
+}
+
+TEST_F(NativeSystemHealthUnitTest, getCpuHeadroom) {
+    CpuHeadroomParamsInternal internalParams1;
+    ACpuHeadroomParams* params2 = ACpuHeadroomParams_create();
+    ACpuHeadroomParams_setCalculationWindowMillis(params2, 200);
+    CpuHeadroomParamsInternal internalParams2;
+    internalParams2.calculationWindowMillis = 200;
+    ACpuHeadroomParams* params3 = ACpuHeadroomParams_create();
+    ACpuHeadroomParams_setCalculationType(params3, ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE);
+    CpuHeadroomParamsInternal internalParams3;
+    internalParams3.calculationType = hal::CpuHeadroomParams::CalculationType::AVERAGE;
+    ACpuHeadroomParams* params4 = ACpuHeadroomParams_create();
+    int tids[3] = {1, 2, 3};
+    ACpuHeadroomParams_setTids(params4, tids, 3);
+    CpuHeadroomParamsInternal internalParams4;
+    internalParams4.tids = {1, 2, 3};
+
+    EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams1, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+                                             hal::CpuHeadroomResult::globalHeadroom>(1.0f)),
+                            [] { return ScopedAStatus::ok(); }));
+    EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams2, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+                                             hal::CpuHeadroomResult::globalHeadroom>(2.0f)),
+                            [] { return ScopedAStatus::ok(); }));
+    EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams3, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(std::nullopt), [] { return ScopedAStatus::ok(); }));
+    EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams4, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+                                             hal::CpuHeadroomResult::globalHeadroom>(4.0f)),
+                            [] { return ScopedAStatus::ok(); }));
+
+    float headroom1 = 0.0f;
+    float headroom2 = 0.0f;
+    float headroom3 = 0.0f;
+    float headroom4 = 0.0f;
+    ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(nullptr, &headroom1));
+    ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params2, &headroom2));
+    ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params3, &headroom3));
+    ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params4, &headroom4));
+    ASSERT_EQ(1.0f, headroom1);
+    ASSERT_EQ(2.0f, headroom2);
+    ASSERT_TRUE(isnan(headroom3));
+    ASSERT_EQ(4.0f, headroom4);
+
+    ACpuHeadroomParams_destroy(params2);
+    ACpuHeadroomParams_destroy(params3);
+    ACpuHeadroomParams_destroy(params4);
+}
+
+TEST_F(NativeSystemHealthUnitTest, getGpuHeadroom) {
+    GpuHeadroomParamsInternal internalParams1;
+    AGpuHeadroomParams* params2 = AGpuHeadroomParams_create();
+    AGpuHeadroomParams_setCalculationWindowMillis(params2, 200);
+    GpuHeadroomParamsInternal internalParams2;
+    internalParams2.calculationWindowMillis = 200;
+    AGpuHeadroomParams* params3 = AGpuHeadroomParams_create();
+    AGpuHeadroomParams_setCalculationType(params3, AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE);
+    GpuHeadroomParamsInternal internalParams3;
+    internalParams3.calculationType = hal::GpuHeadroomParams::CalculationType::AVERAGE;
+
+    EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams1, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(hal::GpuHeadroomResult::make<
+                                             hal::GpuHeadroomResult::globalHeadroom>(1.0f)),
+                            [] { return ScopedAStatus::ok(); }));
+    EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams2, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(hal::GpuHeadroomResult::make<
+                                             hal::GpuHeadroomResult::globalHeadroom>(2.0f)),
+                            [] { return ScopedAStatus::ok(); }));
+    EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams3, _))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<1>(std::nullopt), [] { return ScopedAStatus::ok(); }));
+
+    float headroom1 = 0.0f;
+    float headroom2 = 0.0f;
+    float headroom3 = 0.0f;
+    ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(nullptr, &headroom1));
+    ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(params2, &headroom2));
+    ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(params3, &headroom3));
+    ASSERT_EQ(1.0f, headroom1);
+    ASSERT_EQ(2.0f, headroom2);
+    ASSERT_TRUE(isnan(headroom3));
+
+    AGpuHeadroomParams_destroy(params2);
+    AGpuHeadroomParams_destroy(params3);
+}
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 1a3446ec..5dd42bb 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -1,8 +1,8 @@
 filegroup {
     name: "framework-crashrecovery-sources",
     srcs: [
-        "java/**/*.java",
         "java/**/*.aidl",
+        "java/**/*.java",
     ],
     path: "java",
     visibility: [
@@ -12,11 +12,14 @@
 
 java_sdk_library {
     name: "framework-platformcrashrecovery",
-    srcs: [":framework-crashrecovery-sources"],
+    srcs: [
+        ":framework-crashrecovery-module-sources",
+        ":framework-crashrecovery-sources",
+    ],
     defaults: ["framework-non-updatable-unbundled-defaults"],
     permitted_packages: [
-        "android.service.watchdog",
         "android.crashrecovery",
+        "android.service.watchdog",
     ],
     static_libs: ["android.crashrecovery.flags-aconfig-java"],
     aidl: {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index 40bc5f7..846da19 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -29,18 +29,13 @@
 import android.content.pm.VersionedPackage;
 import android.crashrecovery.flags.Flags;
 import android.os.Build;
-import android.os.Environment;
 import android.os.PowerManager;
 import android.os.RecoverySystem;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.sysprop.CrashRecoveryProperties;
 import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.ArrayUtils;
 import android.util.EventLog;
 import android.util.FileUtils;
 import android.util.Log;
@@ -56,10 +51,7 @@
 import java.io.File;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -241,87 +233,11 @@
         CrashRecoveryProperties.maxRescueLevelAttempted(level);
     }
 
-    private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) {
-        Set<String> resultSet = new ArraySet<String>();
-        if (!Flags.deprecateFlagsAndSettingsResets()) {
-            try {
-                String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION,
-                        NAMESPACE_TO_PACKAGE_MAPPING_FLAG, "");
-                String[] mappingEntries = flagVal.split(",");
-                for (int i = 0; i < mappingEntries.length; i++) {
-                    if (TextUtils.isEmpty(mappingEntries[i])) {
-                        continue;
-                    }
-                    String[] splitEntry = mappingEntries[i].split(":");
-                    if (splitEntry.length != 2) {
-                        throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]);
-                    }
-                    String namespace = splitEntry[0];
-                    String packageName = splitEntry[1];
-
-                    if (packageNames.contains(packageName)) {
-                        resultSet.add(namespace);
-                    }
-                }
-            } catch (Exception e) {
-                resultSet.clear();
-                Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e);
-            } finally {
-                return resultSet;
-            }
-        } else {
-            return resultSet;
-        }
-    }
-
     @VisibleForTesting
     static long getElapsedRealtime() {
         return SystemClock.elapsedRealtime();
     }
 
-    private static class RescuePartyMonitorCallback implements DeviceConfig.MonitorCallback {
-        Context mContext;
-
-        RescuePartyMonitorCallback(Context context) {
-            this.mContext = context;
-        }
-
-        public void onNamespaceUpdate(@NonNull String updatedNamespace) {
-            if (!Flags.deprecateFlagsAndSettingsResets()) {
-                startObservingPackages(mContext, updatedNamespace);
-            }
-        }
-
-        public void onDeviceConfigAccess(@NonNull String callingPackage,
-                @NonNull String namespace) {
-
-            if (!Flags.deprecateFlagsAndSettingsResets()) {
-                RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
-                        callingPackage,
-                        namespace);
-            }
-        }
-    }
-
-    private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
-        if (!Flags.deprecateFlagsAndSettingsResets()) {
-            RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
-            Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(
-                    updatedNamespace);
-            if (callingPackages == null) {
-                return;
-            }
-            List<String> callingPackageList = new ArrayList<>();
-            callingPackageList.addAll(callingPackages);
-            Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
-                    + updatedNamespace);
-            PackageWatchdog.getInstance(context).startExplicitHealthCheck(
-                    callingPackageList,
-                    DEFAULT_OBSERVING_DURATION_MS,
-                    rescuePartyObserver);
-        }
-    }
-
     private static int getMaxRescueLevel(boolean mayPerformReboot) {
         if (Flags.recoverabilityDetection()) {
             if (!mayPerformReboot
@@ -849,34 +765,6 @@
             }
         }
 
-        private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
-                @NonNull String namespace) {
-            if (!Flags.deprecateFlagsAndSettingsResets()) {
-                // Record it in calling packages to namespace map
-                Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
-                if (namespaceSet == null) {
-                    namespaceSet = new ArraySet<>();
-                    mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
-                }
-                namespaceSet.add(namespace);
-                // Record it in namespace to calling packages map
-                Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
-                if (callingPackageSet == null) {
-                    callingPackageSet = new ArraySet<>();
-                }
-                callingPackageSet.add(callingPackage);
-                mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
-            }
-        }
-
-        private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
-            return mCallingPackageNamespaceSetMap.get(failedPackage);
-        }
-
-        private synchronized Set<String> getAllAffectedNamespaceSet() {
-            return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
-        }
-
         private synchronized Set<String> getCallingPackagesSet(String namespace) {
             return mNamespaceCallingPackageSetMap.get(namespace);
         }
@@ -894,26 +782,6 @@
         return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
     }
 
-    private static int[] getAllUserIds() {
-        int systemUserId = UserHandle.SYSTEM.getIdentifier();
-        int[] userIds = { systemUserId };
-        try {
-            for (File file : FileUtils.listFilesOrEmpty(
-                    Environment.getDataSystemDeviceProtectedDirectory())) {
-                try {
-                    final int userId = Integer.parseInt(file.getName());
-                    if (userId != systemUserId) {
-                        userIds = ArrayUtils.appendInt(userIds, userId);
-                    }
-                } catch (NumberFormatException ignored) {
-                }
-            }
-        } catch (Throwable t) {
-            Slog.w(TAG, "Trouble discovering users", t);
-        }
-        return userIds;
-    }
-
     /**
      * Hacky test to check if the device has an active USB connection, which is
      * a good proxy for someone doing local development work.
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
index 0b7b986..29ff7cc 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
@@ -16,13 +16,8 @@
 
 package android.util;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import java.io.File;
-import java.util.List;
-import java.util.Objects;
-
 /**
  * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java
  *
@@ -30,25 +25,6 @@
  */
 public class ArrayUtils {
     private ArrayUtils() { /* cannot be instantiated */ }
-    public static final File[] EMPTY_FILE = new File[0];
-
-
-    /**
-     * Return first index of {@code value} in {@code array}, or {@code -1} if
-     * not found.
-     */
-    public static <T> int indexOf(@Nullable T[] array, T value) {
-        if (array == null) return -1;
-        for (int i = 0; i < array.length; i++) {
-            if (Objects.equals(array[i], value)) return i;
-        }
-        return -1;
-    }
-
-    /** @hide */
-    public static @NonNull File[] defeatNullable(@Nullable File[] val) {
-        return (val != null) ? val : EMPTY_FILE;
-    }
 
     /**
      * Checks if given array is null or has zero elements.
@@ -63,53 +39,4 @@
     public static boolean isEmpty(@Nullable byte[] array) {
         return array == null || array.length == 0;
     }
-
-    /**
-     * Converts from List of bytes to byte array
-     * @param list
-     * @return byte[]
-     */
-    public static byte[] toPrimitive(List<byte[]> list) {
-        if (list.size() == 0) {
-            return new byte[0];
-        }
-        int byteLen = list.get(0).length;
-        byte[] array = new byte[list.size() * byteLen];
-        for (int i = 0; i < list.size(); i++) {
-            for (int j = 0; j < list.get(i).length; j++) {
-                array[i * byteLen + j] = list.get(i)[j];
-            }
-        }
-        return array;
-    }
-
-    /**
-     * Adds value to given array if not already present, providing set-like
-     * behavior.
-     */
-    public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
-        return appendInt(cur, val, false);
-    }
-
-    /**
-     * Adds value to given array.
-     */
-    public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
-            boolean allowDuplicates) {
-        if (cur == null) {
-            return new int[] { val };
-        }
-        final int n = cur.length;
-        if (!allowDuplicates) {
-            for (int i = 0; i < n; i++) {
-                if (cur[i] == val) {
-                    return cur;
-                }
-            }
-        }
-        int[] ret = new int[n + 1];
-        System.arraycopy(cur, 0, ret, 0, n);
-        ret[n] = val;
-        return ret;
-    }
 }
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
index 9c73fee..d60a9b9 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
@@ -16,7 +16,6 @@
 
 package android.util;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 
 import java.io.BufferedInputStream;
@@ -115,14 +114,4 @@
         }
         return false;
     }
-
-    /**
-     * List the files in the directory or return empty file.
-     *
-     * @hide
-     */
-    public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
-        return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
-            : ArrayUtils.EMPTY_FILE;
-    }
 }
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
index 50823f5..488b531 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
@@ -16,21 +16,10 @@
 
 package android.util;
 
-import android.annotation.NonNull;
-import android.system.ErrnoException;
-import android.system.Os;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-import libcore.util.XmlObjectFactory;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.InputStream;
 
 /**
  *  Bits and pieces copied from hidden API of
@@ -40,8 +29,6 @@
  */
 public class XmlUtils {
 
-    private static final String STRING_ARRAY_SEPARATOR = ":";
-
     /** @hide */
     public static final void beginDocument(XmlPullParser parser, String firstElementName)
             throws XmlPullParserException, IOException {
@@ -76,44 +63,4 @@
             }
         }
     }
-
-    private static XmlPullParser newPullParser() {
-        try {
-            XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-            return parser;
-        } catch (XmlPullParserException e) {
-            throw new AssertionError();
-        }
-    }
-
-    /** @hide */
-    public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
-            throws IOException {
-        final byte[] magic = new byte[4];
-        if (in instanceof FileInputStream) {
-            try {
-                Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0);
-            } catch (ErrnoException e) {
-                throw e.rethrowAsIOException();
-            }
-        } else {
-            if (!in.markSupported()) {
-                in = new BufferedInputStream(in);
-            }
-            in.mark(8);
-            in.read(magic);
-            in.reset();
-        }
-
-        final TypedXmlPullParser xml;
-        xml = (TypedXmlPullParser) newPullParser();
-        try {
-            xml.setInput(in, "UTF_8");
-        } catch (XmlPullParserException e) {
-            throw new IOException(e);
-        }
-        return xml;
-    }
 }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
index 5b7e2a8..e6cc8a8 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
@@ -24,6 +24,7 @@
 import android.icu.text.UnicodeSet
 import android.icu.text.UnicodeSetSpanner
 import android.icu.util.Measure
+import android.text.BidiFormatter
 import android.text.format.Formatter
 import android.text.format.Formatter.RoundedBytesResult
 import java.math.BigDecimal
@@ -40,11 +41,17 @@
     constructor(context: Context) : this(context.resources)
 
     private val locale = resources.configuration.locales[0]
+    private val bidiFormatter = BidiFormatter.getInstance(locale)
 
     fun format(bytes: Long, useCase: UseCase): String {
         val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
         val numberFormatter = getNumberFormatter(rounded.fractionDigits)
-        return numberFormatter.formatRoundedBytesResult(rounded)
+        val formattedString = numberFormatter.formatRoundedBytesResult(rounded)
+        return if (useCase == UseCase.FileSize) {
+            formattedString.bidiWrap()
+        } else {
+            formattedString
+        }
     }
 
     fun formatWithUnits(bytes: Long, useCase: UseCase): Result {
@@ -74,6 +81,14 @@
             }
         }
 
+    /** Wraps the source string in bidi formatting characters in RTL locales. */
+    private fun String.bidiWrap(): String =
+        if (bidiFormatter.isRtlContext) {
+            bidiFormatter.unicodeWrap(this)
+        } else {
+            this
+        }
+
     private companion object {
         fun String.removeFirst(removed: String): String =
             SPACES_AND_CONTROLS.trim(replaceFirst(removed, "")).toString()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt
new file mode 100644
index 0000000..6fd470c
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.spaprivileged.model.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.util.Log
+import com.android.settingslib.spaprivileged.framework.common.BytesFormatter
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+
+/** A repository interface for accessing and formatting app storage information. */
+interface AppStorageRepository {
+    /**
+     * Formats the size of an application into a human-readable string.
+     *
+     * This function retrieves the total size of the application, including APK file and its
+     * associated data.
+     *
+     * This function takes an [ApplicationInfo] object as input and returns a formatted string
+     * representing the size of the application. The size is formatted in units like kB, MB, GB,
+     * etc.
+     *
+     * @param app The [ApplicationInfo] object representing the application.
+     * @return A formatted string representing the size of the application.
+     */
+    fun formatSize(app: ApplicationInfo): String
+
+    /**
+     * Formats the size about an application into a human-readable string.
+     *
+     * @param sizeBytes The size in bytes to format.
+     * @return A formatted string representing the size about application.
+     */
+    fun formatSizeBytes(sizeBytes: Long): String
+
+    /**
+     * Calculates the size of an application in bytes.
+     *
+     * This function retrieves the total size of the application, including APK file and its
+     * associated data.
+     *
+     * @param app The [ApplicationInfo] object representing the application.
+     * @return The total size of the application in bytes, or null if the size could not be
+     *   determined.
+     */
+    fun calculateSizeBytes(app: ApplicationInfo): Long?
+}
+
+class AppStorageRepositoryImpl(context: Context) : AppStorageRepository {
+    private val storageStatsManager = context.storageStatsManager
+    private val bytesFormatter = BytesFormatter(context)
+
+    override fun formatSize(app: ApplicationInfo): String {
+        val sizeBytes = calculateSizeBytes(app)
+        return if (sizeBytes != null) formatSizeBytes(sizeBytes) else ""
+    }
+
+    override fun formatSizeBytes(sizeBytes: Long): String =
+        bytesFormatter.format(sizeBytes, BytesFormatter.UseCase.FileSize)
+
+    override fun calculateSizeBytes(app: ApplicationInfo): Long? =
+        try {
+            val stats =
+                storageStatsManager.queryStatsForPackage(
+                    app.storageUuid,
+                    app.packageName,
+                    app.userHandle,
+                )
+            stats.codeBytes + stats.dataBytes
+        } catch (e: Exception) {
+            Log.w(TAG, "Failed to query stats", e)
+            null
+        }
+
+    companion object {
+        private const val TAG = "AppStorageRepository"
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
index 7a4f81c..7c98e9c 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
@@ -16,42 +16,30 @@
 
 package com.android.settingslib.spaprivileged.template.app
 
-import android.content.Context
 import android.content.pm.ApplicationInfo
-import android.text.format.Formatter
-import android.util.Log
+import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.android.settingslib.spa.framework.compose.rememberContext
 import com.android.settingslib.spaprivileged.framework.compose.placeholder
-import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepository
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepositoryImpl
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOn
 
-private const val TAG = "AppStorageSize"
-
 @Composable
-fun ApplicationInfo.getStorageSize(): State<String> {
-    val context = LocalContext.current
-    return remember(this) {
-        flow {
-            val sizeBytes = calculateSizeBytes(context)
-            this.emit(if (sizeBytes != null) Formatter.formatFileSize(context, sizeBytes) else "")
-        }.flowOn(Dispatchers.IO)
-    }.collectAsStateWithLifecycle(initialValue = placeholder())
-}
+fun ApplicationInfo.getStorageSize(): State<String> =
+    getStorageSize(rememberContext(::AppStorageRepositoryImpl))
 
-fun ApplicationInfo.calculateSizeBytes(context: Context): Long? {
-    val storageStatsManager = context.storageStatsManager
-    return try {
-        val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
-        stats.codeBytes + stats.dataBytes
-    } catch (e: Exception) {
-        Log.w(TAG, "Failed to query stats: $e")
-        null
-    }
+@VisibleForTesting
+@Composable
+fun ApplicationInfo.getStorageSize(appStorageRepository: AppStorageRepository): State<String> {
+    val app = this
+    return remember(app) {
+            flow { emit(appStorageRepository.formatSize(app)) }.flowOn(Dispatchers.Default)
+        }
+        .collectAsStateWithLifecycle(initialValue = placeholder())
 }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt
new file mode 100644
index 0000000..e8ec974b
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.spaprivileged.model.app
+
+import android.app.usage.StorageStats
+import android.app.usage.StorageStatsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.NameNotFoundException
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import java.util.UUID
+
+@RunWith(AndroidJUnit4::class)
+class AppStorageRepositoryTest {
+    private val app = ApplicationInfo().apply { storageUuid = UUID.randomUUID() }
+
+    private val mockStorageStatsManager =
+        mock<StorageStatsManager> {
+            on { queryStatsForPackage(app.storageUuid, app.packageName, app.userHandle) } doReturn
+                STATS
+        }
+
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            on { storageStatsManager } doReturn mockStorageStatsManager
+        }
+
+    private val repository = AppStorageRepositoryImpl(context)
+
+    @Test
+    fun calculateSizeBytes() {
+        val sizeBytes = repository.calculateSizeBytes(app)
+
+        assertThat(sizeBytes).isEqualTo(120)
+    }
+
+    @Test
+    fun formatSize() {
+        val fileSize = repository.formatSize(app)
+
+        assertThat(fileSize).isEqualTo("120 byte")
+    }
+
+    @Test
+    fun formatSize_throwException() {
+        mockStorageStatsManager.stub {
+            on { queryStatsForPackage(app.storageUuid, app.packageName, app.userHandle) } doThrow
+                NameNotFoundException()
+        }
+
+        val fileSize = repository.formatSize(app)
+
+        assertThat(fileSize).isEqualTo("")
+    }
+
+    @Test
+    fun formatSizeBytes() {
+        val fileSize = repository.formatSizeBytes(120)
+
+        assertThat(fileSize).isEqualTo("120 byte")
+    }
+
+    companion object {
+        private val STATS =
+            StorageStats().apply {
+                codeBytes = 100
+                dataBytes = 20
+            }
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
index 60f3d0c..4f42c82 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
@@ -16,98 +16,37 @@
 
 package com.android.settingslib.spaprivileged.template.app
 
-import android.app.usage.StorageStats
-import android.app.usage.StorageStatsManager
-import android.content.Context
 import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager.NameNotFoundException
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settingslib.spa.framework.compose.stateOf
-import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
-import com.android.settingslib.spaprivileged.model.app.userHandle
-import java.util.UUID
-import org.junit.Before
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepository
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import java.util.UUID
 
 @RunWith(AndroidJUnit4::class)
 class AppStorageSizeTest {
-    @get:Rule
-    val mockito: MockitoRule = MockitoJUnit.rule()
+    @get:Rule val composeTestRule = createComposeRule()
 
-    @get:Rule
-    val composeTestRule = createComposeRule()
+    private val app = ApplicationInfo().apply { storageUuid = UUID.randomUUID() }
 
-    @Spy
-    private val context: Context = ApplicationProvider.getApplicationContext()
-
-    @Mock
-    private lateinit var storageStatsManager: StorageStatsManager
-
-    private val app = ApplicationInfo().apply {
-        storageUuid = UUID.randomUUID()
-    }
-
-    @Before
-    fun setUp() {
-        whenever(context.storageStatsManager).thenReturn(storageStatsManager)
-        whenever(
-            storageStatsManager.queryStatsForPackage(
-                app.storageUuid,
-                app.packageName,
-                app.userHandle,
-            )
-        ).thenReturn(STATS)
-    }
+    private val mockAppStorageRepository =
+        mock<AppStorageRepository> { on { formatSize(app) } doReturn SIZE }
 
     @Test
     fun getStorageSize() {
         var storageSize = stateOf("")
 
-        composeTestRule.setContent {
-            CompositionLocalProvider(LocalContext provides context) {
-                storageSize = app.getStorageSize()
-            }
-        }
+        composeTestRule.setContent { storageSize = app.getStorageSize(mockAppStorageRepository) }
 
-        composeTestRule.waitUntil { storageSize.value == "120 B" }
+        composeTestRule.waitUntil { storageSize.value == SIZE }
     }
 
-    @Test
-    fun getStorageSize_throwException() {
-        var storageSize = stateOf("Computing")
-        whenever(
-            storageStatsManager.queryStatsForPackage(
-                app.storageUuid,
-                app.packageName,
-                app.userHandle,
-            )
-        ).thenThrow(NameNotFoundException())
-
-        composeTestRule.setContent {
-            CompositionLocalProvider(LocalContext provides context) {
-                storageSize = app.getStorageSize()
-            }
-        }
-
-        composeTestRule.waitUntil { storageSize.value == "" }
-    }
-
-    companion object {
-        private val STATS = StorageStats().apply {
-            codeBytes = 100
-            dataBytes = 20
-            cacheBytes = 3
-        }
+    private companion object {
+        const val SIZE = "120 kB"
     }
 }
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6b2449f..c3c5e87 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -519,6 +519,7 @@
         "androidx.activity_activity-compose",
         "androidx.compose.animation_animation-graphics",
         "androidx.lifecycle_lifecycle-viewmodel-compose",
+        "kairos",
     ],
     libs: [
         "keepanno-annotations",
@@ -739,6 +740,7 @@
         "PlatformMotionTesting",
         "SystemUICustomizationTestUtils",
         "androidx.compose.runtime_runtime",
+        "kairos",
         "kosmos",
         "testables",
         "androidx.test.rules",
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index b5eba08..72b3d08 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -125,3 +125,13 @@
     description: "Update hearing device icon in floating menu according to the connection status."
     bug: "357882387"
 }
+
+flag {
+    name: "floating_menu_notify_targets_changed_on_strict_diff"
+    namespace: "accessibility"
+    description: "Only notify listeners that the list of accessibility targets has changed if the lists are not identical."
+    bug: "376473165"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a0d7fd2..153f892 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -382,6 +382,13 @@
 }
 
 flag {
+    name: "status_bar_mobile_icon_kairos"
+    namespace: "systemui"
+    description: "Refactors the mobile connection icon in the status bar to use the Kairos library"
+    bug: "383172066"
+}
+
+flag {
     name: "status_bar_monochrome_icons_fix"
     namespace: "systemui"
     description: "Fixes the status bar icon size when drawing InsetDrawables (ie. monochrome icons)"
@@ -1912,3 +1919,13 @@
    description: "Special UI treatment for magic actions"
    bug: "383567383"
 }
+
+flag {
+    name: "show_audio_sharing_slider_in_volume_panel"
+    namespace: "cross_device_experiences"
+    description: "Show two sliders in volume panel when audio sharing."
+    bug: "336183611"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 5ff7bd0..737170f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,17 +18,25 @@
 
 import static android.app.UiModeManager.MODE_NIGHT_YES;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.UiModeManager;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
@@ -37,6 +45,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.Prefs;
@@ -54,6 +64,9 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** Tests for {@link MenuView}. */
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -63,17 +76,24 @@
     private int mNightMode;
     private UiModeManager mUiModeManager;
     private MenuView mMenuView;
+    private MenuView mMenuViewSpy;
     private String mLastPosition;
     private MenuViewAppearance mStubMenuViewAppearance;
+    private MenuViewModel mMenuViewModel;
+    private final List<String> mShortcutTargets = new ArrayList<>();
 
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+
     @Mock
     private HearingAidDeviceManager mHearingAidDeviceManager;
 
+    @Mock
+    private MenuView.OnTargetFeaturesChangeListener mOnTargetFeaturesChangeListener;
+
     private SysuiTestableContext mSpyContext;
 
     @Before
@@ -91,22 +111,38 @@
         mSpyContext = spy(mContext);
         doNothing().when(mSpyContext).startActivity(any());
 
+        mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
+        mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+        doReturn(mShortcutTargets)
+                .when(mAccessibilityManager)
+                .getAccessibilityShortcutTargets(anyInt());
+
         final SecureSettings secureSettings = TestUtils.mockSecureSettings(mContext);
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
-                secureSettings, mHearingAidDeviceManager);
+        mMenuViewModel =
+                new MenuViewModel(
+                    mContext, mAccessibilityManager, secureSettings, mHearingAidDeviceManager);
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
-        mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
-                secureSettings));
+        mMenuView =
+                new MenuView(mSpyContext, mMenuViewModel, mStubMenuViewAppearance, secureSettings);
+        mMenuView.setOnTargetFeaturesChangeListener(mOnTargetFeaturesChangeListener);
         mLastPosition = Prefs.getString(mSpyContext,
                 Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
+
+        mMenuViewSpy =
+                spy(
+                        new MenuView(
+                                mSpyContext,
+                                mMenuViewModel,
+                                mStubMenuViewAppearance,
+                                secureSettings));
     }
 
     @Test
     public void onConfigurationChanged_updateViewModel() {
-        mMenuView.onConfigurationChanged(/* newConfig= */ null);
+        mMenuViewSpy.onConfigurationChanged(/* newConfig= */ null);
 
-        verify(mMenuView).loadLayoutResources();
+        verify(mMenuViewSpy).loadLayoutResources();
     }
 
     @Test
@@ -179,6 +215,75 @@
         assertThat(radiiAnimator.isStarted()).isTrue();
     }
 
+    @Test
+    @DisableFlags(Flags.FLAG_FLOATING_MENU_NOTIFY_TARGETS_CHANGED_ON_STRICT_DIFF)
+    public void onTargetFeaturesChanged_listenerCalled_flagDisabled() {
+        // Call show() to start observing the target features change listener.
+        mMenuView.show();
+
+        // The target features change listener should be called when the observer is added.
+        verify(mOnTargetFeaturesChangeListener, times(1)).onChange(any());
+
+        // When the target features list changes, the listener should be called.
+        mMenuViewModel.onTargetFeaturesChanged(
+                List.of(
+                        new TestAccessibilityTarget(mContext, 123),
+                        new TestAccessibilityTarget(mContext, 456)));
+        verify(mOnTargetFeaturesChangeListener, times(2)).onChange(any());
+
+        // Double check that when the target features list changes, the listener should be called.
+        List<AccessibilityTarget> newFeaturesList =
+                List.of(
+                        new TestAccessibilityTarget(mContext, 123),
+                        new TestAccessibilityTarget(mContext, 789),
+                        new TestAccessibilityTarget(mContext, 456));
+        mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+        verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+        // When the target features list doesn't change, the listener will still be called.
+        mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+        verify(mOnTargetFeaturesChangeListener, times(4)).onChange(any());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_NOTIFY_TARGETS_CHANGED_ON_STRICT_DIFF)
+    public void onTargetFeaturesChanged_listenerCalled_flagEnabled() {
+        // Call show() to start observing the target features change listener.
+        mMenuView.show();
+
+        // The target features change listener should be called when the observer is added.
+        verify(mOnTargetFeaturesChangeListener, times(1)).onChange(any());
+
+        // When the target features list changes, the listener should be called.
+        mMenuViewModel.onTargetFeaturesChanged(
+                List.of(
+                        new TestAccessibilityTarget(mContext, 123),
+                        new TestAccessibilityTarget(mContext, 456)));
+        verify(mOnTargetFeaturesChangeListener, times(2)).onChange(any());
+
+        // Double check that when the target features list changes, the listener should be called.
+        List<AccessibilityTarget> newFeaturesList =
+                List.of(
+                        new TestAccessibilityTarget(mContext, 123),
+                        new TestAccessibilityTarget(mContext, 789),
+                        new TestAccessibilityTarget(mContext, 456));
+        mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+        verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+        // When the target features list doesn't change, the listener should not be called again.
+        mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+        verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+        // When the target features list changes order (but the UIDs of the targets don't change),
+        // the listener should be called.
+        mMenuViewModel.onTargetFeaturesChanged(
+                List.of(
+                        new TestAccessibilityTarget(mContext, 789),
+                        new TestAccessibilityTarget(mContext, 123),
+                        new TestAccessibilityTarget(mContext, 456)));
+        verify(mOnTargetFeaturesChangeListener, times(4)).onChange(any());
+    }
+
     private InstantInsetLayerDrawable getMenuViewInsetLayer() {
         return (InstantInsetLayerDrawable) mMenuView.getBackground();
     }
@@ -196,6 +301,23 @@
         return radiiAnimator;
     }
 
+    /** Simplified AccessibilityTarget for testing MenuView. */
+    private static class TestAccessibilityTarget extends AccessibilityTarget {
+        TestAccessibilityTarget(Context context, int uid) {
+            // Set fields unused by tests to defaults that allow test compilation.
+            super(
+                    context,
+                    ShortcutConstants.UserShortcutType.SOFTWARE,
+                    0,
+                    false,
+                    MAGNIFICATION_COMPONENT_NAME.flattenToString(),
+                    uid,
+                    null,
+                    null,
+                    null);
+        }
+    }
+
     @After
     public void tearDown() throws Exception {
         mUiModeManager.setNightMode(mNightMode);
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 43d0d69c..e4539b7 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
@@ -66,7 +66,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.bluetooth.VolumeControlProfile;
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.bluetooth.qsdialog.DeviceItem;
@@ -227,7 +226,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
     public void showDialog_noLiveCaption_noRelatedToolsInConfig_relatedToolLayoutGone() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
@@ -239,7 +237,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
     public void showDialog_hasLiveCaption_noRelatedToolsInConfig_showOneRelatedTool() {
         when(mPackageManager.queryIntentActivities(
                 eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
@@ -254,7 +251,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
     public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
             throws PackageManager.NameNotFoundException {
         when(mPackageManager.queryIntentActivities(eq(LIVE_CAPTION_INTENT), anyInt()))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 7d41a20..307b87a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -27,8 +27,6 @@
 
 import android.content.Intent;
 import android.os.Handler;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.testing.TestableLooper;
@@ -38,7 +36,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
@@ -124,18 +121,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
-    public void isAvailable_flagEnabled_true() {
-        assertThat(mTile.isAvailable()).isTrue();
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
-    public void isAvailable_flagDisabled_false() {
-        assertThat(mTile.isAvailable()).isFalse();
-    }
-
-    @Test
     public void longClick_expectedAction() {
         mTile.longClick(null);
         mTestableLooper.processAllMessages();
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 1dfa2cd..9099d3d 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
@@ -17,12 +17,9 @@
 package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
 
 import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.annotations.EnabledOnRavenwood
 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.accessibility.hearingaid.HearingDevicesChecker
 import com.android.systemui.coroutines.collectLastValue
@@ -66,24 +63,14 @@
         underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
     }
 
-    @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
     @Test
-    fun availability_flagEnabled_returnTrue() =
+    fun availability_returnTrue() =
         testScope.runTest {
             val availability by collectLastValue(underTest.availability(testUser))
 
             assertThat(availability).isTrue()
         }
 
-    @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
-    @Test
-    fun availability_flagDisabled_returnFalse() =
-        testScope.runTest {
-            val availability by collectLastValue(underTest.availability(testUser))
-
-            assertThat(availability).isFalse()
-        }
-
     @Test
     fun tileData_bluetoothStateChanged_dataMatchesChecker() =
         testScope.runTest {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index ff6bcdb..51892aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -19,7 +19,6 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.RemoteAnimationTarget;
-import android.window.TransitionInfo;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -31,7 +30,7 @@
      */
     void onAnimationStart(RecentsAnimationControllerCompat controller,
             RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
-            Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras, TransitionInfo info);
+            Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras);
 
     /**
      * Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
new file mode 100644
index 0000000..5e29ba9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.EventsLoop
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.IncrementalLoop
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.StateLoop
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.kairos.launchScope
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.Multibinds
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A Kairos-powered class that needs late-initialization within a Kairos [BuildScope].
+ *
+ * If your class is a [SysUISingleton], you can leverage Dagger to automatically initialize your
+ * instance after SystemUI has initialized:
+ * ```kotlin
+ * class MyClass : KairosActivatable { ... }
+ *
+ * @dagger.Module
+ * interface MyModule {
+ *     @Binds
+ *     @IntoSet
+ *     fun bindKairosActivatable(impl: MyClass): KairosActivatable
+ * }
+ * ```
+ *
+ * Alternatively, you can utilize Dagger's [dagger.assisted.AssistedInject]:
+ * ```kotlin
+ * class MyClass @AssistedInject constructor(...) : KairosActivatable {
+ *     @AssistedFactory
+ *     interface Factory {
+ *         fun create(...): MyClass
+ *     }
+ * }
+ *
+ * // When you need an instance:
+ *
+ * class OtherClass @Inject constructor(
+ *     private val myClassFactory: MyClass.Factory,
+ * ) {
+ *     fun BuildScope.foo() {
+ *         val myClass = activated { myClassFactory.create() }
+ *         ...
+ *     }
+ * }
+ * ```
+ *
+ * @see activated
+ */
+@ExperimentalKairosApi
+fun interface KairosActivatable {
+    /** Initializes any Kairos fields that require a [BuildScope] in order to be constructed. */
+    fun BuildScope.activate()
+}
+
+/** Constructs [KairosActivatable] instances. */
+@ExperimentalKairosApi
+fun interface KairosActivatableFactory<T : KairosActivatable> {
+    fun BuildScope.create(): T
+}
+
+/** Instantiates, [activates][KairosActivatable.activate], and returns a [KairosActivatable]. */
+@ExperimentalKairosApi
+fun <T : KairosActivatable> BuildScope.activated(factory: KairosActivatableFactory<T>): T =
+    factory.run { create() }.apply { activate() }
+
+/**
+ * Utilities for defining [State] and [Events] from a constructor without a provided [BuildScope].
+ * These instances are not active until the builder is [activated][activate]; while you can
+ * immediately use them with other Kairos APIs, the Kairos transaction will be suspended until
+ * initialization is complete.
+ *
+ * ```kotlin
+ * class MyRepository(private val dataSource: DataSource) : KairosBuilder by kairosBuilder() {
+ *   val dataSourceEvent = buildEvents<SomeData> {
+ *       // inside this lambda, we have access to a BuildScope, which can be used to create
+ *       // new inputs to the Kairos network
+ *       dataSource.someDataFlow.toEvents()
+ *   }
+ * }
+ * ```
+ */
+@ExperimentalKairosApi
+interface KairosBuilder : KairosActivatable {
+    /**
+     * Returns a forward-reference to a [State] that will be instantiated when this builder is
+     * [activated][activate].
+     */
+    fun <R> buildState(block: BuildScope.() -> State<R>): State<R>
+
+    /**
+     * Returns a forward-reference to an [Events] that will be instantiated when this builder is
+     * [activated][activate].
+     */
+    fun <R> buildEvents(block: BuildScope.() -> Events<R>): Events<R>
+
+    fun <K, V> buildIncremental(block: BuildScope.() -> Incremental<K, V>): Incremental<K, V>
+
+    /** Defers [block] until this builder is [activated][activate]. */
+    fun onActivated(block: BuildScope.() -> Unit)
+}
+
+/** Returns an [KairosBuilder] that can only be [activated][KairosActivatable.activate] once. */
+@ExperimentalKairosApi fun kairosBuilder(): KairosBuilder = KairosBuilderImpl()
+
+@OptIn(ExperimentalKairosApi::class)
+private class KairosBuilderImpl @Inject constructor() : KairosBuilder {
+
+    // TODO: atomic?
+    // TODO: are two lists really necessary?
+    private var _builds: MutableList<KairosActivatable>? = mutableListOf()
+    private var _startables: MutableList<KairosActivatable>? = mutableListOf()
+
+    private val startables
+        get() = checkNotNull(_startables) { "Kairos network has already been initialized" }
+
+    private val builds
+        get() = checkNotNull(_builds) { "Kairos network has already been initialized" }
+
+    override fun <R> buildState(block: BuildScope.() -> State<R>): State<R> =
+        StateLoop<R>().apply { builds.add { loopback = block() } }
+
+    override fun <R> buildEvents(block: BuildScope.() -> Events<R>): Events<R> =
+        EventsLoop<R>().apply { builds.add { loopback = block() } }
+
+    override fun <K, V> buildIncremental(
+        block: BuildScope.() -> Incremental<K, V>
+    ): Incremental<K, V> = IncrementalLoop<K, V>().apply { builds.add { loopback = block() } }
+
+    override fun onActivated(block: BuildScope.() -> Unit) {
+        startables.add { block() }
+    }
+
+    override fun BuildScope.activate() {
+        builds.forEach { it.run { activate() } }
+        _builds = null
+        deferredBuildScopeAction {
+            startables.forEach { it.run { activate() } }
+            _startables = null
+        }
+    }
+}
+
+/** Initializes [KairosActivatables][KairosActivatable] after SystemUI is initialized. */
+@SysUISingleton
+@ExperimentalKairosApi
+class KairosCoreStartable
+@Inject
+constructor(
+    @Application private val appScope: CoroutineScope,
+    private val kairosNetwork: KairosNetwork,
+    private val activatables: dagger.Lazy<Set<@JvmSuppressWildcards KairosActivatable>>,
+) : CoreStartable {
+    override fun start() {
+        appScope.launch {
+            kairosNetwork.activateSpec {
+                for (activatable in activatables.get()) {
+                    launchScope { activatable.run { activate() } }
+                }
+            }
+        }
+    }
+}
+
+@Module
+@ExperimentalKairosApi
+interface KairosCoreStartableModule {
+    @Binds
+    @IntoMap
+    @ClassKey(KairosCoreStartable::class)
+    fun bindCoreStartable(impl: KairosCoreStartable): CoreStartable
+
+    @Multibinds fun kairosActivatables(): Set<@JvmSuppressWildcards KairosActivatable>
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        fun provideKairosNetwork(@Application scope: CoroutineScope): KairosNetwork =
+            scope.launchKairosNetwork()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index a1cb036..bb6ab51 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -266,7 +266,7 @@
                 mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
                 /* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
                 UserHandle.USER_CURRENT);
-        if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+        if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
             mSecureSettings.registerContentObserverForUserSync(
                     mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
                     /* notifyForDescendants */ false,
@@ -287,7 +287,7 @@
                 UserHandle.USER_CURRENT);
         mContext.registerComponentCallbacks(mComponentCallbacks);
 
-        if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+        if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
             mAccessibilityManager.addAccessibilityServicesStateChangeListener(
                     mA11yServicesStateChangeListener);
         }
@@ -317,7 +317,7 @@
         mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
         mContext.unregisterComponentCallbacks(mComponentCallbacks);
 
-        if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+        if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
             mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
                     mA11yServicesStateChangeListener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 3f49010..ae39b72 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -284,13 +284,36 @@
         onEdgeChanged();
         onPositionChanged();
 
-        if (mFeaturesChangeListener != null) {
+        boolean shouldSendFeatureChangeNotification =
+                com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()
+                    ? !areFeatureListsIdentical(targetFeatures, newTargetFeatures)
+                    : true;
+        if (mFeaturesChangeListener != null && shouldSendFeatureChangeNotification) {
             mFeaturesChangeListener.onChange(newTargetFeatures);
         }
 
         mMenuAnimationController.fadeOutIfEnabled();
     }
 
+    /**
+     * Returns true if the given feature lists are identical lists, i.e. the same list of {@link
+     * AccessibilityTarget} (equality checked via UID) in the same order.
+     */
+    private boolean areFeatureListsIdentical(
+            List<AccessibilityTarget> currentFeatures, List<AccessibilityTarget> newFeatures) {
+        if (currentFeatures.size() != newFeatures.size()) {
+            return false;
+        }
+
+        for (int i = 0; i < currentFeatures.size(); i++) {
+            if (currentFeatures.get(i).getUid() != newFeatures.get(i).getUid()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private void onMenuFadeEffectInfoChanged(MenuFadeEffectInfo fadeEffectInfo) {
         mMenuAnimationController.updateOpacityWith(fadeEffectInfo.isFadeEffectEnabled(),
                 fadeEffectInfo.getOpacity());
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 73aabc3..438184d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -286,9 +286,7 @@
         if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
             setupAmbientControls();
         }
-        if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) {
-            setupRelatedToolsView(dialog);
-        }
+        setupRelatedToolsView(dialog);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
index 02e65fd..90b8fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
@@ -22,8 +22,6 @@
 import android.content.Context;
 import android.content.Intent;
 
-import com.android.systemui.Flags;
-
 import javax.inject.Inject;
 
 /**
@@ -43,10 +41,6 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!Flags.hearingAidsQsTileDialog()) {
-            return;
-        }
-
         if (ACTION.equals(intent.getAction())) {
             mDialogManager.showDialog(/* expandable= */ null, LAUNCH_SOURCE_A11Y);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index 610e3f8a..fb47d42 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -411,7 +411,7 @@
             stateInteractor: HearingDevicesTileDataInteractor,
             userActionInteractor: HearingDevicesTileUserActionInteractor,
         ): QSTileViewModel {
-            return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+            return if (Flags.qsNewTilesFuture()) {
                 factory.create(
                     TileSpec.create(HEARING_DEVICES_TILE_SPEC),
                     userActionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7ebe52f..c02784d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -31,6 +31,7 @@
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.CameraProtectionModule;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.KairosCoreStartableModule;
 import com.android.systemui.SystemUISecondaryUserService;
 import com.android.systemui.activity.ActivityManagerModule;
 import com.android.systemui.ambient.dagger.AmbientModule;
@@ -232,6 +233,7 @@
         FlagsModule.class,
         FlagDependenciesModule.class,
         FooterActionsModule.class,
+        KairosCoreStartableModule.class,
         GestureModule.class,
         InputMethodModule.class,
         KeyEventRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index c40adfe..08e0a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -33,6 +33,7 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
@@ -45,11 +46,13 @@
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
 
 sealed interface TutorialActionState {
     data object NotStarted : TutorialActionState
@@ -82,18 +85,25 @@
 ) {
     Column(
         verticalArrangement = Arrangement.Center,
-        modifier =
-            Modifier.fillMaxSize()
-                .background(config.colors.background)
-                .safeDrawingPadding()
-                .padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp),
+        modifier = Modifier.fillMaxSize().background(config.colors.background).safeDrawingPadding(),
     ) {
+        val isCompactWindow = hasCompactWindowSize()
         when (LocalConfiguration.current.orientation) {
             Configuration.ORIENTATION_LANDSCAPE -> {
-                HorizontalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+                HorizontalDescriptionAndAnimation(
+                    actionState,
+                    config,
+                    isCompactWindow,
+                    Modifier.weight(1f),
+                )
             }
             else -> {
-                VerticalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+                VerticalDescriptionAndAnimation(
+                    actionState,
+                    config,
+                    isCompactWindow,
+                    Modifier.weight(1f),
+                )
             }
         }
         val buttonAlpha by animateFloatAsState(if (actionState is Finished) 1f else 0f)
@@ -109,11 +119,15 @@
 private fun HorizontalDescriptionAndAnimation(
     actionState: TutorialActionState,
     config: TutorialScreenConfig,
+    isCompactWindow: Boolean,
     modifier: Modifier = Modifier,
 ) {
-    Row(modifier = modifier.fillMaxWidth()) {
-        TutorialDescription(actionState, config, modifier = Modifier.weight(1f))
-        Spacer(modifier = Modifier.width(70.dp))
+    Row(
+        modifier =
+            modifier.fillMaxWidth().padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp)
+    ) {
+        TutorialDescription(actionState, config, isCompactWindow, modifier = Modifier.weight(1f))
+        Spacer(modifier = Modifier.width(24.dp))
         TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
     }
 }
@@ -122,20 +136,25 @@
 private fun VerticalDescriptionAndAnimation(
     actionState: TutorialActionState,
     config: TutorialScreenConfig,
+    isCompactWindow: Boolean,
     modifier: Modifier = Modifier,
 ) {
-    Column(modifier = modifier.fillMaxWidth().padding(horizontal = 40.dp, vertical = 40.dp)) {
-        Spacer(modifier = Modifier.weight(0.1f))
+    val horizontalPadding = if (isCompactWindow) 24.dp else 96.dp
+    // Represents the majority of tablets in portrait - we need extra spacer at the top and bottom
+    val isTablet = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
+    Column(
+        modifier =
+            modifier.fillMaxWidth().padding(start = 0.dp, top = 100.dp, end = 0.dp, bottom = 8.dp)
+    ) {
+        if (isTablet) Spacer(modifier = Modifier.weight(0.3f))
         TutorialDescription(
             actionState,
             config,
-            modifier =
-                Modifier.weight(0.2f)
-                    // extra padding to better align with animation which has embedded padding
-                    .padding(horizontal = 15.dp),
+            isCompactWindow,
+            modifier = Modifier.weight(1f).padding(horizontal = horizontalPadding),
         )
-        Spacer(modifier = Modifier.width(70.dp))
-        TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
+        TutorialAnimation(actionState, config, modifier = Modifier.weight(1.8f).fillMaxWidth())
+        if (isTablet) Spacer(modifier = Modifier.weight(0.3f))
     }
 }
 
@@ -143,6 +162,7 @@
 fun TutorialDescription(
     actionState: TutorialActionState,
     config: TutorialScreenConfig,
+    isCompactWindow: Boolean,
     modifier: Modifier = Modifier,
 ) {
     val focusRequester = remember { FocusRequester() }
@@ -159,7 +179,9 @@
     Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
         Text(
             text = stringResource(id = titleTextId),
-            style = MaterialTheme.typography.displayLarge,
+            style =
+                if (isCompactWindow) MaterialTheme.typography.headlineLarge
+                else MaterialTheme.typography.displayMedium,
             color = config.colors.title,
             modifier = Modifier.focusRequester(focusRequester).focusable(),
         )
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index 21407f3..02b4037 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -27,6 +27,7 @@
 import com.android.internal.R
 import com.android.internal.annotations.VisibleForTesting
 import com.android.settingslib.Utils
+import com.android.systemui.Flags
 import com.android.systemui.media.controls.ui.view.MediaViewHolder
 import com.android.systemui.monet.ColorScheme
 import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
@@ -51,7 +52,7 @@
 open class AnimatingColorTransition(
     private val defaultColor: Int,
     private val extractColor: (ColorScheme) -> Int,
-    private val applyColor: (Int) -> Unit
+    private val applyColor: (Int) -> Unit,
 ) : AnimatorUpdateListener, ColorTransition {
 
     private val argbEvaluator = ArgbEvaluator()
@@ -105,35 +106,52 @@
     private val mediaViewHolder: MediaViewHolder,
     private val multiRippleController: MultiRippleController,
     private val turbulenceNoiseController: TurbulenceNoiseController,
-    animatingColorTransitionFactory: AnimatingColorTransitionFactory
+    animatingColorTransitionFactory: AnimatingColorTransitionFactory,
 ) {
     constructor(
         context: Context,
         mediaViewHolder: MediaViewHolder,
         multiRippleController: MultiRippleController,
-        turbulenceNoiseController: TurbulenceNoiseController
+        turbulenceNoiseController: TurbulenceNoiseController,
     ) : this(
         context,
         mediaViewHolder,
         multiRippleController,
         turbulenceNoiseController,
-        ::AnimatingColorTransition
+        ::AnimatingColorTransition,
     )
+
     var loadingEffect: LoadingEffect? = null
 
-    val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
+    val bgColor =
+        if (Flags.mediaControlsUiUpdate()) {
+            context.getColor(R.color.materialColorOnSurface)
+        } else {
+            context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
+        }
+
+    val textColor = context.getColor(R.color.materialColorInverseOnSurface)
+    val buttonBgColor = context.getColor(R.color.materialColorPrimary)
+    val insideButtonColor = context.getColor(R.color.materialColorOnPrimary)
+
     val surfaceColor =
         animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor ->
             val colorList = ColorStateList.valueOf(surfaceColor)
-            mediaViewHolder.seamlessIcon.imageTintList = colorList
-            mediaViewHolder.seamlessText.setTextColor(surfaceColor)
             mediaViewHolder.albumView.backgroundTintList = colorList
             mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
+
+            if (Flags.mediaControlsUiUpdate()) return@animatingColorTransitionFactory
+            mediaViewHolder.seamlessIcon.imageTintList = colorList
+            mediaViewHolder.seamlessText.setTextColor(surfaceColor)
         }
     val accentPrimary =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorPrimary),
-            ::accentPrimaryFromScheme
+            if (Flags.mediaControlsUiUpdate()) {
+                buttonBgColor
+            } else {
+                loadDefaultColor(R.attr.textColorPrimary)
+            },
+            ::accentPrimaryFromScheme,
         ) { accentPrimary ->
             val accentColorList = ColorStateList.valueOf(accentPrimary)
             mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
@@ -145,8 +163,12 @@
 
     val accentSecondary =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorPrimary),
-            ::accentSecondaryFromScheme
+            if (Flags.mediaControlsUiUpdate()) {
+                buttonBgColor
+            } else {
+                loadDefaultColor(R.attr.textColorPrimary)
+            },
+            ::accentSecondaryFromScheme,
         ) { accentSecondary ->
             val colorList = ColorStateList.valueOf(accentSecondary)
             (mediaViewHolder.seamlessButton.background as? RippleDrawable)?.let {
@@ -157,7 +179,11 @@
 
     val colorSeamless =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorPrimary),
+            if (Flags.mediaControlsUiUpdate()) {
+                buttonBgColor
+            } else {
+                loadDefaultColor(R.attr.textColorPrimary)
+            },
             { colorScheme: ColorScheme ->
                 // A1-100 dark in dark theme, A1-200 in light theme
                 if (
@@ -170,13 +196,17 @@
             { seamlessColor: Int ->
                 val accentColorList = ColorStateList.valueOf(seamlessColor)
                 mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
-            }
+            },
         )
 
     val textPrimary =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorPrimary),
-            ::textPrimaryFromScheme
+            if (Flags.mediaControlsUiUpdate()) {
+                textColor
+            } else {
+                loadDefaultColor(R.attr.textColorPrimary)
+            },
+            ::textPrimaryFromScheme,
         ) { textPrimary ->
             mediaViewHolder.titleText.setTextColor(textPrimary)
             val textColorList = ColorStateList.valueOf(textPrimary)
@@ -192,25 +222,41 @@
 
     val textPrimaryInverse =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorPrimaryInverse),
-            ::textPrimaryInverseFromScheme
+            if (Flags.mediaControlsUiUpdate()) {
+                insideButtonColor
+            } else {
+                loadDefaultColor(R.attr.textColorPrimaryInverse)
+            },
+            ::textPrimaryInverseFromScheme,
         ) { textPrimaryInverse ->
-            mediaViewHolder.actionPlayPause.imageTintList =
-                ColorStateList.valueOf(textPrimaryInverse)
+            val colorList = ColorStateList.valueOf(textPrimaryInverse)
+            mediaViewHolder.actionPlayPause.imageTintList = colorList
+
+            if (!Flags.mediaControlsUiUpdate()) return@animatingColorTransitionFactory
+            mediaViewHolder.seamlessIcon.imageTintList = colorList
+            mediaViewHolder.seamlessText.setTextColor(textPrimaryInverse)
         }
 
     val textSecondary =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorSecondary),
-            ::textSecondaryFromScheme
+            if (Flags.mediaControlsUiUpdate()) {
+                textColor
+            } else {
+                loadDefaultColor(R.attr.textColorSecondary)
+            },
+            ::textSecondaryFromScheme,
         ) { textSecondary ->
             mediaViewHolder.artistText.setTextColor(textSecondary)
         }
 
     val textTertiary =
         animatingColorTransitionFactory(
-            loadDefaultColor(R.attr.textColorTertiary),
-            ::textTertiaryFromScheme
+            if (Flags.mediaControlsUiUpdate()) {
+                textColor
+            } else {
+                loadDefaultColor(R.attr.textColorTertiary)
+            },
+            ::textTertiaryFromScheme,
         ) { textTertiary ->
             mediaViewHolder.seekBar.progressBackgroundTintList =
                 ColorStateList.valueOf(textTertiary)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 74563ff..22b742f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -29,7 +29,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Flags;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
 import com.android.systemui.animation.Expandable;
@@ -138,9 +137,4 @@
     public CharSequence getTileLabel() {
         return mContext.getString(R.string.quick_settings_hearing_devices_label);
     }
-
-    @Override
-    public boolean isAvailable() {
-        return Flags.hearingAidsQsTileDialog();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
index ec0a4e9..33b7feb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.Flags
 import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
@@ -62,8 +61,7 @@
             .flowOn(backgroundContext)
             .distinctUntilChanged()
 
-    override fun availability(user: UserHandle): Flow<Boolean> =
-        flowOf(Flags.hearingAidsQsTileDialog())
+    override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
 
     private fun getModel() =
         HearingDevicesTileModel(
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 76591ac..7b55e83 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
@@ -1245,15 +1245,19 @@
     @Override
     public void setHeadsUpTop(float headsUpTop) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
-        mAmbientState.setHeadsUpTop(headsUpTop);
-        requestChildrenUpdate();
+        if (mAmbientState.getHeadsUpTop() != headsUpTop) {
+            mAmbientState.setHeadsUpTop(headsUpTop);
+            requestChildrenUpdate();
+        }
     }
 
     @Override
     public void setHeadsUpBottom(float headsUpBottom) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
-        mAmbientState.setHeadsUpBottom(headsUpBottom);
-        mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
+        if (mAmbientState.getHeadsUpBottom() != headsUpBottom) {
+            mAmbientState.setHeadsUpBottom(headsUpBottom);
+            mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 66a900b..c8a5840 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -218,9 +218,11 @@
             verticalArrangement = Arrangement.Center,
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
+            // contentDescription is set to null because the icon is decorative and we don't want to
+            // repeat the text twice
             Icon(
                 imageVector = icon,
-                contentDescription = text,
+                contentDescription = null,
                 modifier = Modifier.width(30.dp).height(30.dp),
                 tint = iconColor,
             )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
index 4f79f7b4..cb38cc3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
@@ -18,6 +18,7 @@
 
 import android.content.applicationContext
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.volumeDialogController
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
@@ -74,6 +75,7 @@
     volumeDialogSliderType = type
     applicationContext = parentKosmos.applicationContext
     testScope = parentKosmos.testScope
+    testDispatcher = parentKosmos.testDispatcher
 
     volumeDialogController = parentKosmos.volumeDialogController
     mediaControllerInteractor = parentKosmos.mediaControllerInteractor
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
index bd2173c..c9fae70 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
@@ -100,7 +100,7 @@
      * observation of new emissions. It will however *not* cancel any running effects from previous
      * emissions. To achieve this behavior, use [launchScope] or [asyncScope] to create a child
      * build scope:
-     * ``` kotlin
+     * ```
      *   val job = launchScope {
      *       events.observe { x ->
      *           launchEffect { longRunningEffect(x) }
@@ -141,7 +141,7 @@
      *
      * By default, [builder] is only running while the returned [Events] is being
      * [observed][observe]. If you want it to run at all times, simply add a no-op observer:
-     * ``` kotlin
+     * ```
      *   events { ... }.apply { observe() }
      * ```
      */
@@ -158,7 +158,7 @@
      *
      * By default, [builder] is only running while the returned [Events] is being
      * [observed][observe]. If you want it to run at all times, simply add a no-op observer:
-     * ``` kotlin
+     * ```
      *   events { ... }.apply { observe() }
      * ```
      *
@@ -196,7 +196,7 @@
      * outside of the current Kairos transaction; when [transform] returns, the returned value is
      * emitted from the result [Events] in a new transaction.
      *
-     * ``` kotlin
+     * ```
      *     fun <A, B> Events<A>.mapAsyncLatest(transform: suspend (A) -> B): Events<B> =
      *         mapLatestBuild { a -> asyncEvent { transform(a) } }.flatten()
      * ```
@@ -571,7 +571,7 @@
 
     /**
      * Shorthand for:
-     * ``` kotlin
+     * ```
      * flow.toEvents().holdState(initialValue)
      * ```
      */
@@ -579,7 +579,7 @@
 
     /**
      * Shorthand for:
-     * ``` kotlin
+     * ```
      * flow.scan(initialValue, operation).toEvents().holdState(initialValue)
      * ```
      */
@@ -588,7 +588,7 @@
 
     /**
      * Shorthand for:
-     * ``` kotlin
+     * ```
      * flow.scan(initialValue) { a, f -> f(a) }.toEvents().holdState(initialValue)
      * ```
      */
@@ -665,7 +665,7 @@
      * be used to make further modifications to the Kairos network, and/or perform side-effects via
      * [effect].
      *
-     * ``` kotlin
+     * ```
      *     fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
      *         block(sample())
      *         changes.observeBuild(block)
@@ -698,7 +698,7 @@
  * outside of the current Kairos transaction; when it completes, the returned [Events] emits in a
  * new transaction.
  *
- * ``` kotlin
+ * ```
  *   fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
  *       events { emit(block()) }.apply { observe() }
  * ```
@@ -719,7 +719,7 @@
  * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
  * -Latest combinator, for example.
  *
- * ``` kotlin
+ * ```
  *   fun BuildScope.effect(
  *       context: CoroutineContext = EmptyCoroutineContext,
  *       block: EffectScope.() -> Unit,
@@ -740,7 +740,7 @@
  * done because the current [BuildScope] might be deactivated within this transaction, perhaps due
  * to a -Latest combinator. If this happens, then the coroutine will never actually be started.
  *
- * ``` kotlin
+ * ```
  *   fun BuildScope.launchEffect(block: suspend KairosScope.() -> Unit): Job =
  *       effect { effectCoroutineScope.launch { block() } }
  * ```
@@ -757,7 +757,7 @@
  * to a -Latest combinator. If this happens, then the coroutine will never actually be started.
  *
  * Shorthand for:
- * ``` kotlin
+ * ```
  *   fun <R> BuildScope.asyncEffect(block: suspend KairosScope.() -> R): Deferred<R> =
  *       CompletableDeferred<R>.apply {
  *               effect { effectCoroutineScope.launch { complete(block()) } }
@@ -789,7 +789,7 @@
  *
  * By default, [builder] is only running while the returned [Events] is being
  * [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
  * events { ... }.apply { observe() }
  * ```
  *
@@ -813,7 +813,7 @@
  *
  * By default, [builder] is only running while the returned [Events] is being
  * [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
  * events { ... }.apply { observe() }
  * ```
  *
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
index 8f468c1..1a13773 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
@@ -105,7 +105,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Lazy<Events<A>>.defer() = deferredEvents { value }
  * ```
  *
@@ -122,7 +122,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> DeferredValue<Events<A>>.defer() = deferredEvents { get() }
  * ```
  *
@@ -160,7 +160,7 @@
  * Returns an [Events] that contains only the non-null results of applying [transform] to each value
  * of the original [Events].
  *
- * ``` kotlin
+ * ```
  *  fun <A> Events<A>.mapNotNull(transform: TransactionScope.(A) -> B?): Events<B> =
  *      mapMaybe { if (it == null) absent else present(it) }
  * ```
@@ -201,7 +201,7 @@
  * Returns an [Events] that invokes [action] before each value of the original [Events] is emitted.
  * Useful for logging and debugging.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> =
  *       map { it.also { action(it) } }
  * ```
@@ -220,7 +220,7 @@
  * Splits an [Events] of pairs into a pair of [Events], where each returned [Events] emits half of
  * the original.
  *
- * ``` kotlin
+ * ```
  *   fun <A, B> Events<Pair<A, B>>.unzip(): Pair<Events<A>, Events<B>> {
  *       val lefts = map { it.first }
  *       val rights = map { it.second }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
index 8ca5ac8..d412b84 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
@@ -29,7 +29,7 @@
 /**
  * Returns an [Events] containing only values of the original [Events] that are not null.
  *
- * ``` kotlin
+ * ```
  *  fun <A> Events<A?>.filterNotNull(): Events<A> = mapNotNull { it }
  * ```
  *
@@ -41,7 +41,7 @@
 /**
  * Returns an [Events] containing only values of the original [Events] that are instances of [A].
  *
- * ``` kotlin
+ * ```
  *   inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
  *       mapNotNull { it as? A }
  * ```
@@ -55,7 +55,7 @@
 /**
  * Returns an [Events] containing only values of the original [Events] that are present.
  *
- * ``` kotlin
+ * ```
  *  fun <A> Events<Maybe<A>>.filterPresent(): Events<A> = mapMaybe { it }
  * ```
  *
@@ -69,7 +69,7 @@
  * Returns an [Events] containing only values of the original [Events] that satisfy the given
  * [predicate].
  *
- * ``` kotlin
+ * ```
  *   fun <A> Events<A>.filter(predicate: TransactionScope.(A) -> Boolean): Events<A> =
  *       mapMaybe { if (predicate(it)) present(it) else absent }
  * ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
index 45da34a..27fc1b4 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
@@ -63,7 +63,7 @@
  * downstream [Events]. The downstream [Events] are associated with a [key][K], which is derived
  * from each emission of the original [Events] via [extractKey].
  *
- * ``` kotlin
+ * ```
  *   fun <K, A> Events<A>.groupBy(
  *       numKeys: Int? = null,
  *       extractKey: TransactionScope.(A) -> K,
@@ -108,7 +108,7 @@
  * Using this is equivalent to `upstream.filter(predicate) to upstream.filter { !predicate(it) }`
  * but is more efficient; specifically, [partition] will only invoke [predicate] once per element.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Events<A>.partition(
  *       predicate: TransactionScope.(A) -> Boolean
  *   ): Pair<Events<A>, Events<A>> =
@@ -133,7 +133,7 @@
  * [First]s and once for [Second]s, but is slightly more efficient; specifically, the
  * [filterIsInstance] check is only performed once per element.
  *
- * ``` kotlin
+ * ```
  *   fun <A, B> Events<Either<A, B>>.partitionEither(): Pair<Events<A>, Events<B>> =
  *     map { it.asThese() }.partitionThese()
  * ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
index d88ae3b8..02941bd 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
@@ -62,7 +62,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Lazy<Incremental<K, V>>.defer() = deferredIncremental { value }
  * ```
  */
@@ -78,7 +78,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> DeferredValue<Incremental<K, V>>.defer() = deferredIncremental { get() }
  * ```
  */
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
index de9dca4..cc4ce53 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
@@ -33,7 +33,7 @@
  * function is used to combine coincident emissions to produce the result value to be emitted by the
  * merged [Events].
  *
- * ``` kotlin
+ * ```
  * fun <A> Events<A>.mergeWith(
  *     other: Events<A>,
  *     transformCoincidence: TransactionScope.(A, A) -> A = { a, _ -> a },
@@ -62,7 +62,7 @@
  * Merges the given [Events] into a single [Events] that emits events from all. All coincident
  * emissions are collected into the emitted [List], preserving the input ordering.
  *
- * ``` kotlin
+ * ```
  *   fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().merge()
  * ```
  *
@@ -76,7 +76,7 @@
  * Merges the given [Events] into a single [Events] that emits events from all. In the case of
  * coincident emissions, the emission from the left-most [Events] is emitted.
  *
- * ``` kotlin
+ * ```
  *   fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mergeLeft()
  * ```
  *
@@ -92,7 +92,7 @@
  * function is used to combine coincident emissions to produce the result value to be emitted by the
  * merged [Events].
  *
- * ``` kotlin
+ * ```
  *   fun <A> merge(vararg events: Events<A>, transformCoincidence: (A, A) -> A): Events<A> =
  *       merge(*events).map { l -> l.reduce(transformCoincidence) }
  * ```
@@ -117,7 +117,7 @@
  * coincident emissions, the emission from the left-most [Events] is emitted.
  *
  * Semantically equivalent to the following definition:
- * ``` kotlin
+ * ```
  *   fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
  *       merge().mapCheap { it.first() }
  * ```
@@ -135,7 +135,7 @@
  * Creates a new [Events] that emits events from all given [Events]. All simultaneous emissions are
  * collected into the emitted [List], preserving the input ordering.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Sequence<Events<A>>.merge(): Events<List<A>> = asIterable().merge()
  * ```
  *
@@ -148,7 +148,7 @@
  * collected into the emitted [Map], and are given the same key of the associated [Events] in the
  * input [Map].
  *
- * ``` kotlin
+ * ```
  *   fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
  *       asSequence()
  *           .map { (k, events) -> events.map { a -> k to a } }
@@ -173,7 +173,7 @@
  * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
  *
  * Conceptually this is equivalent to:
- * ``` kotlin
+ * ```
  *   fun <K, V> State<Map<K, V>>.mergeEventsIncrementally(): Events<Map<K, V>> =
  *       map { it.merge() }.switchEvents()
  * ```
@@ -218,7 +218,7 @@
  * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
  *
  * Conceptually this is equivalent to:
- * ``` kotlin
+ * ```
  *   fun <K, V> State<Map<K, V>>.mergeEventsIncrementallyPromptly(): Events<Map<K, V>> =
  *       map { it.merge() }.switchEventsPromptly()
  * ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
index 22ca83c..e8b005e 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
@@ -76,7 +76,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Lazy<State<A>>.defer() = deferredState { value }
  * ```
  */
@@ -91,7 +91,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> DeferredValue<State<A>>.defer() = deferredState { get() }
  * ```
  */
@@ -150,7 +150,7 @@
  * Splits a [State] of pairs into a pair of [Events][State], where each returned [State] holds half
  * of the original.
  *
- * ``` kotlin
+ * ```
  *   fun <A, B> State<Pair<A, B>>.unzip(): Pair<State<A>, State<B>> {
  *       val first = map { it.first }
  *       val second = map { it.second }
@@ -186,7 +186,7 @@
 /**
  * Returns a [State] that behaves like the current value of the original [State].
  *
- * ``` kotlin
+ * ```
  *   fun <A> State<State<A>>.flatten() = flatMap { it }
  * ```
  *
@@ -201,7 +201,7 @@
  * recent value is used.
  *
  * Effectively equivalent to:
- * ``` kotlin
+ * ```
  *     ConflatedMutableEvents(kairosNetwork).holdState(initialValue)
  * ```
  */
@@ -328,7 +328,7 @@
  * Like [changes] but also includes the old value of this [State].
  *
  * Shorthand for:
- * ``` kotlin
+ * ```
  *     stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
  * ```
  */
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
index faeffe8..8020896 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
@@ -116,7 +116,7 @@
      * [Events] emitted from this, following the patch rules outlined in
      * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
      *     initialEvents: DeferredValue<Map<K, Events<V>>>,
      *   ): Events<Map<K, V>> =
@@ -135,7 +135,7 @@
      * [Events] emitted from this, following the patch rules outlined in
      * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
      *     initialEvents: DeferredValue<Map<K, Events<V>>>,
      *   ): Events<Map<K, V>> =
@@ -155,7 +155,7 @@
      * [Events] emitted from this, following the patch rules outlined in
      * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
      *     initialEvents: Map<K, Events<V>>,
      *   ): Events<Map<K, V>> =
@@ -174,7 +174,7 @@
      * [Events] emitted from this, following the patch rules outlined in
      * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
      *     initialEvents: Map<K, Events<V>>,
      *   ): Events<Map<K, V>> =
@@ -220,7 +220,7 @@
      * [mapLatestStateful], accumulation is not stopped with each subsequent emission of the
      * original [Events].
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> Events<A>.mapStateful(transform: StateScope.(A) -> B): Events<B> =
      *       map { statefully { transform(it) } }.applyStatefuls()
      * ```
@@ -234,7 +234,7 @@
      *
      * Unlike [applyLatestStateful], state accumulation is not stopped with each state change.
      *
-     * ``` kotlin
+     * ```
      *   fun <A> State<Stateful<A>>.applyStatefuls(): State<A> =
      *       changes
      *           .applyStatefuls()
@@ -252,7 +252,7 @@
      * Returns an [Events] that acts like the most recent [Events] to be emitted from the original
      * [Events].
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<Events<A>>.flatten() = holdState(emptyEvents).switchEvents()
      * ```
      *
@@ -267,7 +267,7 @@
      * [transform] can perform state accumulation via its [StateScope] receiver. With each
      * invocation of [transform], state accumulation from previous invocation is stopped.
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> Events<A>.mapLatestStateful(transform: StateScope.(A) -> B): Events<B> =
      *       map { statefully { transform(it) } }.applyLatestStateful()
      * ```
@@ -282,7 +282,7 @@
      * [transform] can perform state accumulation via its [StateScope] receiver. With each
      * invocation of [transform], state accumulation from previous invocation is stopped.
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> Events<A>.flatMapLatestStateful(
      *       transform: StateScope.(A) -> Events<B>
      *   ): Events<B> =
@@ -495,7 +495,7 @@
      *
      * The optional [numKeys] argument is an optimization used to initialize the internal storage.
      *
-     * ``` kotlin
+     * ```
      *   fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
      *       numKeys: Int? = null,
      *       transform: StateScope.(A) -> B,
@@ -516,7 +516,7 @@
      * If the original [Events] is emitting an event at this exact time, then it will be the only
      * even emitted from the result [Events].
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.nextOnly(): Events<A> =
      *       EventsLoop<A>().apply {
      *           loopback = map { emptyEvents }.holdState(this@nextOnly).switchEvents()
@@ -535,7 +535,7 @@
     /**
      * Returns an [Events] that skips the next emission of the original [Events].
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.skipNext(): Events<A> =
      *       nextOnly().map { this@skipNext }.holdState(emptyEvents).switchEvents()
      * ```
@@ -554,7 +554,7 @@
      * If the original [Events] emits at the same time as [stop], then the returned [Events] will
      * emit that value.
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.takeUntil(stop: Events<*>): Events<A> =
      *       stop.map { emptyEvents }.nextOnly().holdState(this).switchEvents()
      * ```
@@ -586,7 +586,7 @@
      * Returns an [Events] that emits values from the original [Events] up to and including a value
      * is emitted that satisfies [predicate].
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.takeUntil(predicate: TransactionScope.(A) -> Boolean): Events<A> =
      *       takeUntil(filter(predicate))
      * ```
@@ -602,7 +602,7 @@
      * have been processed; this keeps the value of the [State] consistent during the entire Kairos
      * transaction.
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> Events<A>.foldState(
      *       initialValue: B,
      *       transform: TransactionScope.(A, B) -> B,
@@ -630,7 +630,7 @@
      * have been processed; this keeps the value of the [State] consistent during the entire Kairos
      * transaction.
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> Events<A>.foldStateDeferred(
      *       initialValue: DeferredValue<B>,
      *       transform: TransactionScope.(A, B) -> B,
@@ -663,7 +663,7 @@
      * have been processed; this keeps the value of the [State] consistent during the entire Kairos
      * transaction.
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<Stateful<A>>.holdLatestStateful(init: Stateful<A>): State<A> {
      *       val (changes, initApplied) = applyLatestStateful(init)
      *       return changes.holdStateDeferred(initApplied)
@@ -724,7 +724,7 @@
      * Returns an [Events] that wraps each emission of the original [Events] into an [IndexedValue],
      * containing the emitted value and its index (starting from zero).
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.withIndex(): Events<IndexedValue<A>> {
      *     val index = fold(0) { _, oldIdx -> oldIdx + 1 }
      *     return sample(index) { a, idx -> IndexedValue(idx, a) }
@@ -740,7 +740,7 @@
      * Returns an [Events] containing the results of applying [transform] to each value of the
      * original [Events] and its index (starting from zero).
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.mapIndexed(transform: TransactionScope.(Int, A) -> B): Events<B> {
      *       val index = foldState(0) { _, i -> i + 1 }
      *       return sample(index) { a, idx -> transform(idx, a) }
@@ -755,7 +755,7 @@
     /**
      * Returns an [Events] where all subsequent repetitions of the same value are filtered out.
      *
-     * ``` kotlin
+     * ```
      *   fun <A> Events<A>.distinctUntilChanged(): Events<A> {
      *       val state: State<Any?> = holdState(Any())
      *       return filter { it != state.sample() }
@@ -774,7 +774,7 @@
      * Note that the returned [Events] will not emit anything until [other] has emitted at least one
      * value.
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B, C> Events<A>.sample(
      *       other: Events<B>,
      *       transform: TransactionScope.(A, B) -> C,
@@ -796,7 +796,7 @@
      * Returns a [State] that samples the [Transactional] held by the given [State] within the same
      * transaction that the state changes.
      *
-     * ``` kotlin
+     * ```
      *   fun <A> State<Transactional<A>>.sampleTransactionals(): State<A> =
      *       changes
      *           .sampleTransactionals()
@@ -815,7 +815,7 @@
      * Note that this is less efficient than [State.map], which should be preferred if [transform]
      * does not need access to [TransactionScope].
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> State<A>.mapTransactionally(transform: TransactionScope.(A) -> B): State<B> =
      *       map { transactionally { transform(it) } }.sampleTransactionals()
      * ```
@@ -830,7 +830,7 @@
      * Note that this is less efficient than [combine], which should be preferred if [transform]
      * does not need access to [TransactionScope].
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B, Z> combineTransactionally(
      *       stateA: State<A>,
      *       stateB: State<B>,
@@ -895,7 +895,7 @@
      * Note that this is less efficient than [flatMap], which should be preferred if [transform]
      * does not need access to [TransactionScope].
      *
-     * ``` kotlin
+     * ```
      *   fun <A, B> State<A>.flatMapTransactionally(
      *       transform: TransactionScope.(A) -> State<B>
      *   ): State<B> = map { transactionally { transform(it) } }.sampleTransactionals().flatten()
@@ -950,7 +950,7 @@
      * Returns an [Incremental] that reflects the state of the original [Incremental], but also adds
      * / removes entries based on the state of the original's values.
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Incremental<K, State<Maybe<V>>>.applyStateIncrementally(): Incremental<K, V> =
      *       mapValues { (_, v) -> v.changes }
      *           .mergeEventsIncrementallyPromptly()
@@ -971,7 +971,7 @@
      * / removes entries based on the [State] returned from applying [transform] to the original's
      * entries.
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V, U> Incremental<K, V>.mapIncrementalState(
      *       transform: KairosScope.(Map.Entry<K, V>) -> State<Maybe<U>>
      *   ): Incremental<K, U> = mapValues { transform(it) }.applyStateIncrementally()
@@ -986,7 +986,7 @@
      * / removes entries based on the [State] returned from applying [transform] to the original's
      * entries, such that entries are added when that state is `true`, and removed when `false`.
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Incremental<K, V>.filterIncrementally(
      *       transform: KairosScope.(Map.Entry<K, V>) -> State<Boolean>
      *   ): Incremental<K, V> = mapIncrementalState { entry ->
@@ -1004,7 +1004,7 @@
      * Returns an [Incremental] that samples the [Transactionals][Transactional] held by the
      * original within the same transaction that the incremental [updates].
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V> Incremental<K, Transactional<V>>.sampleTransactionals(): Incremental<K, V> =
      *       updates
      *           .map { patch -> patch.mapValues { (k, mv) -> mv.map { it.sample() } } }
@@ -1027,7 +1027,7 @@
      * Note that this is less efficient than [mapValues], which should be preferred if [transform]
      * does not need access to [TransactionScope].
      *
-     * ``` kotlin
+     * ```
      *   fun <K, V, U> Incremental<K, V>.mapValuesTransactionally(
      *       transform: TransactionScope.(Map.Entry<K, V>) -> U
      *   ): Incremental<K, U> =
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
index cf98821..5050511 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
@@ -51,7 +51,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> DeferredValue<Transactional<A>>.defer() = deferredTransactional { get() }
  * ```
  */
@@ -67,7 +67,7 @@
  *
  * Useful for recursive definitions.
  *
- * ``` kotlin
+ * ```
  *   fun <A> Lazy<Transactional<A>>.defer() = deferredTransactional { value }
  * ```
  */
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
index e193a49..3dbc6f0 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
@@ -21,28 +21,28 @@
  *
  * Let's say you want to write a class that is generic over both a map, and the type of data within
  * the map:
- * ``` kotlin
+ * ```
  *   class Foo<TMap, TKey, TValue> {
  *     val container: TMap<TKey, TElement> // disallowed!
  *   }
  * ```
  *
  * You can use `MapK` to represent the "higher-kinded" type variable `TMap`:
- * ``` kotlin
+ * ```
  *   class Foo<TMap, TKey, TValue> {
  *      val container: MapK<TMap, TKey, TValue> // OK!
  *   }
  * ```
  *
  * Note that Kotlin will not let you use the generic type without parameters as `TMap`:
- * ``` kotlin
+ * ```
  *   val fooHk: MapK<HashMap, Int, String> // not allowed: HashMap requires two type parameters
  * ```
  *
  * To work around this, you need to declare a special type-witness object. This object is only used
  * at compile time and can be stripped out by a minifier because it's never used at runtime.
  *
- * ``` kotlin
+ * ```
  *   class Foo<A, B> : MapK<FooWitness, A, B> { ... }
  *   object FooWitness
  *
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
index 8fe41bc..dde5d82 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
@@ -47,7 +47,7 @@
  * Returns a [MapPatch] that, when applied, includes all of the values from the original [Map].
  *
  * Shorthand for:
- * ``` kotlin
+ * ```
  *   mapValues { (key, value) -> Maybe.present(value) }
  * ```
  */
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
index 4754bc4..4373705 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
@@ -69,7 +69,7 @@
  *
  * This can be used instead of Kotlin's built-in nullability (`?.` and `?:`) operators when dealing
  * with complex combinations of nullables:
- * ``` kotlin
+ * ```
  * val aMaybe: Maybe<Any> = ...
  * val bMaybe: Maybe<Any> = ...
  * val result: String = maybe {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8e037c3..b08aa5b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4385,13 +4385,7 @@
 
     private void launchAccessibilityFrameworkFeature(int displayId, ComponentName assignedTarget) {
         if (assignedTarget.equals(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)) {
-            //import com.android.systemui.Flags;
-            if (com.android.systemui.Flags.hearingAidsQsTileDialog()) {
-                launchHearingDevicesDialog();
-            } else {
-                launchAccessibilitySubSettings(displayId,
-                        ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME);
-            }
+            launchHearingDevicesDialog();
         }
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 1dba629b..cffdfbd 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -5666,13 +5666,6 @@
         }
 
         public boolean canAccessAppWidget(Widget widget, int uid, String packageName) {
-            if (isDifferentPackageFromHost(widget.host, packageName)
-                    && isDifferentPackageFromProvider(widget.provider, packageName)) {
-                // Apps providing AppWidget are only allowed to access widgets provided by the
-                // same package. Similarly, apps hosting AppWidget are only allowed to access
-                // widgets hosted by the same package.
-                return false;
-            }
             if (isHostInPackageForUid(widget.host, uid, packageName)) {
                 // Apps hosting the AppWidget have access to it.
                 return true;
@@ -5775,19 +5768,6 @@
                     && provider.id.componentName.getPackageName().equals(packageName);
         }
 
-        private boolean isDifferentPackageFromHost(
-                @Nullable final Host host, @Nullable final String packageName) {
-            return packageName == null || host == null || host.id == null
-                || !packageName.equals(host.id.packageName);
-        }
-
-        private boolean isDifferentPackageFromProvider(
-                @Nullable final Provider provider, @Nullable final String packageName) {
-            return packageName == null || provider == null || provider.id == null
-                    || provider.id.componentName == null
-                    || !packageName.equals(provider.id.componentName.getPackageName());
-        }
-
         private boolean isProfileEnabled(int profileId) {
             final long identity = Binder.clearCallingIdentity();
             try {
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 260ea75..5edd9d7 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -21,7 +21,6 @@
 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
-import static android.companion.virtual.VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED;
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
 import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
@@ -492,17 +491,10 @@
                     this, getDeviceId(), getPersistentDeviceId(), mParams.getName());
         }
 
-        if (Flags.dynamicPolicy()) {
-            mActivityPolicyExemptions = new ArraySet<>(
-                    mParams.getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
-                            ? mParams.getBlockedActivities()
-                            : mParams.getAllowedActivities());
-        } else {
-            mActivityPolicyExemptions =
-                    mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED
-                            ? mParams.getBlockedActivities()
-                            : mParams.getAllowedActivities();
-        }
+        mActivityPolicyExemptions = new ArraySet<>(
+                mParams.getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
+                        ? mParams.getBlockedActivities()
+                        : mParams.getAllowedActivities());
 
         if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
             final String imeId = mParams.getInputMethodComponent().flattenToShortString();
@@ -587,12 +579,8 @@
     @Override  // Binder call
     public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
             @VirtualDeviceParams.PolicyType int policyType) {
-        if (Flags.dynamicPolicy()) {
-            synchronized (mVirtualDeviceLock) {
-                return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
-            }
-        } else {
-            return mParams.getDevicePolicy(policyType);
+        synchronized (mVirtualDeviceLock) {
+            return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
         }
     }
 
@@ -891,9 +879,6 @@
     public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
         checkCallerIsDeviceOwner();
-        if (!Flags.dynamicPolicy()) {
-            return;
-        }
 
         switch (policyType) {
             case POLICY_TYPE_RECENTS:
@@ -924,23 +909,21 @@
                 }
                 break;
             case POLICY_TYPE_CLIPBOARD:
-                if (Flags.crossDeviceClipboard()) {
-                    if (devicePolicy == DEVICE_POLICY_CUSTOM
+                if (devicePolicy == DEVICE_POLICY_CUSTOM
                             && mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
                             != PackageManager.PERMISSION_GRANTED) {
-                        throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
-                                + "set a custom clipboard policy.");
-                    }
-                    synchronized (mVirtualDeviceLock) {
-                        for (int i = 0; i < mVirtualDisplays.size(); i++) {
-                            VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
-                            if (!wrapper.isTrusted() && !wrapper.isMirror()) {
-                                throw new SecurityException("All displays must be trusted for "
-                                        + "devices with custom clipboard policy.");
-                            }
+                    throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+                            + "set a custom clipboard policy.");
+                }
+                synchronized (mVirtualDeviceLock) {
+                    for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                        VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+                        if (!wrapper.isTrusted() && !wrapper.isMirror()) {
+                            throw new SecurityException("All displays must be trusted for "
+                                    + "devices with custom clipboard policy.");
                         }
-                        mDevicePolicies.put(policyType, devicePolicy);
                     }
+                    mDevicePolicies.put(policyType, devicePolicy);
                 }
                 break;
             case POLICY_TYPE_BLOCKED_ACTIVITY:
@@ -1443,15 +1426,11 @@
     private GenericWindowPolicyController createWindowPolicyControllerLocked(
             @NonNull Set<String> displayCategories) {
         final boolean activityLaunchAllowedByDefault =
-                Flags.dynamicPolicy()
-                    ? getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
-                    : mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED;
+                getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT;
         final boolean crossTaskNavigationAllowedByDefault =
                 mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED;
         final boolean showTasksInHostDeviceRecents =
                 getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT;
-        final ComponentName homeComponent =
-                Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
 
         if (mActivityListenerAdapter == null) {
             mActivityListenerAdapter = new GwpcActivityListener();
@@ -1472,7 +1451,7 @@
                 mActivityListenerAdapter,
                 displayCategories,
                 showTasksInHostDeviceRecents,
-                homeComponent);
+                mParams.getHomeComponent());
         gwpc.registerRunningAppsChangedListener(/* listener= */ this);
         return gwpc;
     }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 40726b4..a60fa69 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -216,17 +216,14 @@
                 VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
                 mActivityInterceptorCallback);
 
-        if (Flags.persistentDeviceIdApi()) {
-            CompanionDeviceManager cdm =
-                    getContext().getSystemService(CompanionDeviceManager.class);
-            if (cdm != null) {
-                onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
-                cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
-                        this::onCdmAssociationsChanged, UserHandle.USER_ALL);
-            } else {
-                Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
-                        + " will be available.");
-            }
+        CompanionDeviceManager cdm = getContext().getSystemService(CompanionDeviceManager.class);
+        if (cdm != null) {
+            onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
+            cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
+                    this::onCdmAssociationsChanged, UserHandle.USER_ALL);
+        } else {
+            Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
+                    + " will be available.");
         }
         if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()) {
             mStrongAuthTracker = new StrongAuthTracker(getContext());
@@ -338,14 +335,6 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             getContext().sendBroadcastAsUser(i, UserHandle.ALL);
-
-            if (!Flags.persistentDeviceIdApi()) {
-                synchronized (mVirtualDeviceManagerLock) {
-                    if (mVirtualDevices.size() == 0) {
-                        unregisterCdmAssociationListener();
-                    }
-                }
-            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -379,21 +368,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
-    private void registerCdmAssociationListener() {
-        final CompanionDeviceManager cdm = getContext().getSystemService(
-                CompanionDeviceManager.class);
-        cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
-                mCdmAssociationListener);
-    }
-
-    @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
-    private void unregisterCdmAssociationListener() {
-        final CompanionDeviceManager cdm = getContext().getSystemService(
-                CompanionDeviceManager.class);
-        cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
-    }
-
     void onCdmAssociationsChanged(List<AssociationInfo> associations) {
         ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>();
         for (int i = 0; i < associations.size(); ++i) {
@@ -479,9 +453,8 @@
             AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
             if (associationInfo == null) {
                 throw new IllegalArgumentException("No association with ID " + associationId);
-            } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
-                    .contains(associationInfo.getDeviceProfile())
-                    && Flags.persistentDeviceIdApi()) {
+            } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
+                    associationInfo.getDeviceProfile())) {
                 throw new IllegalArgumentException("Unsupported CDM Association device profile "
                         + associationInfo.getDeviceProfile() + " for virtual device creation.");
             }
@@ -522,14 +495,6 @@
             Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
 
             synchronized (mVirtualDeviceManagerLock) {
-                if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
-                    final long callingId = Binder.clearCallingIdentity();
-                    try {
-                        registerCdmAssociationListener();
-                    } finally {
-                        Binder.restoreCallingIdentity(callingId);
-                    }
-                }
                 mVirtualDevices.put(deviceId, virtualDevice);
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4b8770b..50876da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -277,7 +277,9 @@
     private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
 
     /** The default value to {@link #KEY_FREEZER_CUTOFF_ADJ} */
-    private static final int DEFAULT_FREEZER_CUTOFF_ADJ = ProcessList.CACHED_APP_MIN_ADJ;
+    private static final int DEFAULT_FREEZER_CUTOFF_ADJ =
+            Flags.prototypeAggressiveFreezing() ? ProcessList.HOME_APP_ADJ
+                    : ProcessList.CACHED_APP_MIN_ADJ;
 
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index cfcede8..d9be471 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -270,6 +270,16 @@
 }
 
 flag {
+    name: "prototype_aggressive_freezing"
+    namespace: "backstage_power"
+    description: "Grant PROCESS_CAPABILITY_CPU_TIME to as many valid states and aggressively freeze other states"
+    bug: "370798593"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "add_modify_raw_oom_adj_service_level"
     namespace: "backstage_power"
     description: "Add a SERVICE_ADJ level to the modifyRawOomAdj method"
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index e4c36cc..695032e 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -110,7 +110,12 @@
             }
             db.setTransactionSuccessful();
         } finally {
-            db.endTransaction();
+            try {
+                db.endTransaction();
+            } catch (SQLiteException exception) {
+                Slog.e(LOG_TAG, "Couldn't commit transaction when inserting discrete ops, database"
+                        + " file size (bytes) : " + getDatabaseFile().length(), exception);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
index d05ded5..a2465d1 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
@@ -28,21 +28,17 @@
  */
 public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
     private static final String TAG = "PowerStatusMonitorActionFromPlayback";
+    // State that waits for next monitoring.
+    private static final int STATE_WAIT_FOR_NEXT_MONITORING = 1;
 
     // State that waits for <Report Power Status> once sending <Give Device Power Status>
-    // to all external devices.
-    private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
-    // State that waits for next monitoring.
-    private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;
+    // to the TV.
+    private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+
     // Monitoring interval (60s)
     @VisibleForTesting
     protected static final int MONITORING_INTERVAL_MS = 60000;
     // Timeout once sending <Give Device Power Status>
-    private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
-    // Maximum number of retries in case the <Give Device Power Status> failed being sent or times
-    // out.
-    private static final int GIVE_POWER_STATUS_FOR_SOURCE_RETRIES = 5;
-    private int mPowerStatusRetries = 0;
 
     PowerStatusMonitorActionFromPlayback(HdmiCecLocalDevice source) {
         super(source);
@@ -68,11 +64,10 @@
 
     private boolean handleReportPowerStatusFromTv(HdmiCecMessage cmd) {
         int powerStatus = cmd.getParams()[0] & 0xFF;
-        mState = STATE_WAIT_FOR_NEXT_MONITORING;
-        addTimer(mState, MONITORING_INTERVAL_MS);
         if (powerStatus == POWER_STATUS_STANDBY) {
             Slog.d(TAG, "TV reported it turned off, going to sleep.");
             source().getService().standby();
+            finish();
             return true;
         }
         return false;
@@ -80,34 +75,28 @@
 
     @Override
     void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
         switch (mState) {
             case STATE_WAIT_FOR_NEXT_MONITORING:
-                mPowerStatusRetries = 0;
                 queryPowerStatus();
                 break;
             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
-                handleTimeout();
+                mState = STATE_WAIT_FOR_NEXT_MONITORING;
+                addTimer(mState, MONITORING_INTERVAL_MS);
+                break;
+            default:
                 break;
         }
     }
 
     private void queryPowerStatus() {
         sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
-                        Constants.ADDR_TV));
+                Constants.ADDR_TV));
 
         mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
-        addTimer(mState, REPORT_POWER_STATUS_TIMEOUT_MS);
-    }
-
-    private void handleTimeout() {
-        if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS) {
-            if (mPowerStatusRetries++ < GIVE_POWER_STATUS_FOR_SOURCE_RETRIES) {
-                queryPowerStatus();
-            } else {
-                mPowerStatusRetries = 0;
-                mState = STATE_WAIT_FOR_NEXT_MONITORING;
-                addTimer(mState, MONITORING_INTERVAL_MS);
-            }
-        }
+        addTimer(mState, HdmiConfig.TIMEOUT_MS);
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 281db0a..5fe8318 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -40,6 +40,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.IBinder;
@@ -58,6 +59,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.PrintWriter;
@@ -78,6 +80,7 @@
     private final int mUserId;
 
     private final InputMethodManagerService mService;
+    private final UserManagerInternal mUserManagerInternal;
     private final WindowManagerInternal mWindowManagerInternal;
 
     final InputMethodManagerService.ImeDisplayValidator mImeDisplayValidator;
@@ -188,6 +191,7 @@
     public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
             @UserIdInt int userId) {
         this(service,
+                LocalServices.getService(UserManagerInternal.class),
                 LocalServices.getService(WindowManagerInternal.class),
                 LocalServices.getService(WindowManagerInternal.class)::getDisplayImePolicy,
                 new ImeVisibilityPolicy(), userId);
@@ -196,12 +200,15 @@
     @VisibleForTesting
     public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
             @NonNull Injector injector) {
-        this(service, injector.getWmService(), injector.getImeValidator(),
-                new ImeVisibilityPolicy(), injector.getUserId());
+        this(service, injector.getUserManagerService(), injector.getWmService(),
+                injector.getImeValidator(), new ImeVisibilityPolicy(), injector.getUserId());
     }
 
     interface Injector {
         @NonNull
+        UserManagerInternal getUserManagerService();
+
+        @NonNull
         WindowManagerInternal getWmService();
 
         @NonNull
@@ -212,11 +219,13 @@
     }
 
     private ImeVisibilityStateComputer(InputMethodManagerService service,
+            UserManagerInternal userManagerInternal,
             WindowManagerInternal wmService,
             InputMethodManagerService.ImeDisplayValidator imeDisplayValidator,
             ImeVisibilityPolicy imePolicy, @UserIdInt int userId) {
         mUserId = userId;
         mService = service;
+        mUserManagerInternal = userManagerInternal;
         mWindowManagerInternal = wmService;
         mImeDisplayValidator = imeDisplayValidator;
         mPolicy = imePolicy;
@@ -337,7 +346,16 @@
 
     @GuardedBy("ImfLock.class")
     int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) {
-        final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
+        final int displayToShowIme;
+        final PackageManager pm = mService.mContext.getPackageManager();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+                && mUserManagerInternal.isVisibleBackgroundFullUser(mUserId)
+                && Flags.fallbackDisplayForSecondaryUserOnSecondaryDisplay()) {
+            displayToShowIme = mService.computeImeDisplayIdForVisibleBackgroundUserOnAutomotive(
+                    displayId, mUserId, mImeDisplayValidator);
+        } else {
+            displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
+        }
         state.setImeDisplayId(displayToShowIme);
         final boolean imeHiddenByPolicy = displayToShowIme == INVALID_DISPLAY;
         mPolicy.setImeHiddenByDisplayPolicy(imeHiddenByPolicy);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1f414ac..45c7cff 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2345,8 +2345,32 @@
      * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
      */
     static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
-        if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
-            return FALLBACK_DISPLAY_ID;
+        return computeImeDisplayIdForTargetInner(displayId, checker, FALLBACK_DISPLAY_ID);
+    }
+
+    /**
+     * Find the display where the IME should be shown for a visible background user.
+     *
+     * @param displayId the ID of the display where the IME client target is
+     * @param userId the ID of the user who own the IME
+     * @param checker   instance of {@link ImeDisplayValidator} which is used for
+     *                  checking display config to adjust the final target display
+     * @return the ID of the display where the IME should be shown or
+     * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
+     * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
+     */
+    int computeImeDisplayIdForVisibleBackgroundUserOnAutomotive(
+            int displayId, @UserIdInt int userId, @NonNull ImeDisplayValidator checker) {
+        // Visible background user can be assigned to a secondary display, not the default display.
+        // The main display assigned to the user will be used as the fallback display.
+        final int mainDisplayId = mUserManagerInternal.getMainDisplayAssignedToUser(userId);
+        return computeImeDisplayIdForTargetInner(displayId, checker, mainDisplayId);
+    }
+
+    private static int computeImeDisplayIdForTargetInner(
+            int displayId, @NonNull ImeDisplayValidator checker, int fallbackDisplayId) {
+        if (displayId == fallbackDisplayId || displayId == INVALID_DISPLAY) {
+            return fallbackDisplayId;
         }
 
         // Show IME window on fallback display when the display doesn't support system decorations
@@ -2356,9 +2380,8 @@
             return displayId;
         } else if (result == DISPLAY_IME_POLICY_HIDE) {
             return INVALID_DISPLAY;
-        } else {
-            return FALLBACK_DISPLAY_ID;
         }
+        return fallbackDisplayId;
     }
 
     @GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0d0cdd8..a0e5433 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -137,6 +137,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
@@ -1325,7 +1326,7 @@
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN,
                 "Requires MANAGE_WEAK_ESCROW_TOKEN permission.");
-        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+        if (!RoSystemFeatures.hasFeatureAutomotive(mContext)) {
             throw new IllegalArgumentException(
                     "Weak escrow token are only for automotive devices.");
         }
@@ -3613,7 +3614,7 @@
         }
 
         // Escrow tokens are enabled on automotive builds.
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+        if (RoSystemFeatures.hasFeatureAutomotive(mContext)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index d440d3a..d9df09d 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -110,7 +110,7 @@
             if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
                     && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
                     && !hasGlobalPictureQualityServicePermission()) {
-                notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
@@ -136,7 +136,7 @@
         public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
             Long dbId = mPictureProfileTempIdMap.getKey(id);
             if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
-                notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+                notifyPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
@@ -165,7 +165,7 @@
             Long dbId = mPictureProfileTempIdMap.getKey(id);
 
             if (!hasPermissionToRemovePictureProfile(dbId)) {
-                notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+                notifyPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
@@ -176,7 +176,7 @@
                 int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
                         selectionArgs);
                 if (result == 0) {
-                    notifyError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+                    notifyPictureProfileError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
                             Binder.getCallingUid(), Binder.getCallingPid());
                 }
                 mPictureProfileTempIdMap.remove(dbId);
@@ -246,7 +246,7 @@
         public List<PictureProfile> getPictureProfilesByPackage(
                 String packageName, Bundle options, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
@@ -259,8 +259,7 @@
         }
 
         @Override
-        public List<PictureProfile> getAvailablePictureProfiles(
-                        Bundle options, UserHandle user) {
+        public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) {
             String packageName = getPackageOfCallingUid();
             if (packageName != null) {
                 return getPictureProfilesByPackage(packageName, options, user);
@@ -271,7 +270,7 @@
         @Override
         public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                notifyError(profileId, PictureProfile.ERROR_NO_PERMISSION,
+                notifyPictureProfileError(profileId, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
             // TODO: pass the profile ID to MediaQuality HAL when ready.
@@ -281,7 +280,7 @@
         @Override
         public List<String> getPictureProfilePackageNames(UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
             String [] column = {BaseParameters.PARAMETER_PACKAGE};
@@ -294,13 +293,31 @@
         }
 
         @Override
-        public List<PictureProfileHandle> getPictureProfileHandle(String[] id, UserHandle user) {
-            return new ArrayList<>();
+        public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, UserHandle user) {
+            List<PictureProfileHandle> toReturn = new ArrayList<>();
+            for (String id : ids) {
+                Long key = mPictureProfileTempIdMap.getKey(id);
+                if (key != null) {
+                    toReturn.add(new PictureProfileHandle(key));
+                } else {
+                    toReturn.add(null);
+                }
+            }
+            return toReturn;
         }
 
         @Override
-        public List<SoundProfileHandle> getSoundProfileHandle(String[] id, UserHandle user) {
-            return new ArrayList<>();
+        public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, UserHandle user) {
+            List<SoundProfileHandle> toReturn = new ArrayList<>();
+            for (String id : ids) {
+                Long key = mSoundProfileTempIdMap.getKey(id);
+                if (key != null) {
+                    toReturn.add(new SoundProfileHandle(key));
+                } else {
+                    toReturn.add(null);
+                }
+            }
+            return toReturn;
         }
 
         @Override
@@ -308,8 +325,8 @@
             if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
                     && !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
                     && !hasGlobalPictureQualityServicePermission()) {
-                //TODO: error handling
-                return null;
+                notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
@@ -332,10 +349,9 @@
         @Override
         public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
             Long dbId = mSoundProfileTempIdMap.getKey(id);
-
             if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
-                //TODO: error handling
-                return;
+                notifySoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
 
             ContentValues values = getContentValues(dbId,
@@ -359,22 +375,23 @@
 
         @Override
         public void removeSoundProfile(String id, UserHandle user) {
-            Long intId = mSoundProfileTempIdMap.getKey(id);
-            if (!hasPermissionToRemoveSoundProfile(intId)) {
-                //TODO: error handling
-                return;
+            Long dbId = mSoundProfileTempIdMap.getKey(id);
+            if (!hasPermissionToRemoveSoundProfile(dbId)) {
+                notifySoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            if (intId != null) {
+            if (dbId != null) {
                 SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
                 String selection = BaseParameters.PARAMETER_ID + " = ?";
-                String[] selectionArgs = {Long.toString(intId)};
+                String[] selectionArgs = {Long.toString(dbId)};
                 int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
                         selectionArgs);
                 if (result == 0) {
-                    //TODO: error handling
+                    notifySoundProfileError(id, SoundProfile.ERROR_INVALID_ARGUMENT,
+                            Binder.getCallingUid(), Binder.getCallingPid());
                 }
-                mSoundProfileTempIdMap.remove(intId);
+                mSoundProfileTempIdMap.remove(dbId);
             }
         }
 
@@ -403,7 +420,7 @@
                     return null;
                 }
                 if (count > 1) {
-                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s"
+                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for name=%s"
                                     + " in %s. Should only ever be 0 or 1.", count, name,
                             mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
                     return null;
@@ -441,8 +458,8 @@
         public List<SoundProfile> getSoundProfilesByPackage(
                 String packageName, Bundle options, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
-                //TODO: error handling
-                return new ArrayList<>();
+                notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
 
             boolean includeParams =
@@ -465,8 +482,8 @@
         @Override
         public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
-                //TODO: error handling
-                return false;
+                notifySoundProfileError(profileId, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
             // TODO: pass the profile ID to MediaQuality HAL when ready.
             return false;
@@ -475,8 +492,8 @@
         @Override
         public List<String> getSoundProfilePackageNames(UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
-                //TODO: error handling
-                return new ArrayList<>();
+                notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
             String [] column = {BaseParameters.PARAMETER_NAME};
             List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
@@ -718,23 +735,48 @@
             }
         }
 
-        private void notifyError(String profileId, int errorCode, int uid, int pid) {
+        private void notifyPictureProfileError(String profileId, int errorCode, int uid, int pid) {
             UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
-            int n = userState.mCallbacks.beginBroadcast();
+            int n = userState.mPictureProfileCallbacks.beginBroadcast();
 
             for (int i = 0; i < n; ++i) {
                 try {
-                    IPictureProfileCallback callback = userState.mCallbacks.getBroadcastItem(i);
-                    Pair<Integer, Integer> pidUid = userState.mCallbackPidUidMap.get(callback);
+                    IPictureProfileCallback callback = userState.mPictureProfileCallbacks
+                            .getBroadcastItem(i);
+                    Pair<Integer, Integer> pidUid = userState.mPictureProfileCallbackPidUidMap
+                            .get(callback);
 
                     if (pidUid.first == pid && pidUid.second == uid) {
-                        userState.mCallbacks.getBroadcastItem(i).onError(profileId, errorCode);
+                        userState.mPictureProfileCallbacks.getBroadcastItem(i)
+                                .onError(profileId, errorCode);
                     }
                 } catch (RemoteException e) {
                     Slog.e(TAG, "failed to report added input to callback", e);
                 }
             }
-            userState.mCallbacks.finishBroadcast();
+            userState.mPictureProfileCallbacks.finishBroadcast();
+        }
+
+        private void notifySoundProfileError(String profileId, int errorCode, int uid, int pid) {
+            UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
+            int n = userState.mSoundProfileCallbacks.beginBroadcast();
+
+            for (int i = 0; i < n; ++i) {
+                try {
+                    ISoundProfileCallback callback = userState.mSoundProfileCallbacks
+                            .getBroadcastItem(i);
+                    Pair<Integer, Integer> pidUid = userState.mSoundProfileCallbackPidUidMap
+                            .get(callback);
+
+                    if (pidUid.first == pid && pidUid.second == uid) {
+                        userState.mSoundProfileCallbacks.getBroadcastItem(i)
+                                .onError(profileId, errorCode);
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "failed to report added input to callback", e);
+                }
+            }
+            userState.mSoundProfileCallbacks.finishBroadcast();
         }
 
         @Override
@@ -743,11 +785,18 @@
             int callingUid = Binder.getCallingUid();
 
             UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
-            userState.mCallbackPidUidMap.put(callback, Pair.create(callingPid, callingUid));
+            userState.mPictureProfileCallbackPidUidMap.put(callback,
+                    Pair.create(callingPid, callingUid));
         }
 
         @Override
         public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
+            int callingPid = Binder.getCallingPid();
+            int callingUid = Binder.getCallingUid();
+
+            UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
+            userState.mSoundProfileCallbackPidUidMap.put(callback,
+                    Pair.create(callingPid, callingUid));
         }
 
         @Override
@@ -781,8 +830,8 @@
         @Override
         public List<String> getPictureProfileAllowList(UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                //TODO: error handling
-                return new ArrayList<>();
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
             return new ArrayList<>();
         }
@@ -790,15 +839,16 @@
         @Override
         public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                //TODO: error handling
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
         }
 
         @Override
         public List<String> getSoundProfileAllowList(UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
-                //TODO: error handling
-                return new ArrayList<>();
+                notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
             return new ArrayList<>();
         }
@@ -806,7 +856,8 @@
         @Override
         public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
-                //TODO: error handling
+                notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
         }
 
@@ -818,7 +869,8 @@
         @Override
         public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                //TODO: error handling
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
 
             try {
@@ -849,7 +901,8 @@
         @Override
         public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
-                //TODO: error handling
+                notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
 
             try {
@@ -880,7 +933,8 @@
         @Override
         public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
-                //TODO: error handling
+                notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
             }
 
             try {
@@ -914,7 +968,7 @@
         }
     }
 
-    private class MediaQualityManagerCallbackList extends
+    private class MediaQualityManagerPictureProfileCallbackList extends
             RemoteCallbackList<IPictureProfileCallback> {
         @Override
         public void onCallbackDied(IPictureProfileCallback callback) {
@@ -922,13 +976,27 @@
         }
     }
 
+    private class MediaQualityManagerSoundProfileCallbackList extends
+            RemoteCallbackList<ISoundProfileCallback> {
+        @Override
+        public void onCallbackDied(ISoundProfileCallback callback) {
+            //todo
+        }
+    }
+
     private final class UserState {
         // A list of callbacks.
-        private final MediaQualityManagerCallbackList mCallbacks =
-                new MediaQualityManagerCallbackList();
+        private final MediaQualityManagerPictureProfileCallbackList mPictureProfileCallbacks =
+                new MediaQualityManagerPictureProfileCallbackList();
 
-        private final Map<IPictureProfileCallback, Pair<Integer, Integer>> mCallbackPidUidMap =
-                new HashMap<>();
+        private final MediaQualityManagerSoundProfileCallbackList mSoundProfileCallbacks =
+                new MediaQualityManagerSoundProfileCallbackList();
+
+        private final Map<IPictureProfileCallback, Pair<Integer, Integer>>
+                mPictureProfileCallbackPidUidMap = new HashMap<>();
+
+        private final Map<ISoundProfileCallback, Pair<Integer, Integer>>
+                mSoundProfileCallbackPidUidMap = new HashMap<>();
 
         private UserState(Context context, int userId) {
 
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index a8f31f9..4cca855 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3039,13 +3039,14 @@
             if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
                     && android.security.Flags.extendEcmToAllSettings()) {
                 final int appId = request.getAppId();
-                mPm.mHandler.post(() -> {
+                // TODO: b/388960315 - Implement a long-term solution to race condition
+                mPm.mHandler.postDelayed(() -> {
                     for (int userId : firstUserIds) {
                         // MODE_DEFAULT means that the app's guardedness will be decided lazily
                         setAccessRestrictedSettingsMode(packageName, appId, userId,
                                 AppOpsManager.MODE_DEFAULT);
                     }
-                });
+                }, 1000L);
             } else {
                 // Apply restricted settings on potentially dangerous packages. Needs to happen
                 // after appOpsManager is notified of the new package
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index fb16b86..a902f5f 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1848,8 +1848,10 @@
         boolean manifestOverrideEnabled =  (mPageSizeAppCompatFlags
                 & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
         boolean settingsOverrideEnabled =  (mPageSizeAppCompatFlags
-                & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
-        if (manifestOverrideEnabled || settingsOverrideEnabled) {
+                & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED) != 0;
+        boolean settingsOverrideDisabled =  (mPageSizeAppCompatFlags
+                & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED) != 0;
+        if (manifestOverrideEnabled || settingsOverrideEnabled || settingsOverrideDisabled) {
             return null;
         }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 81956fb..f4d4c5b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3312,13 +3312,18 @@
         }
     }
 
-
-
     private void sendUserInfoChangedBroadcast(@UserIdInt int userId) {
         Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
         changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         mContext.sendBroadcastAsUser(changedIntent, UserHandle.ALL);
+
+        // This intent allow system UI apps to refresh the content even if process was freezed.
+        Intent bgIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED_BACKGROUND);
+        bgIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        bgIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mContext.sendBroadcastAsUser(bgIntent, UserHandle.ALL,
+                Manifest.permission.MANAGE_USERS);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index e75f852e..a755ee1 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -45,6 +45,7 @@
 import com.android.server.input.InputManagerInternal;
 import com.android.server.policy.devicestate.config.Conditions;
 import com.android.server.policy.devicestate.config.DeviceStateConfig;
+import com.android.server.policy.devicestate.config.Flags;
 import com.android.server.policy.devicestate.config.LidSwitchCondition;
 import com.android.server.policy.devicestate.config.NumericRange;
 import com.android.server.policy.devicestate.config.Properties;
@@ -140,7 +141,16 @@
     private static final String PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT =
             "com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT";
 
-
+    // Deprecated flag definitions to maintain backwards compatibility.
+    private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
+    private static final String FLAG_APP_INACCESSIBLE = "FLAG_APP_INACCESSIBLE";
+    private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
+    private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
+            "FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
+    private static final String FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
+            "FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
+    private static final String FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
+            "FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
 
     /** Interface that allows reading the device state configuration. */
     interface ReadableConfig {
@@ -185,15 +195,29 @@
                             new HashSet<>();
                     Set<@DeviceState.DeviceStateProperties Integer> physicalProperties =
                             new HashSet<>();
-                    final Properties configFlags = stateConfig.getProperties();
-                    if (configFlags != null) {
-                        List<String> configPropertyStrings = configFlags.getProperty();
+                    final Properties configProperties = stateConfig.getProperties();
+                    if (configProperties != null) {
+                        List<String> configPropertyStrings = configProperties.getProperty();
                         for (int i = 0; i < configPropertyStrings.size(); i++) {
                             final String configPropertyString = configPropertyStrings.get(i);
                             addPropertyByString(configPropertyString, systemProperties,
                                     physicalProperties);
                         }
                     }
+
+                    if (android.hardware.devicestate.feature.flags
+                            .Flags.deviceStateConfigurationFlag()) {
+                        // Parse through the deprecated flag configuration to keep compatibility.
+                        final Flags configFlags = stateConfig.getFlags();
+                        if (configFlags != null) {
+                            List<String> configFlagStrings = configFlags.getFlag();
+                            for (int i = 0; i < configFlagStrings.size(); i++) {
+                                final String configFlagString = configFlagStrings.get(i);
+                                addFlagByString(configFlagString, systemProperties);
+                            }
+                        }
+                    }
+
                     DeviceState.Configuration deviceStateConfiguration =
                             new DeviceState.Configuration.Builder(state, name)
                                     .setSystemProperties(systemProperties)
@@ -292,6 +316,34 @@
         }
     }
 
+    private static void addFlagByString(String flagString,
+            Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties) {
+        switch (flagString) {
+            case FLAG_APP_INACCESSIBLE:
+                systemProperties.add(DeviceState.PROPERTY_APP_INACCESSIBLE);
+                break;
+            case FLAG_EMULATED_ONLY:
+                systemProperties.add(DeviceState.PROPERTY_EMULATED_ONLY);
+                break;
+            case FLAG_CANCEL_OVERRIDE_REQUESTS:
+                systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS);
+                break;
+            case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
+                systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
+                break;
+            case FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
+                systemProperties.add(DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE);
+                break;
+            case FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
+                systemProperties.add(
+                        DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL);
+                break;
+            default:
+                Slog.w(TAG, "Parsed unknown flag with name: " + flagString);
+                break;
+        }
+    }
+
     // Lock for internal state.
     private final Object mLock = new Object();
     private final Context mContext;
diff --git a/services/core/java/com/android/server/selinux/QuotaExceededException.java b/services/core/java/com/android/server/selinux/QuotaExceededException.java
new file mode 100644
index 0000000..26d4d827
--- /dev/null
+++ b/services/core/java/com/android/server/selinux/QuotaExceededException.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+package com.android.server.selinux;
+
+/** An exception raised when the quota has been reached.
+ *
+ * This exception is raised in EventLogCollection.add(). See QuotaLimiter
+ * for the implementation details.
+ */
+class QuotaExceededException extends Exception {}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
index 0aa7058..54365ff 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
@@ -28,10 +28,8 @@
 
 import java.io.IOException;
 import java.time.Instant;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
+import java.util.AbstractCollection;
+import java.util.Iterator;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
 import java.util.regex.Matcher;
@@ -57,6 +55,7 @@
     private final Supplier<String> mAuditDomainSupplier;
     private final RateLimiter mRateLimiter;
     private final QuotaLimiter mQuotaLimiter;
+    private EventLogCollection mEventCollection;
 
     @VisibleForTesting Instant mLastWrite = Instant.MIN;
 
@@ -69,6 +68,7 @@
         mAuditDomainSupplier = auditDomainSupplier;
         mRateLimiter = rateLimiter;
         mQuotaLimiter = quotaLimiter;
+        mEventCollection = new EventLogCollection();
     }
 
     SelinuxAuditLogsCollector(RateLimiter rateLimiter, QuotaLimiter quotaLimiter) {
@@ -86,75 +86,72 @@
         mStopRequested.set(stopRequested);
     }
 
-    /**
-     * Collect and push SELinux audit logs for the provided {@code tagCode}.
+    /** A Collection to work around EventLog.readEvents() constraints.
      *
-     * @return true if the job was completed. If the job was interrupted, return false.
+     * This collection only supports add(). Any other method inherited from
+     * Collection will throw an UnsupportedOperationException exception.
+     *
+     * This collection ensures that we are processing one event at a time and
+     * avoid collecting all the event objects before processing (e.g.,
+     * ArrayList), which could lead to an OOM situation.
      */
-    boolean collect(int tagCode) {
-        Queue<Event> logLines = new ArrayDeque<>();
-        Instant latestTimestamp = collectLogLines(tagCode, logLines);
+    class EventLogCollection extends AbstractCollection<Event> {
 
-        boolean quotaExceeded = writeAuditLogs(logLines);
-        if (quotaExceeded) {
-            Slog.w(TAG, "Too many SELinux logs in the queue, I am giving up.");
-            mLastWrite = latestTimestamp; // next run we will ignore all these logs.
-            logLines.clear();
+        SelinuxAuditLogBuilder mAuditLogBuilder;
+        int mAuditsWritten = 0;
+        Instant mLatestTimestamp;
+
+        void reset() {
+            mAuditsWritten = 0;
+            mLatestTimestamp = mLastWrite;
+            mAuditLogBuilder = new SelinuxAuditLogBuilder(mAuditDomainSupplier.get());
         }
 
-        return logLines.isEmpty();
-    }
-
-    private Instant collectLogLines(int tagCode, Queue<Event> logLines) {
-        List<Event> events = new ArrayList<>();
-        try {
-            EventLog.readEvents(new int[] {tagCode}, events);
-        } catch (IOException e) {
-            Slog.e(TAG, "Error reading event logs", e);
+        int getAuditsWritten() {
+            return mAuditsWritten;
         }
 
-        Instant latestTimestamp = mLastWrite;
-        for (Event event : events) {
-            Instant eventTime = Instant.ofEpochSecond(0, event.getTimeNanos());
-            if (eventTime.isAfter(latestTimestamp)) {
-                latestTimestamp = eventTime;
+        Instant getLatestTimestamp() {
+            return mLatestTimestamp;
+        }
+
+        @Override
+        public Iterator<Event> iterator() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean add(Event event) {
+            if (mStopRequested.get()) {
+                throw new IllegalStateException(new InterruptedException());
             }
+
+            Instant eventTime = Instant.ofEpochSecond(/* epochSecond= */ 0, event.getTimeNanos());
             if (eventTime.compareTo(mLastWrite) <= 0) {
-                continue;
+                return true;
             }
             Object eventData = event.getData();
             if (!(eventData instanceof String)) {
-                continue;
+                return true;
             }
-            logLines.add(event);
-        }
-        return latestTimestamp;
-    }
-
-    private boolean writeAuditLogs(Queue<Event> logLines) {
-        final SelinuxAuditLogBuilder auditLogBuilder =
-                new SelinuxAuditLogBuilder(mAuditDomainSupplier.get());
-        int auditsWritten = 0;
-
-        while (!mStopRequested.get() && !logLines.isEmpty()) {
-            Event event = logLines.poll();
-            String logLine = (String) event.getData();
-            Instant logTime = Instant.ofEpochSecond(0, event.getTimeNanos());
+            String logLine = (String) eventData;
             if (!SELINUX_MATCHER.reset(logLine).matches()) {
-                continue;
+                return true;
             }
 
-            auditLogBuilder.reset(SELINUX_MATCHER.group("denial"));
-            final SelinuxAuditLog auditLog = auditLogBuilder.build();
+            mAuditLogBuilder.reset(SELINUX_MATCHER.group("denial"));
+            final SelinuxAuditLog auditLog = mAuditLogBuilder.build();
             if (auditLog == null) {
-                continue;
+                return true;
             }
 
             if (!mQuotaLimiter.acquire()) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "Running out of quota after %d logs.", auditsWritten);
-                }
-                return true;
+                throw new IllegalStateException(new QuotaExceededException());
             }
             mRateLimiter.acquire();
 
@@ -169,16 +166,50 @@
                     auditLog.mTClass,
                     auditLog.mPath,
                     auditLog.mPermissive);
-            auditsWritten++;
 
-            if (logTime.isAfter(mLastWrite)) {
-                mLastWrite = logTime;
+            mAuditsWritten++;
+            if (eventTime.isAfter(mLatestTimestamp)) {
+                mLatestTimestamp = eventTime;
             }
+
+            return true;
+        }
+    }
+
+    /**
+     * Collect and push SELinux audit logs for the provided {@code tagCode}.
+     *
+     * @return true if the job was completed. If the job was interrupted or
+     * failed because of IOException, return false.
+     * @throws QuotaExceededException if it ran out of quota.
+     */
+    boolean collect(int tagCode) throws QuotaExceededException {
+        mEventCollection.reset();
+        try {
+            EventLog.readEvents(new int[] {tagCode}, mEventCollection);
+        } catch (IllegalStateException e) {
+            if (e.getCause() instanceof QuotaExceededException) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "Running out of quota after %d logs.",
+                            mEventCollection.getAuditsWritten());
+                }
+                // next run we will ignore all these logs.
+                mLastWrite = mEventCollection.getLatestTimestamp();
+                throw (QuotaExceededException) e.getCause();
+            } else if (e.getCause() instanceof InterruptedException) {
+                mLastWrite = mEventCollection.getLatestTimestamp();
+                return false;
+            }
+            throw e;
+        } catch (IOException e) {
+            Slog.e(TAG, "Error reading event logs", e);
+            return false;
         }
 
+        mLastWrite = mEventCollection.getLatestTimestamp();
         if (DEBUG) {
-            Slogf.d(TAG, "Written %d logs", auditsWritten);
+            Slogf.d(TAG, "Written %d logs", mEventCollection.getAuditsWritten());
         }
-        return false;
+        return true;
     }
 }
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
index 0092c37..e55e590 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
@@ -51,8 +51,12 @@
             return;
         }
         mIsRunning.set(true);
-        boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
-        if (done) {
+        try {
+            boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
+            if (done) {
+                jobService.jobFinished(params, /* wantsReschedule= */ false);
+            }
+        } catch (QuotaExceededException e) {
             jobService.jobFinished(params, /* wantsReschedule= */ false);
         }
         mIsRunning.set(false);
diff --git a/services/core/java/com/android/server/timezonedetector/Environment.java b/services/core/java/com/android/server/timezonedetector/Environment.java
new file mode 100644
index 0000000..795fb02
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/Environment.java
@@ -0,0 +1,80 @@
+/*
+ * 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.server.timezonedetector;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+
+import com.android.server.SystemTimeZone;
+
+import java.io.PrintWriter;
+
+/**
+ * Used by the time zone detector code to interact with device state besides that available from
+ * {@link ServiceConfigAccessor}. It can be faked for testing.
+ */
+public interface Environment {
+
+    /**
+     * Returns the device's currently configured time zone. May return an empty string.
+     */
+    @NonNull
+    String getDeviceTimeZone();
+
+    /**
+     * Returns the confidence of the device's current time zone.
+     */
+    @SystemTimeZone.TimeZoneConfidence
+    int getDeviceTimeZoneConfidence();
+
+    /**
+     * Sets the device's time zone, associated confidence, and records a debug log entry.
+     */
+    void setDeviceTimeZoneAndConfidence(
+            @NonNull String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence,
+            @NonNull String logInfo);
+
+    /**
+     * Returns the time according to the elapsed realtime clock, the same as {@link
+     * android.os.SystemClock#elapsedRealtime()}.
+     */
+    @ElapsedRealtimeLong
+    long elapsedRealtimeMillis();
+
+    /**
+     * Returns the current time in milliseconds, the same as
+     * {@link java.lang.System#currentTimeMillis()}.
+     */
+    @CurrentTimeMillisLong
+    long currentTimeMillis();
+
+    /**
+     * Adds a standalone entry to the time zone debug log.
+     */
+    void addDebugLogEntry(@NonNull String logMsg);
+
+    /**
+     * Dumps the time zone debug log to the supplied {@link PrintWriter}.
+     */
+    void dumpDebugLog(PrintWriter printWriter);
+
+    /**
+     * Requests that the supplied runnable be invoked asynchronously.
+     */
+    void runAsync(@NonNull Runnable runnable);
+}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 449b41a..8491b48 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.timezonedetector;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.os.Handler;
@@ -31,9 +32,9 @@
 import java.util.Objects;
 
 /**
- * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
+ * The real implementation of {@link Environment}.
  */
-final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {
+final class EnvironmentImpl implements Environment {
 
     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
@@ -69,6 +70,11 @@
     }
 
     @Override
+    public @CurrentTimeMillisLong long currentTimeMillis() {
+        return System.currentTimeMillis();
+    }
+
+    @Override
     public void addDebugLogEntry(@NonNull String logMsg) {
         SystemTimeZone.addDebugLogEntry(logMsg);
     }
diff --git a/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java b/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
index 2e73829..cf85a9a 100644
--- a/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
+++ b/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
@@ -29,6 +29,7 @@
 
 import android.annotation.DurationMillisLong;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
@@ -44,7 +45,6 @@
 import android.icu.text.SimpleDateFormat;
 import android.icu.util.TimeZone;
 import android.os.Handler;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
@@ -153,6 +153,9 @@
         }
     };
 
+    @NonNull
+    private final Environment mEnvironment;
+
     private final Object mConfigurationLock = new Object();
     @GuardedBy("mConfigurationLock")
     private ConfigurationInternal mConfigurationInternal;
@@ -170,12 +173,14 @@
     /** Create and initialise a new {@code TimeZoneChangeTrackerImpl} */
     @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
     public static NotifyingTimeZoneChangeListener create(Handler handler, Context context,
-            ServiceConfigAccessor serviceConfigAccessor) {
+            ServiceConfigAccessor serviceConfigAccessor,
+            @NonNull Environment environment) {
         NotifyingTimeZoneChangeListener changeTracker =
                 new NotifyingTimeZoneChangeListener(handler,
                         context,
                         serviceConfigAccessor,
-                        context.getSystemService(NotificationManager.class));
+                        context.getSystemService(NotificationManager.class),
+                        environment);
 
         // Pretend there was an update to initialize configuration.
         changeTracker.handleConfigurationUpdate();
@@ -184,9 +189,9 @@
     }
 
     @VisibleForTesting
-    NotifyingTimeZoneChangeListener(
-            Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor,
-            NotificationManager notificationManager) {
+    NotifyingTimeZoneChangeListener(Handler handler, Context context,
+            ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager,
+            @NonNull Environment environment) {
         mHandler = Objects.requireNonNull(handler);
         mContext = Objects.requireNonNull(context);
         mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
@@ -194,6 +199,7 @@
                 this::handleConfigurationUpdate);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mNotificationManager = notificationManager;
+        mEnvironment = Objects.requireNonNull(environment);
     }
 
     @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
@@ -420,7 +426,7 @@
             if (!changeEvent.getOldZoneId().equals(lastChangeEvent.getNewZoneId())) {
                 int changeEventId = mNextChangeEventId.getAndIncrement();
                 TimeZoneChangeEvent syntheticChangeEvent = new TimeZoneChangeEvent(
-                        SystemClock.elapsedRealtime(), System.currentTimeMillis(),
+                        mEnvironment.elapsedRealtimeMillis(), mEnvironment.currentTimeMillis(),
                         ORIGIN_UNKNOWN, UserHandle.USER_NULL, lastChangeEvent.getNewZoneId(),
                         changeEvent.getOldZoneId(), 0, "Synthetic");
                 TimeZoneChangeRecord syntheticTrackedChangeEvent =
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index b2b06b0..042d81a 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -25,7 +25,6 @@
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
 
-import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -54,7 +53,6 @@
 import com.android.server.flags.Flags;
 import com.android.server.timezonedetector.ConfigurationInternal.DetectionMode;
 
-import java.io.PrintWriter;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.List;
@@ -67,55 +65,6 @@
  */
 public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
 
-    /**
-     * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that
-     * available from {@link #mServiceConfigAccessor}. It can be faked for testing.
-     */
-    @VisibleForTesting
-    public interface Environment {
-
-        /**
-         * Returns the device's currently configured time zone. May return an empty string.
-         */
-        @NonNull
-        String getDeviceTimeZone();
-
-        /**
-         * Returns the confidence of the device's current time zone.
-         */
-        @TimeZoneConfidence
-        int getDeviceTimeZoneConfidence();
-
-        /**
-         * Sets the device's time zone, associated confidence, and records a debug log entry.
-         */
-        void setDeviceTimeZoneAndConfidence(
-                @NonNull String zoneId, @TimeZoneConfidence int confidence,
-                @NonNull String logInfo);
-
-        /**
-         * Returns the time according to the elapsed realtime clock, the same as {@link
-         * android.os.SystemClock#elapsedRealtime()}.
-         */
-        @ElapsedRealtimeLong
-        long elapsedRealtimeMillis();
-
-        /**
-         * Adds a standalone entry to the time zone debug log.
-         */
-        void addDebugLogEntry(@NonNull String logMsg);
-
-        /**
-         * Dumps the time zone debug log to the supplied {@link PrintWriter}.
-         */
-        void dumpDebugLog(PrintWriter printWriter);
-
-        /**
-         * Requests that the supplied runnable be invoked asynchronously.
-         */
-        void runAsync(@NonNull Runnable runnable);
-    }
-
     private static final String LOG_TAG = TimeZoneDetectorService.TAG;
     private static final boolean DBG = TimeZoneDetectorService.DBG;
 
@@ -263,10 +212,10 @@
     public static TimeZoneDetectorStrategyImpl create(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull ServiceConfigAccessor serviceConfigAccessor) {
-
         Environment environment = new EnvironmentImpl(handler);
         TimeZoneChangeListener changeEventTracker =
-                NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor);
+                NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor,
+                        environment);
         return new TimeZoneDetectorStrategyImpl(
                 serviceConfigAccessor, environment, changeEventTracker);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d31aed2..e52fbbf 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -90,10 +90,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_METADATA;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
 import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
 import static android.content.res.Configuration.EMPTY;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -1272,8 +1268,9 @@
                 pw.println(prefix + "manifestMinAspectRatio="
                         + info.getManifestMinAspectRatio());
             }
-            pw.println(prefix + "supportsSizeChanges="
-                    + ActivityInfo.sizeChangesSupportModeToString(supportsSizeChanges()));
+            pw.println(
+                    prefix + "supportsSizeChanges=" + ActivityInfo.sizeChangesSupportModeToString(
+                            mAppCompatController.getSizeCompatModePolicy().supportsSizeChanges()));
             if (info.configChanges != 0) {
                 pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
             }
@@ -6581,7 +6578,7 @@
         mTaskSupervisor.mStoppingActivities.remove(this);
         if (getDisplayArea().allResumedActivitiesComplete()) {
             // Construct the compat environment at a relatively stable state if needed.
-            mAppCompatController.getAppCompatSizeCompatModePolicy().updateAppCompatDisplayInsets();
+            mAppCompatController.getSizeCompatModePolicy().updateAppCompatDisplayInsets();
             mRootWindowContainer.executeAppTransitionForAllDisplay();
         }
 
@@ -8222,7 +8219,7 @@
                     != getRequestedConfigurationOrientation(false /*forDisplay */)) {
             // Do not change the requested configuration now, because this will be done when setting
             // the orientation below with the new mAppCompatDisplayInsets
-            mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatModeAttributes();
+            mAppCompatController.getSizeCompatModePolicy().clearSizeCompatModeAttributes();
         }
         ProtoLog.v(WM_DEBUG_ORIENTATION,
                 "Setting requested orientation %s for %s",
@@ -8366,7 +8363,7 @@
 
     @Nullable
     AppCompatDisplayInsets getAppCompatDisplayInsets() {
-        return mAppCompatController.getAppCompatSizeCompatModePolicy().getAppCompatDisplayInsets();
+        return mAppCompatController.getSizeCompatModePolicy().getAppCompatDisplayInsets();
     }
 
     /**
@@ -8374,31 +8371,7 @@
      *         density than its parent or its bounds don't fit in parent naturally.
      */
     boolean inSizeCompatMode() {
-        final AppCompatSizeCompatModePolicy scmPolicy = mAppCompatController
-                .getAppCompatSizeCompatModePolicy();
-        if (scmPolicy.isInSizeCompatModeForBounds()) {
-            return true;
-        }
-        if (getAppCompatDisplayInsets() == null || !shouldCreateAppCompatDisplayInsets()
-                // The orientation is different from parent when transforming.
-                || isFixedRotationTransforming()) {
-            return false;
-        }
-        final Rect appBounds = getConfiguration().windowConfiguration.getAppBounds();
-        if (appBounds == null) {
-            // The app bounds hasn't been computed yet.
-            return false;
-        }
-        final WindowContainer parent = getParent();
-        if (parent == null) {
-            // The parent of detached Activity can be null.
-            return false;
-        }
-        final Configuration parentConfig = parent.getConfiguration();
-        // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
-        // fields should be changed with density and bounds, so here only compares the most
-        // significant field.
-        return parentConfig.densityDpi != getConfiguration().densityDpi;
+        return mAppCompatController.getSizeCompatModePolicy().inSizeCompatMode();
     }
 
     /**
@@ -8412,67 +8385,12 @@
      *         aspect ratio.
      */
     boolean shouldCreateAppCompatDisplayInsets() {
-        if (mAppCompatController.getAppCompatAspectRatioOverrides().hasFullscreenOverride()) {
-            // If the user has forced the applications aspect ratio to be fullscreen, don't use size
-            // compatibility mode in any situation. The user has been warned and therefore accepts
-            // the risk of the application misbehaving.
-            return false;
-        }
-        switch (supportsSizeChanges()) {
-            case SIZE_CHANGES_SUPPORTED_METADATA:
-            case SIZE_CHANGES_SUPPORTED_OVERRIDE:
-                return false;
-            case SIZE_CHANGES_UNSUPPORTED_OVERRIDE:
-                return true;
-            default:
-                // Fall through
-        }
-        // Use root activity's info for tasks in multi-window mode, or fullscreen tasks in freeform
-        // task display areas, to ensure visual consistency across activity launches and exits in
-        // the same task.
-        final TaskDisplayArea tda = getTaskDisplayArea();
-        if (inMultiWindowMode() || (tda != null && tda.inFreeformWindowingMode())) {
-            final ActivityRecord root = task != null ? task.getRootActivity() : null;
-            if (root != null && root != this && !root.shouldCreateAppCompatDisplayInsets()) {
-                // If the root activity doesn't use size compatibility mode, the activities above
-                // are forced to be the same for consistent visual appearance.
-                return false;
-            }
-        }
-        return !isResizeable() && (info.isFixedOrientation() || hasFixedAspectRatio())
-                // The configuration of non-standard type should be enforced by system.
-                // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
-                // added to a task, but this function is called when resolving the launch params, at
-                // which point, the activity type is still undefined if it will be standard.
-                // For other non-standard types, the type is set in the constructor, so this should
-                // not be a problem.
-                && isActivityTypeStandardOrUndefined();
-    }
-
-    /**
-     * Returns whether the activity supports size changes.
-     */
-    @ActivityInfo.SizeChangesSupportMode
-    private int supportsSizeChanges() {
-        final AppCompatResizeOverrides resizeOverrides = mAppCompatController.getResizeOverrides();
-        if (resizeOverrides.shouldOverrideForceNonResizeApp()) {
-            return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
-        }
-
-        if (info.supportsSizeChanges) {
-            return SIZE_CHANGES_SUPPORTED_METADATA;
-        }
-
-        if (resizeOverrides.shouldOverrideForceResizeApp()) {
-            return SIZE_CHANGES_SUPPORTED_OVERRIDE;
-        }
-
-        return SIZE_CHANGES_UNSUPPORTED_METADATA;
+        return mAppCompatController.getSizeCompatModePolicy().shouldCreateAppCompatDisplayInsets();
     }
 
     @Override
     boolean hasSizeCompatBounds() {
-        return mAppCompatController.getAppCompatSizeCompatModePolicy().hasSizeCompatBounds();
+        return mAppCompatController.getSizeCompatModePolicy().hasSizeCompatBounds();
     }
 
     @Override
@@ -8491,7 +8409,7 @@
     @Override
     float getCompatScale() {
         // We need to invoke {#getCompatScale()} only if the CompatScale is not available.
-        return mAppCompatController.getAppCompatSizeCompatModePolicy()
+        return mAppCompatController.getSizeCompatModePolicy()
                 .getCompatScaleIfAvailable(ActivityRecord.super::getCompatScale);
     }
 
@@ -8518,7 +8436,7 @@
             newParentConfiguration = mTmpConfig;
         }
 
-        mAppCompatController.getAppCompatAspectRatioPolicy().reset();
+        mAppCompatController.getAspectRatioPolicy().reset();
         mIsEligibleForFixedOrientationLetterbox = false;
         mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
                 isFixedRotationTransforming());
@@ -8549,7 +8467,7 @@
         // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
         // are already calculated in resolveFixedOrientationConfiguration.
         // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
-        if (!mAppCompatController.getAppCompatAspectRatioPolicy()
+        if (!mAppCompatController.getAspectRatioPolicy()
                     .isLetterboxedForFixedOrientationAndAspectRatio()
                 && !mAppCompatController.getAppCompatAspectRatioOverrides()
                     .hasFullscreenOverride()) {
@@ -8557,7 +8475,7 @@
         }
         final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
         final AppCompatSizeCompatModePolicy scmPolicy =
-                mAppCompatController.getAppCompatSizeCompatModePolicy();
+                mAppCompatController.getSizeCompatModePolicy();
         if (appCompatDisplayInsets != null) {
             scmPolicy.resolveSizeCompatModeConfiguration(newParentConfiguration,
                     appCompatDisplayInsets, mTmpBounds);
@@ -8586,7 +8504,7 @@
                 // Fixed orientation letterboxing is possible on both large screen devices
                 // with ignoreOrientationRequest enabled and on phones in split screen even with
                 // ignoreOrientationRequest disabled.
-                && (mAppCompatController.getAppCompatAspectRatioPolicy()
+                && (mAppCompatController.getAspectRatioPolicy()
                     .isLetterboxedForFixedOrientationAndAspectRatio()
                         // Limiting check for aspect ratio letterboxing to devices with enabled
                         // ignoreOrientationRequest. This avoids affecting phones where apps may
@@ -8595,7 +8513,7 @@
                         // accurate on phones shouldn't make the big difference and is expected
                         // to be already well-tested by apps.
                         || (isIgnoreOrientationRequest
-                && mAppCompatController.getAppCompatAspectRatioPolicy().isAspectRatioApplied()))) {
+                && mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied()))) {
             // TODO(b/264034555): Use mDisplayContent to calculate smallestScreenWidthDp from all
             // rotations and only re-calculate if parent bounds have non-orientation size change.
             resolvedConfig.smallestScreenWidthDp =
@@ -8707,7 +8625,7 @@
             return mAppCompatController.getTransparentPolicy().getInheritedAppCompatState();
         }
         final AppCompatSizeCompatModePolicy scmPolicy = mAppCompatController
-                .getAppCompatSizeCompatModePolicy();
+                .getSizeCompatModePolicy();
         if (scmPolicy.isInSizeCompatModeForBounds()) {
             return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
         }
@@ -8715,13 +8633,13 @@
         // letterboxed for fixed orientation. Aspect ratio restrictions are also applied if
         // present. But this doesn't return true when the activity is letterboxed only because
         // of aspect ratio restrictions.
-        if (mAppCompatController.getAppCompatAspectRatioPolicy()
-                .isLetterboxedForFixedOrientationAndAspectRatio()) {
+        final AppCompatAspectRatioPolicy aspectRatioPolicy =
+                mAppCompatController.getAspectRatioPolicy();
+        if (aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio()) {
             return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
         }
         // Letterbox for limited aspect ratio.
-        if (mAppCompatController.getAppCompatAspectRatioPolicy()
-                .isLetterboxedForAspectRatioOnly()) {
+        if (aspectRatioPolicy.isLetterboxedForAspectRatioOnly()) {
             return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
         }
 
@@ -8745,7 +8663,7 @@
             return;
         }
         final AppCompatSizeCompatModePolicy scmPolicy =
-                mAppCompatController.getAppCompatSizeCompatModePolicy();
+                mAppCompatController.getSizeCompatModePolicy();
         final Rect screenResolvedBounds = scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
         final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
         final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
@@ -8843,7 +8761,7 @@
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
         final AppCompatSizeCompatModePolicy scmPolicy =
-                mAppCompatController.getAppCompatSizeCompatModePolicy();
+                mAppCompatController.getSizeCompatModePolicy();
         return scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
     }
 
@@ -8997,7 +8915,7 @@
         }
         final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
         final AppCompatSizeCompatModePolicy scmPolicy =
-                mAppCompatController.getAppCompatSizeCompatModePolicy();
+                mAppCompatController.getSizeCompatModePolicy();
 
         if (appCompatDisplayInsets != null
                 && !appCompatDisplayInsets.mIsInFixedOrientationOrAspectRatioLetterbox) {
@@ -9048,8 +8966,10 @@
         final Rect prevResolvedBounds = new Rect(resolvedBounds);
         resolvedBounds.set(containingBounds);
 
-        mAppCompatController.getAppCompatAspectRatioPolicy()
-                .applyDesiredAspectRatio(newParentConfig, parentBounds, resolvedBounds,
+        final AppCompatAspectRatioPolicy aspectRatioPolicy = mAppCompatController
+                .getAspectRatioPolicy();
+
+        aspectRatioPolicy.applyDesiredAspectRatio(newParentConfig, parentBounds, resolvedBounds,
                         containingBoundsWithInsets, containingBounds);
 
         if (appCompatDisplayInsets != null) {
@@ -9078,8 +8998,8 @@
         // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
         mResolveConfigHint.mTmpCompatInsets = appCompatDisplayInsets;
         computeConfigByResolveHint(getResolvedOverrideConfiguration(), newParentConfig);
-        mAppCompatController.getAppCompatAspectRatioPolicy()
-                .setLetterboxBoundsForFixedOrientationAndAspectRatio(new Rect(resolvedBounds));
+        aspectRatioPolicy.setLetterboxBoundsForFixedOrientationAndAspectRatio(
+                new Rect(resolvedBounds));
     }
 
     /**
@@ -9096,8 +9016,9 @@
         // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
         // restricted size (resolved bounds may be the requested override bounds).
         mTmpBounds.setEmpty();
-        mAppCompatController.getAppCompatAspectRatioPolicy()
-                .applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
+        final AppCompatAspectRatioPolicy aspectRatioPolicy = mAppCompatController
+                .getAspectRatioPolicy();
+        aspectRatioPolicy.applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
         // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
         // then they should be aligned later in #updateResolvedBoundsPosition()
         if (!mTmpBounds.isEmpty()) {
@@ -9108,8 +9029,7 @@
             // restrict, the bounds should be the requested override bounds.
             mResolveConfigHint.mTmpOverrideDisplayInfo = getFixedRotationTransformDisplayInfo();
             computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
-            mAppCompatController.getAppCompatAspectRatioPolicy()
-                    .setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
+            aspectRatioPolicy.setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
         }
     }
 
@@ -9118,7 +9038,7 @@
         // TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
         final Rect superBounds = super.getBounds();
         final AppCompatSizeCompatModePolicy scmPolicy =
-                mAppCompatController.getAppCompatSizeCompatModePolicy();
+                mAppCompatController.getSizeCompatModePolicy();
         return mAppCompatController.getTransparentPolicy().findOpaqueNotFinishingActivityBelow()
                 .map(ActivityRecord::getBounds)
                 .orElseGet(() -> scmPolicy.getAppSizeCompatBoundsIfAvailable(superBounds));
@@ -9359,18 +9279,11 @@
      * Returns the min aspect ratio of this activity.
      */
     float getMinAspectRatio() {
-        return mAppCompatController.getAppCompatAspectRatioPolicy().getMinAspectRatio();
+        return mAppCompatController.getAspectRatioPolicy().getMinAspectRatio();
     }
 
     float getMaxAspectRatio() {
-        return mAppCompatController.getAppCompatAspectRatioPolicy().getMaxAspectRatio();
-    }
-
-    /**
-     * Returns true if the activity has maximum or minimum aspect ratio.
-     */
-    private boolean hasFixedAspectRatio() {
-        return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+        return mAppCompatController.getAspectRatioPolicy().getMaxAspectRatio();
     }
 
     /**
@@ -9452,7 +9365,7 @@
         if (mVisibleRequested) {
             // Calling from here rather than resolveOverrideConfiguration to ensure that this is
             // called after full config is updated in ConfigurationContainer#onConfigurationChanged.
-            mAppCompatController.getAppCompatSizeCompatModePolicy().updateAppCompatDisplayInsets();
+            mAppCompatController.getSizeCompatModePolicy().updateAppCompatDisplayInsets();
         }
 
         // Short circuit: if the two full configurations are equal (the common case), then there is
@@ -9792,7 +9705,7 @@
 
         // Reset the existing override configuration so it can be updated according to the latest
         // configuration.
-        mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         if (!attachedToProcess()) {
             return;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0ab2ffe..bdbd0d1 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1964,7 +1964,6 @@
             if (mLastStartActivityRecord != null) {
                 targetTaskTop.mLaunchSourceType = mLastStartActivityRecord.mLaunchSourceType;
             }
-            targetTaskTop.mTransitionController.collect(targetTaskTop);
             recordTransientLaunchIfNeeded(targetTaskTop);
             // Recycle the target task for this launch.
             startResult =
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 6a0de98..3e232ea 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -215,6 +215,13 @@
         mAppCompatAspectRatioState.mLetterboxBoundsForAspectRatio = bounds;
     }
 
+    /**
+     * Returns true if the activity has maximum or minimum aspect ratio.
+     */
+    boolean hasFixedAspectRatio() {
+        return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+    }
+
     private boolean isParentFullscreenPortrait() {
         final WindowContainer<?> parent = mActivityRecord.getParent();
         return parent != null
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 6d0e8ea..fa510db 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -31,7 +31,7 @@
     @NonNull
     private final AppCompatOrientationPolicy mOrientationPolicy;
     @NonNull
-    private final AppCompatAspectRatioPolicy mAppCompatAspectRatioPolicy;
+    private final AppCompatAspectRatioPolicy mAspectRatioPolicy;
     @NonNull
     private final AppCompatReachabilityPolicy mReachabilityPolicy;
     @NonNull
@@ -43,7 +43,7 @@
     @NonNull
     private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
     @NonNull
-    private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy;
+    private final AppCompatSizeCompatModePolicy mSizeCompatModePolicy;
 
     AppCompatController(@NonNull WindowManagerService wmService,
                         @NonNull ActivityRecord activityRecord) {
@@ -56,7 +56,7 @@
         mAppCompatOverrides = new AppCompatOverrides(activityRecord, packageManager,
                 wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery);
         mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
-        mAppCompatAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
+        mAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
                 mTransparentPolicy, mAppCompatOverrides);
         mReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord,
                 wmService.mAppCompatConfiguration);
@@ -64,7 +64,7 @@
                 wmService.mAppCompatConfiguration);
         mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
                 mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
-        mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
+        mSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
                 mAppCompatOverrides);
     }
 
@@ -79,8 +79,8 @@
     }
 
     @NonNull
-    AppCompatAspectRatioPolicy getAppCompatAspectRatioPolicy() {
-        return mAppCompatAspectRatioPolicy;
+    AppCompatAspectRatioPolicy getAspectRatioPolicy() {
+        return mAspectRatioPolicy;
     }
 
     @NonNull
@@ -139,14 +139,14 @@
     }
 
     @NonNull
-    AppCompatSizeCompatModePolicy getAppCompatSizeCompatModePolicy() {
-        return mAppCompatSizeCompatModePolicy;
+    AppCompatSizeCompatModePolicy getSizeCompatModePolicy() {
+        return mSizeCompatModePolicy;
     }
 
     void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
         getTransparentPolicy().dump(pw, prefix);
         getAppCompatLetterboxPolicy().dump(pw, prefix);
-        getAppCompatSizeCompatModePolicy().dump(pw, prefix);
+        getSizeCompatModePolicy().dump(pw, prefix);
     }
 
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index af83668..a49bec0 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -144,7 +144,7 @@
         mOrientationOverridesState.updateOrientationRequestLoopState();
 
         return mOrientationOverridesState.shouldIgnoreRequestInLoop()
-                && !mActivityRecord.mAppCompatController.getAppCompatAspectRatioPolicy()
+                && !mActivityRecord.mAppCompatController.getAspectRatioPolicy()
                     .isLetterboxedForFixedOrientationAndAspectRatio();
     }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index d278dc3..98bb8e7 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -18,6 +18,10 @@
 
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_METADATA;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 
 import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
@@ -82,7 +86,8 @@
     }
 
     /**
-     * @return The {@code true} if the current instance has {@link #mAppCompatDisplayInsets} without
+     * @return The {@code true} if the current instance has
+     * {@link AppCompatSizeCompatModePolicy#mAppCompatDisplayInsets} without
      * considering the inheritance implemented in {@link #getAppCompatDisplayInsets()}
      */
     boolean hasAppCompatDisplayInsetsWithoutInheritance() {
@@ -199,7 +204,7 @@
         // activity will be displayed within them even if it is in size compat mode. They should be
         // saved here before resolved bounds are overridden below.
         final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivityRecord.mAppCompatController
-                .getAppCompatAspectRatioPolicy();
+                .getAspectRatioPolicy();
         final boolean useResolvedBounds = aspectRatioPolicy.isAspectRatioApplied();
         final Rect containerBounds = useResolvedBounds
                 ? new Rect(resolvedBounds)
@@ -244,8 +249,7 @@
         resolvedBounds.set(containingBounds);
         // The size of floating task is fixed (only swap), so the aspect ratio is already correct.
         if (!appCompatDisplayInsets.mIsFloating) {
-            mActivityRecord.mAppCompatController.getAppCompatAspectRatioPolicy()
-                    .applyAspectRatioForLetterbox(resolvedBounds, containingAppBounds,
+            aspectRatioPolicy.applyAspectRatioForLetterbox(resolvedBounds, containingAppBounds,
                             containingBounds);
         }
 
@@ -359,7 +363,7 @@
         }
 
         final Rect letterboxedContainerBounds = mActivityRecord.mAppCompatController
-                .getAppCompatAspectRatioPolicy().getLetterboxedContainerBounds();
+                .getAspectRatioPolicy().getLetterboxedContainerBounds();
 
         // The role of AppCompatDisplayInsets is like the override bounds.
         mAppCompatDisplayInsets =
@@ -368,6 +372,112 @@
                             .mUseOverrideInsetsForConfig);
     }
 
+    /**
+     * @return {@code true} if this activity is in size compatibility mode that uses the different
+     *         density than its parent or its bounds don't fit in parent naturally.
+     */
+    boolean inSizeCompatMode() {
+        if (isInSizeCompatModeForBounds()) {
+            return true;
+        }
+        if (getAppCompatDisplayInsets() == null || !shouldCreateAppCompatDisplayInsets()
+                // The orientation is different from parent when transforming.
+                || mActivityRecord.isFixedRotationTransforming()) {
+            return false;
+        }
+        final Rect appBounds = mActivityRecord.getConfiguration().windowConfiguration
+                .getAppBounds();
+        if (appBounds == null) {
+            // The app bounds hasn't been computed yet.
+            return false;
+        }
+        final WindowContainer<?> parent = mActivityRecord.getParent();
+        if (parent == null) {
+            // The parent of detached Activity can be null.
+            return false;
+        }
+        final Configuration parentConfig = parent.getConfiguration();
+        // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
+        // fields should be changed with density and bounds, so here only compares the most
+        // significant field.
+        return parentConfig.densityDpi != mActivityRecord.getConfiguration().densityDpi;
+    }
+
+    /**
+     * Indicates the activity will keep the bounds and screen configuration when it was first
+     * launched, no matter how its parent changes.
+     *
+     * <p>If {@true}, then {@link AppCompatDisplayInsets} will be created in {@link
+     * ActivityRecord#resolveOverrideConfiguration} to "freeze" activity bounds and insets.
+     *
+     * @return {@code true} if this activity is declared as non-resizable and fixed orientation or
+     *         aspect ratio.
+     */
+    boolean shouldCreateAppCompatDisplayInsets() {
+        if (mActivityRecord.mAppCompatController.getAppCompatAspectRatioOverrides()
+                .hasFullscreenOverride()) {
+            // If the user has forced the applications aspect ratio to be fullscreen, don't use size
+            // compatibility mode in any situation. The user has been warned and therefore accepts
+            // the risk of the application misbehaving.
+            return false;
+        }
+        switch (supportsSizeChanges()) {
+            case SIZE_CHANGES_SUPPORTED_METADATA:
+            case SIZE_CHANGES_SUPPORTED_OVERRIDE:
+                return false;
+            case SIZE_CHANGES_UNSUPPORTED_OVERRIDE:
+                return true;
+            default:
+                // Fall through
+        }
+        // Use root activity's info for tasks in multi-window mode, or fullscreen tasks in freeform
+        // task display areas, to ensure visual consistency across activity launches and exits in
+        // the same task.
+        final TaskDisplayArea tda = mActivityRecord.getTaskDisplayArea();
+        if (mActivityRecord.inMultiWindowMode() || (tda != null && tda.inFreeformWindowingMode())) {
+            final Task task = mActivityRecord.getTask();
+            final ActivityRecord root = task != null ? task.getRootActivity() : null;
+            if (root != null && root != mActivityRecord
+                    && !root.shouldCreateAppCompatDisplayInsets()) {
+                // If the root activity doesn't use size compatibility mode, the activities above
+                // are forced to be the same for consistent visual appearance.
+                return false;
+            }
+        }
+        final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivityRecord.mAppCompatController
+                .getAspectRatioPolicy();
+        return !mActivityRecord.isResizeable() && (mActivityRecord.info.isFixedOrientation()
+                || aspectRatioPolicy.hasFixedAspectRatio())
+                // The configuration of non-standard type should be enforced by system.
+                // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
+                // added to a task, but this function is called when resolving the launch params, at
+                // which point, the activity type is still undefined if it will be standard.
+                // For other non-standard types, the type is set in the constructor, so this should
+                // not be a problem.
+                && mActivityRecord.isActivityTypeStandardOrUndefined();
+    }
+
+    /**
+     * Returns whether the activity supports size changes.
+     */
+    @ActivityInfo.SizeChangesSupportMode
+    int supportsSizeChanges() {
+        final AppCompatResizeOverrides resizeOverrides = mAppCompatOverrides.getResizeOverrides();
+        if (resizeOverrides.shouldOverrideForceNonResizeApp()) {
+            return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
+        }
+
+        if (mActivityRecord.info.supportsSizeChanges) {
+            return SIZE_CHANGES_SUPPORTED_METADATA;
+        }
+
+        if (resizeOverrides.shouldOverrideForceResizeApp()) {
+            return SIZE_CHANGES_SUPPORTED_OVERRIDE;
+        }
+
+        return SIZE_CHANGES_UNSUPPORTED_METADATA;
+    }
+
 
     private boolean isInSizeCompatModeForBounds(final @NonNull Rect appBounds,
             final @NonNull Rect containerBounds) {
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index e28dddc..e54e93a 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -222,7 +222,7 @@
             return "SIZE_COMPAT_MODE";
         }
         final AppCompatAspectRatioPolicy aspectRatioPolicy = activityRecord.mAppCompatController
-                .getAppCompatAspectRatioPolicy();
+                .getAspectRatioPolicy();
         if (aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio()) {
             return "FIXED_ORIENTATION";
         }
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 37575f0..ab32e54 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -36,6 +36,7 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
 
+import android.annotation.BinderThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -44,6 +45,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -705,12 +707,34 @@
         private WindowState mNavigatingWindow;
         private RemoteCallback mObserver;
 
+        private final IBinder.DeathRecipient mListenerDeathRecipient =
+                new IBinder.DeathRecipient() {
+                    @Override
+                    @BinderThread
+                    public void binderDied() {
+                        synchronized (mWindowManagerService.mGlobalLock) {
+                            stopMonitorForRemote();
+                            stopMonitorTransition();
+                        }
+                    }
+                };
+
         void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) {
             mNavigatingWindow = window;
             mObserver = observer;
+            try {
+                mObserver.getInterface().asBinder().linkToDeath(mListenerDeathRecipient,
+                        0 /* flags */);
+            } catch (RemoteException r) {
+                Slog.e(TAG, "Failed to link to death");
+            }
         }
 
         void stopMonitorForRemote() {
+            if (mObserver != null) {
+                mObserver.getInterface().asBinder().unlinkToDeath(mListenerDeathRecipient,
+                        0 /* flags */);
+            }
             mObserver = null;
         }
 
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index d3c3d28..ba1401a 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -79,7 +79,8 @@
      * @param result    The resulting params.
      */
     void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source,
-            ActivityOptions options, @Nullable Request request, int phase, LaunchParams result) {
+            ActivityOptions options, @Nullable Request request,
+            @LaunchParamsModifier.Phase int phase, LaunchParams result) {
         result.reset();
 
         if (task != null || activity != null) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 57fe0bb..04f09d5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -152,7 +152,6 @@
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.UserState;
-import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.WindowManagerPolicy;
@@ -1541,20 +1540,18 @@
         ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
         boolean lookForSecondaryHomeActivityInPrimaryHomePackage = aInfo != null;
 
-        if (android.companion.virtual.flags.Flags.vdmCustomHome()) {
-            // Resolve the externally set home activity for this display, if any. If it is unset or
-            // we fail to resolve it, fallback to the default secondary home activity.
-            final ComponentName customHomeComponent =
-                    taskDisplayArea.getDisplayContent() != null
-                            ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
-                            : null;
-            if (customHomeComponent != null) {
-                homeIntent.setComponent(customHomeComponent);
-                ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
-                if (customHomeActivityInfo != null) {
-                    aInfo = customHomeActivityInfo;
-                    lookForSecondaryHomeActivityInPrimaryHomePackage = false;
-                }
+        // Resolve the externally set home activity for this display, if any. If it is unset or
+        // we fail to resolve it, fallback to the default secondary home activity.
+        final ComponentName customHomeComponent =
+                taskDisplayArea.getDisplayContent() != null
+                        ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
+                        : null;
+        if (customHomeComponent != null) {
+            homeIntent.setComponent(customHomeComponent);
+            ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
+            if (customHomeActivityInfo != null) {
+                aInfo = customHomeActivityInfo;
+                lookForSecondaryHomeActivityInPrimaryHomePackage = false;
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index c39671d..e3a5b66 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -96,9 +96,10 @@
     }
 
     @Override
+    @Result
     public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
             @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
-            @Nullable ActivityOptions options, @Nullable Request request, int phase,
+            @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase,
             LaunchParams currentParams, LaunchParams outParams) {
         initLogBuilder(task, activity);
         final int result = calculate(task, layout, activity, source, options, request, phase,
@@ -107,9 +108,10 @@
         return result;
     }
 
+    @Result
     private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
             @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
-            @Nullable ActivityOptions options, @Nullable Request request, int phase,
+            @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase,
             LaunchParams currentParams, LaunchParams outParams) {
         final ActivityRecord root;
         if (task != null) {
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index edd9924..88ea073 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -204,7 +204,7 @@
             return true;
         }
         final AppCompatSizeCompatModePolicy scmPolicy = mActivityRecord.mAppCompatController
-                .getAppCompatSizeCompatModePolicy();
+                .getSizeCompatModePolicy();
         if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
                 || scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()) {
             return true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e4ef3d1..dd6e15b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -331,6 +331,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -600,6 +601,7 @@
     final boolean mLimitedAlphaCompositing;
     final int mMaxUiWidth;
 
+    @NonNull
     @VisibleForTesting
     WindowManagerPolicy mPolicy;
 
@@ -1054,13 +1056,16 @@
     private boolean mAnimationsDisabled = false;
     boolean mPointerLocationEnabled = false;
 
+    @NonNull
     final AppCompatConfiguration mAppCompatConfiguration;
 
     private boolean mIsIgnoreOrientationRequestDisabled;
 
+    @NonNull
     final InputManagerService mInputManager;
     final DisplayManagerInternal mDisplayManagerInternal;
     final DisplayManager mDisplayManager;
+    @NonNull
     final ActivityTaskManagerService mAtmService;
 
     /** Indicates whether this device supports wide color gamut / HDR rendering */
@@ -1116,7 +1121,9 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
+    @NonNull
     Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
+    @NonNull
     Supplier<SurfaceControl.Transaction> mTransactionFactory;
 
     private final SurfaceControl.Transaction mTransaction;
@@ -1188,9 +1195,11 @@
 
     private volatile boolean mDisableSecureWindows = false;
 
-    public static WindowManagerService main(final Context context, final InputManagerService im,
-            final boolean showBootMsgs, WindowManagerPolicy policy,
-            ActivityTaskManagerService atm) {
+    /** Creates an instance of the WindowManagerService for the system server. */
+    public static WindowManagerService main(@NonNull final Context context,
+            @NonNull final InputManagerService im, final boolean showBootMsgs,
+            @NonNull final WindowManagerPolicy policy,
+            @NonNull final ActivityTaskManagerService atm) {
         // Using SysUI context to have access to Material colors extracted from Wallpaper.
         final AppCompatConfiguration appCompat = new AppCompatConfiguration(
                 ActivityThread.currentActivityThread().getSystemUiContext());
@@ -1204,15 +1213,19 @@
 
     /**
      * Creates and returns an instance of the WindowManagerService. This call allows the caller
-     * to override factories that can be used to stub native calls during test.
+     * to override factories that can be used to stub native calls during test. Tests should use
+     * {@link WindowManagerServiceTestSupport} instead of calling this directly to ensure
+     * proper initialization and cleanup of dependencies.
      */
-    @VisibleForTesting
-    public static WindowManagerService main(final Context context, final InputManagerService im,
-            final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
-            DisplayWindowSettingsProvider displayWindowSettingsProvider,
-            Supplier<SurfaceControl.Transaction> transactionFactory,
-            Supplier<SurfaceControl.Builder> surfaceControlFactory,
-            AppCompatConfiguration appCompat) {
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static WindowManagerService main(@NonNull final Context context,
+            @NonNull final InputManagerService im, boolean showBootMsgs,
+            @NonNull final WindowManagerPolicy policy,
+            @NonNull final ActivityTaskManagerService atm,
+            @NonNull final DisplayWindowSettingsProvider displayWindowSettingsProvider,
+            @NonNull final Supplier<SurfaceControl.Transaction> transactionFactory,
+            @NonNull final Supplier<SurfaceControl.Builder> surfaceControlFactory,
+            @NonNull final AppCompatConfiguration appCompat) {
 
         final WindowManagerService[] wms = new WindowManagerService[1];
         DisplayThread.getHandler().runWithScissors(() ->
@@ -1238,12 +1251,13 @@
         new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
     }
 
-    private WindowManagerService(Context context, InputManagerService inputManager,
-            boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
-            DisplayWindowSettingsProvider displayWindowSettingsProvider,
-            Supplier<SurfaceControl.Transaction> transactionFactory,
-            Supplier<SurfaceControl.Builder> surfaceControlFactory,
-            AppCompatConfiguration appCompat) {
+    private WindowManagerService(@NonNull Context context,
+            @NonNull InputManagerService inputManager, boolean showBootMsgs,
+            @NonNull WindowManagerPolicy policy, @NonNull ActivityTaskManagerService atm,
+            @NonNull DisplayWindowSettingsProvider displayWindowSettingsProvider,
+            @NonNull Supplier<SurfaceControl.Transaction> transactionFactory,
+            @NonNull Supplier<SurfaceControl.Builder> surfaceControlFactory,
+            @NonNull AppCompatConfiguration appCompat) {
         installLock(this, INDEX_WINDOW);
         mGlobalLock = atm.getGlobalLock();
         mAtmService = atm;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0154d95..d973fb0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -480,18 +480,6 @@
                 if (mLastHidden) {
                     showRobustly(t);
                     mLastHidden = false;
-                    final DisplayContent displayContent = w.getDisplayContent();
-                    if (!displayContent.getLastHasContent()) {
-                        // This draw means the difference between unique content and mirroring.
-                        // Run another pass through performLayout to set mHasContent in the
-                        // LogicalDisplay.
-                        displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
-                        if (DEBUG_LAYOUT_REPEATS) {
-                            mService.mWindowPlacerLocked.debugLayoutRepeats(
-                                    "showSurfaceRobustlyLocked " + w,
-                                    displayContent.pendingLayoutChanges);
-                        }
-                    }
                 }
             }
         }
diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd
index 4a94732..324c9b4 100644
--- a/services/core/xsd/device-state-config/device-state-config.xsd
+++ b/services/core/xsd/device-state-config/device-state-config.xsd
@@ -41,6 +41,7 @@
                 <xs:annotation name="nullable" />
             </xs:element>
             <xs:element name="properties" type="properties" />
+            <xs:element name="flags" type="flags" />
             <xs:element name="conditions" type="conditions" />
         </xs:sequence>
     </xs:complexType>
@@ -53,6 +54,14 @@
         </xs:sequence>
     </xs:complexType>
 
+    <xs:complexType name="flags">
+        <xs:sequence>
+            <xs:element name="flag" type="xs:string" minOccurs="0" maxOccurs="unbounded">
+                <xs:annotation name="nullable" />
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
     <xs:complexType name="conditions">
         <xs:sequence>
             <xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0">
diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt
index 5bb216e..c94f3a8 100644
--- a/services/core/xsd/device-state-config/schema/current.txt
+++ b/services/core/xsd/device-state-config/schema/current.txt
@@ -11,10 +11,12 @@
   public class DeviceState {
     ctor public DeviceState();
     method public com.android.server.policy.devicestate.config.Conditions getConditions();
+    method public com.android.server.policy.devicestate.config.Flags getFlags();
     method public java.math.BigInteger getIdentifier();
     method @Nullable public String getName();
     method public com.android.server.policy.devicestate.config.Properties getProperties();
     method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
+    method public void setFlags(com.android.server.policy.devicestate.config.Flags);
     method public void setIdentifier(java.math.BigInteger);
     method public void setName(@Nullable String);
     method public void setProperties(com.android.server.policy.devicestate.config.Properties);
@@ -25,6 +27,11 @@
     method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
   }
 
+  public class Flags {
+    ctor public Flags();
+    method @Nullable public java.util.List<java.lang.String> getFlag();
+  }
+
   public class LidSwitchCondition {
     ctor public LidSwitchCondition();
     method public boolean getOpen();
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 8ce2422..70eeae6 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -50,6 +50,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Before;
@@ -74,6 +75,11 @@
         super.setUp();
         ImeVisibilityStateComputer.Injector injector = new ImeVisibilityStateComputer.Injector() {
             @Override
+            public UserManagerInternal getUserManagerService() {
+                return mMockUserManagerInternal;
+            }
+
+            @Override
             public WindowManagerInternal getWmService() {
                 return mMockWindowManagerInternal;
             }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index b984624..6adb01c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -23,6 +23,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
@@ -49,6 +50,9 @@
     private InputMethodManagerService mMockInputMethodManagerService;
 
     @Mock
+    private UserManagerInternal mMockUserManagerInternal;
+
+    @Mock
     private WindowManagerInternal mMockWindowManagerInternal;
 
     @NonNull
@@ -70,6 +74,12 @@
                 new ImeVisibilityStateComputer.Injector() {
                     @NonNull
                     @Override
+                    public UserManagerInternal getUserManagerService() {
+                        return mMockUserManagerInternal;
+                    }
+
+                    @NonNull
+                    @Override
                     public WindowManagerInternal getWmService() {
                         return mMockWindowManagerInternal;
                     }
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
index db58c74..29f55ff 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,7 +82,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs() {
+    public void testWriteAuditLogs() throws Exception {
         writeTestLog("granted", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm1", TEST_DOMAIN, "ttype1", "tclass1");
 
@@ -117,7 +118,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_multiplePerms() {
+    public void testWriteAuditLogs_multiplePerms() throws Exception {
         writeTestLog("denied", "perm1 perm2", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm3 perm4", TEST_DOMAIN, "ttype", "tclass");
 
@@ -153,7 +154,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_withPaths() {
+    public void testWriteAuditLogs_withPaths() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/good/path");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/very/long/path");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/short_path");
@@ -217,7 +218,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_withCategories() {
+    public void testWriteAuditLogs_withCategories() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123}, "ttype", null, "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123, 456}, "ttype", null, "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, null, "ttype", new int[] {666}, "tclass");
@@ -288,7 +289,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_withPathAndCategories() {
+    public void testWriteAuditLogs_withPathAndCategories() throws Exception {
         writeTestLog(
                 "denied",
                 "perm",
@@ -318,7 +319,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_permissive() {
+    public void testWriteAuditLogs_permissive() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", true);
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", false);
@@ -356,7 +357,7 @@
     }
 
     @Test
-    public void testNotWriteAuditLogs_notTestDomain() {
+    public void testNotWriteAuditLogs_notTestDomain() throws Exception {
         writeTestLog("denied", "perm", "stype", "ttype", "tclass");
 
         boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -379,7 +380,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_upToQuota() {
+    public void testWriteAuditLogs_upToQuota() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
@@ -389,9 +390,9 @@
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
 
-        boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
+        assertThrows(QuotaExceededException.class, () ->
+                mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
 
-        assertThat(done).isTrue();
         verify(
                 () ->
                         FrameworkStatsLog.write(
@@ -409,7 +410,7 @@
     }
 
     @Test
-    public void testWriteAuditLogs_resetQuota() {
+    public void testWriteAuditLogs_resetQuota() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
@@ -418,8 +419,8 @@
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
 
-        boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
-        assertThat(done).isTrue();
+        assertThrows(QuotaExceededException.class, () ->
+                mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
         verify(
                 () ->
                         FrameworkStatsLog.write(
@@ -442,8 +443,8 @@
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         // move the clock forward to reset the quota limiter.
         mClock.currentTimeMillis += Duration.ofHours(1).toMillis();
-        done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
-        assertThat(done).isTrue();
+        assertThrows(QuotaExceededException.class, () ->
+                mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
         verify(
                 () ->
                         FrameworkStatsLog.write(
@@ -461,15 +462,12 @@
     }
 
     @Test
-    public void testNotWriteAuditLogs_stopRequested() {
+    public void testNotWriteAuditLogs_stopRequested() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
-        // These are not pushed.
-        writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
-        writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
 
         mSelinuxAutidLogsCollector.setStopRequested(true);
         boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -509,7 +507,7 @@
     }
 
     @Test
-    public void testAuditLogs_resumeJobDoesNotExceedLimit() {
+    public void testAuditLogs_resumeJobDoesNotExceedLimit() throws Exception {
         writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
         mSelinuxAutidLogsCollector.setStopRequested(true);
 
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
index 2aea8a0..344f329 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
@@ -53,7 +53,7 @@
     }
 
     @Test
-    public void testFinishSuccessfully() {
+    public void testFinishSuccessfully() throws Exception {
         when(mAuditLogsCollector.collect(anyInt())).thenReturn(true);
 
         mAuditLogsJob.start(mJobService, mParams);
@@ -63,7 +63,7 @@
     }
 
     @Test
-    public void testInterrupt() {
+    public void testInterrupt() throws Exception {
         when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
 
         mAuditLogsJob.start(mJobService, mParams);
@@ -73,7 +73,7 @@
     }
 
     @Test
-    public void testInterruptAndResume() {
+    public void testInterruptAndResume() throws Exception {
         when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
         mAuditLogsJob.start(mJobService, mParams);
         verify(mJobService, never()).jobFinished(any(), anyBoolean());
@@ -85,7 +85,7 @@
     }
 
     @Test
-    public void testRequestStop() throws InterruptedException {
+    public void testRequestStop() throws Exception {
         Semaphore isRunning = new Semaphore(0);
         Semaphore stopRequested = new Semaphore(0);
         AtomicReference<Throwable> uncaughtException = new AtomicReference<>();
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 dafe482..4d8aef4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -829,26 +829,6 @@
 
     @SmallTest
     @Test
-    @DisableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
-    public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
-        final AccessibilityUserState userState = mA11yms.mUserStates.get(
-                mA11yms.getCurrentUserIdLocked());
-        mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
-        userState.updateShortcutTargetsLocked(
-                Set.of(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()), HARDWARE);
-
-        mA11yms.performAccessibilityShortcut(
-                Display.DEFAULT_DISPLAY, HARDWARE,
-                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
-        mTestableLooper.processAllMessages();
-
-        assertStartActivityWithExpectedComponentName(mTestableContext.getMockContext(),
-                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
-    }
-
-    @SmallTest
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
     public void testPerformAccessibilityShortcut_hearingAids_sendExpectedBroadcast() {
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 861e72d..cfdf176 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -2868,6 +2868,9 @@
 
         assertThat(mPowerManager.isInteractive()).isTrue();
         mNativeWrapper.clearResultMessages();
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
         mTestLooper.moveTimeForward(MONITORING_INTERVAL_MS);
         mTestLooper.dispatchAll();
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 776f05d..f536cae 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -167,7 +167,7 @@
      * Test for {@link ShortcutService#getLastResetTimeLocked()} and
      * {@link ShortcutService#getNextResetTimeLocked()}.
      */
-    public void testUpdateAndGetNextResetTimeLocked() {
+    public void disabled_testUpdateAndGetNextResetTimeLocked() {
         assertResetTimes(START_TIME, START_TIME + INTERVAL);
 
         // Advance clock.
@@ -284,7 +284,7 @@
         assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
     }
 
-    public void testSetDynamicShortcuts() {
+    public void disabled_testSetDynamicShortcuts() {
         setCaller(CALLING_PACKAGE_1, USER_10);
 
         final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -596,7 +596,7 @@
                 eq(CALLING_PACKAGE_2), any(), eq(USER_10));
     }
 
-    public void testUnlimitedCalls() {
+    public void disabled_testUnlimitedCalls() {
         setCaller(CALLING_PACKAGE_1, USER_10);
 
         final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -1205,7 +1205,7 @@
                         maxSize));
     }
 
-    public void testShrinkBitmap() {
+    public void disabled_testShrinkBitmap() {
         checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
         checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
         checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
@@ -1302,7 +1302,7 @@
         assertFalse(p11_1_3.getName().contains("_"));
     }
 
-    public void testUpdateShortcuts() {
+    public void disabled_testUpdateShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1"),
@@ -1433,7 +1433,7 @@
         });
     }
 
-    public void testUpdateShortcuts_icons() {
+    public void disabled_testUpdateShortcuts_icons() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1")
@@ -1527,7 +1527,7 @@
         });
     }
 
-    public void testShortcutManagerGetShortcuts_shortcutTypes() {
+    public void disabled_testShortcutManagerGetShortcuts_shortcutTypes() {
 
         // Create 3 manifest and 3 dynamic shortcuts
         addManifestShortcutResource(
@@ -2281,7 +2281,7 @@
         assertEquals("ABC", findById(list, "s1").getTitle());
     }
 
-    public void testPinShortcutAndGetPinnedShortcuts() {
+    public void disabled_testPinShortcutAndGetPinnedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
             final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -3478,7 +3478,7 @@
         });
     }
 
-    public void testStartShortcut() {
+    public void disabled_testStartShortcut() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final ShortcutInfo s1_1 = makeShortcut(
@@ -3902,7 +3902,7 @@
 
     // === Test for persisting ===
 
-    public void testSaveAndLoadUser_empty() {
+    public void disabled_testSaveAndLoadUser_empty() {
         assertTrue(mManager.setDynamicShortcuts(list()));
 
         Log.i(TAG, "Saved state");
@@ -4074,7 +4074,7 @@
         assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
     }
 
-    public void testSaveCorruptAndLoadUser() throws Exception {
+    public void disabled_testSaveCorruptAndLoadUser() throws Exception {
         // First, create some shortcuts and save.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -6658,7 +6658,7 @@
 
     }
 
-    public void testSaveAndLoad_crossProfile() {
+    public void disabled_testSaveAndLoad_crossProfile() {
         prepareCrossProfileDataSet();
 
         dumpsysOnLogcat("Before save & load");
@@ -8471,7 +8471,7 @@
         });
     }
 
-    public void testShortcutsPushedOutByManifest() {
+    public void disabled_testShortcutsPushedOutByManifest() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8708,7 +8708,7 @@
         }
     }
 
-    public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+    public void disabled_testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
         List<ShareTargetInfo> expectedValues = new ArrayList<>();
         expectedValues.add(new ShareTargetInfo(
                 new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8902,7 +8902,7 @@
         });
     }
 
-    public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+    public void disabled_testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
         final ShortcutInfo s1 = makeShortcut("s1");
         final ShortcutInfo s2 = makeShortcut("s2");
         final ShortcutInfo s3 = makeShortcut("s3");
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 5518082..7f2935e 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -18,6 +18,7 @@
 
 
 import static android.content.Context.SENSOR_SERVICE;
+import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_CONFIGURATION_FLAG;
 
 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
@@ -42,6 +43,9 @@
 import android.hardware.SensorManager;
 import android.hardware.devicestate.DeviceState;
 import android.os.PowerManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
 import androidx.annotation.NonNull;
 
@@ -51,6 +55,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
@@ -84,6 +89,10 @@
     private Context mContext;
     private SensorManager mSensorManager;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setup() {
         LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class));
@@ -605,6 +614,37 @@
         verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
     }
 
+    @RequiresFlagsEnabled(FLAG_DEVICE_STATE_CONFIGURATION_FLAG)
+    @Test
+    public void test_createConfigWithFlags() {
+        String configString = "<device-state-config>\n"
+                + "    <device-state>\n"
+                + "        <identifier>1</identifier>\n"
+                + "        <flags>\n"
+                + "            <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
+                + "        </flags>\n"
+                + "    </device-state>\n"
+                + "    <device-state>\n"
+                + "        <identifier>2</identifier>\n"
+                + "        <name>CLOSED</name>\n"
+                + "    </device-state>\n"
+                + "</device-state-config>\n";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+        DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+                config);
+
+        DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+        provider.setListener(listener);
+
+        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+                eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+        final DeviceState[] expectedStates = new DeviceState[]{
+                createDeviceState(1, "",
+                        Set.of(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)),
+                createDeviceState(2, "CLOSED", EMPTY_PROPERTY_SET)};
+        assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+    }
+
     private DeviceState createDeviceState(int identifier, @NonNull String name,
             @NonNull Set<@DeviceState.DeviceStateProperties Integer> systemProperties) {
         DeviceState.Configuration configuration = new DeviceState.Configuration.Builder(identifier,
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java
new file mode 100644
index 0000000..5d181b8
--- /dev/null
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java
@@ -0,0 +1,141 @@
+/*
+ * 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.server.timezonedetector;
+
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+
+import com.android.server.SystemTimeZone;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A partially implemented, fake implementation of Environment for tests.
+ */
+public class FakeEnvironment implements Environment {
+
+    private final TestState<String> mTimeZoneId = new TestState<>();
+    private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
+    private final List<Runnable> mAsyncRunnables = new ArrayList<>();
+    private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
+    private @CurrentTimeMillisLong long mInitializationTimeMillis;
+
+    FakeEnvironment() {
+        // Ensure the fake environment starts with the defaults a fresh device would.
+        initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW);
+    }
+
+    void initializeClock(@CurrentTimeMillisLong long currentTimeMillis,
+            @ElapsedRealtimeLong long elapsedRealtimeMillis) {
+        mInitializationTimeMillis = currentTimeMillis - elapsedRealtimeMillis;
+        mElapsedRealtimeMillis = elapsedRealtimeMillis;
+    }
+
+    void initializeTimeZoneSetting(String zoneId,
+            @SystemTimeZone.TimeZoneConfidence int timeZoneConfidence) {
+        mTimeZoneId.init(zoneId);
+        mTimeZoneConfidence.init(timeZoneConfidence);
+    }
+
+    void incrementClock() {
+        mElapsedRealtimeMillis++;
+    }
+
+    @Override
+    public String getDeviceTimeZone() {
+        return mTimeZoneId.getLatest();
+    }
+
+    @Override
+    public int getDeviceTimeZoneConfidence() {
+        return mTimeZoneConfidence.getLatest();
+    }
+
+    @Override
+    public void setDeviceTimeZoneAndConfidence(
+            String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, String logInfo) {
+        mTimeZoneId.set(zoneId);
+        mTimeZoneConfidence.set(confidence);
+    }
+
+    void assertTimeZoneNotChanged() {
+        mTimeZoneId.assertHasNotBeenSet();
+        mTimeZoneConfidence.assertHasNotBeenSet();
+    }
+
+    void assertTimeZoneChangedTo(String timeZoneId,
+            @SystemTimeZone.TimeZoneConfidence int confidence) {
+        mTimeZoneId.assertHasBeenSet();
+        mTimeZoneId.assertChangeCount(1);
+        mTimeZoneId.assertLatestEquals(timeZoneId);
+
+        mTimeZoneConfidence.assertHasBeenSet();
+        mTimeZoneConfidence.assertChangeCount(1);
+        mTimeZoneConfidence.assertLatestEquals(confidence);
+    }
+
+    void commitAllChanges() {
+        mTimeZoneId.commitLatest();
+        mTimeZoneConfidence.commitLatest();
+    }
+
+    @Override
+    @ElapsedRealtimeLong
+    public long elapsedRealtimeMillis() {
+        return mElapsedRealtimeMillis;
+    }
+
+    @Override
+    @CurrentTimeMillisLong
+    public long currentTimeMillis() {
+        return mInitializationTimeMillis + mElapsedRealtimeMillis;
+    }
+
+    @Override
+    public void addDebugLogEntry(String logMsg) {
+        // No-op for tests
+    }
+
+    @Override
+    public void dumpDebugLog(PrintWriter printWriter) {
+        // No-op for tests
+    }
+
+    /**
+     * Adds the supplied runnable to a list but does not run them. To run all the runnables that
+     * have been supplied, call {@code #runAsyncRunnables}.
+     */
+    @Override
+    public void runAsync(Runnable runnable) {
+        mAsyncRunnables.add(runnable);
+    }
+
+    /**
+     * Requests that the runnable that have been supplied to {@code #runAsync} are invoked
+     * asynchronously and cleared.
+     */
+    public void runAsyncRunnables() {
+        for (Runnable runnable : mAsyncRunnables) {
+            runnable.run();
+        }
+        mAsyncRunnables.clear();
+    }
+}
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
index 23c13bd..45cc891 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
@@ -85,9 +85,6 @@
         return List.of(ORIGIN_LOCATION, ORIGIN_TELEPHONY);
     }
 
-    private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
-    /** A time zone used for initialization that does not occur elsewhere in tests. */
-    private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
     private static final String INTERACT_ACROSS_USERS_FULL_PERMISSION =
             "android.permission.INTERACT_ACROSS_USERS_FULL";
 
@@ -99,6 +96,7 @@
     private HandlerThread mHandlerThread;
     private TestHandler mHandler;
     private FakeServiceConfigAccessor mServiceConfigAccessor;
+    private FakeEnvironment mFakeEnvironment;
     private int mUid;
 
     private NotifyingTimeZoneChangeListener mTimeZoneChangeTracker;
@@ -106,6 +104,9 @@
     @Before
     public void setUp() {
         mUid = Process.myUid();
+        mFakeEnvironment = new FakeEnvironment();
+        mFakeEnvironment.initializeClock(1735689600L, 1234L);
+
         // Create a thread + handler for processing the work that the service posts.
         mHandlerThread = new HandlerThread("TimeZoneDetectorInternalTest");
         mHandlerThread.start();
@@ -138,7 +139,7 @@
         mNotificationManager = new FakeNotificationManager(mContext, InstantSource.system());
 
         mTimeZoneChangeTracker = new NotifyingTimeZoneChangeListener(mHandler, mContext,
-                mServiceConfigAccessor, mNotificationManager);
+                mServiceConfigAccessor, mNotificationManager, mFakeEnvironment);
     }
 
     @After
@@ -151,7 +152,7 @@
     public void process_autoDetectionOff_noManualTracking_shouldTrackWithoutNotifying() {
         enableTimeZoneNotifications();
 
-        TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+        TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 1,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 0,
@@ -162,13 +163,14 @@
                         /* newZoneId= */ "Europe/London",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
 
         assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
 
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
 
-        assertEquals(expectedChangeEvent, mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
+        assertEquals(expectedTimeZoneChangeRecord,
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
         assertEquals(0, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(0);
     }
@@ -177,7 +179,7 @@
     public void process_autoDetectionOff_shouldTrackWithoutNotifying() {
         enableNotificationsWithManualChangeTracking();
 
-        TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+        TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 1,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 0,
@@ -188,13 +190,14 @@
                         /* newZoneId= */ "Europe/London",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
 
         assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
 
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
 
-        assertEquals(expectedChangeEvent, mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
+        assertEquals(expectedTimeZoneChangeRecord,
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
         assertEquals(0, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(1);
     }
@@ -214,7 +217,7 @@
 
         enableNotificationsWithManualChangeTracking();
 
-        TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+        TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 1,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 0,
@@ -225,19 +228,20 @@
                         /* newZoneId= */ "Europe/London",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
 
         assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
 
         // lastTrackedChangeEvent == null
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-        TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+        TimeZoneChangeRecord timeZoneChangeRecord1 =
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
-        assertEquals(expectedChangeEvent, trackedEvent1);
+        assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
         assertEquals(1, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(1);
 
-        expectedChangeEvent = new TimeZoneChangeRecord(
+        expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 2,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 1000L,
@@ -248,14 +252,15 @@
                         /* newZoneId= */ "Europe/Paris",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
 
         // lastTrackedChangeEvent != null
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-        TimeZoneChangeRecord trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+        TimeZoneChangeRecord timeZoneChangeRecord2 =
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
-        assertEquals(STATUS_SUPERSEDED, trackedEvent1.getStatus());
-        assertEquals(expectedChangeEvent, trackedEvent2);
+        assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord1.getStatus());
+        assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord2);
         assertEquals(2, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(2);
 
@@ -263,7 +268,7 @@
 
         // Test manual change within revert threshold
         {
-            expectedChangeEvent = new TimeZoneChangeRecord(
+            expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                     /* id= */ 3,
                     new TimeZoneChangeEvent(
                             /* elapsedRealtimeMillis= */ 999L + AUTO_REVERT_THRESHOLD,
@@ -275,16 +280,16 @@
                             /* newZoneId= */ "Europe/London",
                             /* newConfidence= */ 1,
                             /* cause= */ "NO_REASON"));
-            expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+            expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
 
-            mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-            TimeZoneChangeRecord trackedEvent3 =
+            mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+            TimeZoneChangeRecord timeZoneChangeRecord3 =
                     mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
             // The user manually changed the time zone within a short period of receiving the
             // notification, indicating that they rejected the automatic change of time zone
-            assertEquals(STATUS_REJECTED, trackedEvent2.getStatus());
-            assertEquals(expectedChangeEvent, trackedEvent3);
+            assertEquals(STATUS_REJECTED, timeZoneChangeRecord2.getStatus());
+            assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord3);
             assertEquals(2, mNotificationManager.getNotifications().size());
             mHandler.assertTotalMessagesEnqueued(3);
         }
@@ -293,12 +298,12 @@
         {
             // [START] Reset previous event
             enableNotificationsWithManualChangeTracking();
-            mTimeZoneChangeTracker.process(trackedEvent2.getEvent());
-            trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+            mTimeZoneChangeTracker.process(timeZoneChangeRecord2.getEvent());
+            timeZoneChangeRecord2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
             disableTimeZoneAutoDetection();
             // [END] Reset previous event
 
-            expectedChangeEvent = new TimeZoneChangeRecord(
+            expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                     /* id= */ 5,
                     new TimeZoneChangeEvent(
                             /* elapsedRealtimeMillis= */ 1001L + AUTO_REVERT_THRESHOLD,
@@ -310,15 +315,15 @@
                             /* newZoneId= */ "Europe/London",
                             /* newConfidence= */ 1,
                             /* cause= */ "NO_REASON"));
-            expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+            expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
 
-            mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-            TimeZoneChangeRecord trackedEvent3 =
+            mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+            TimeZoneChangeRecord timeZoneChangeRecord3 =
                     mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
             // The user manually changed the time zone outside of the period we consider as a revert
-            assertEquals(STATUS_SUPERSEDED, trackedEvent2.getStatus());
-            assertEquals(expectedChangeEvent, trackedEvent3);
+            assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord2.getStatus());
+            assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord3);
             assertEquals(3, mNotificationManager.getNotifications().size());
             mHandler.assertTotalMessagesEnqueued(5);
         }
@@ -339,7 +344,7 @@
 
         enableNotificationsWithManualChangeTracking();
 
-        TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+        TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 1,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 0,
@@ -350,19 +355,20 @@
                         /* newZoneId= */ "Europe/London",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
 
         assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
 
         // lastTrackedChangeEvent == null
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-        TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+        TimeZoneChangeRecord timeZoneChangeRecord1 =
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
-        assertEquals(expectedChangeEvent, trackedEvent1);
+        assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
         assertEquals(1, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(1);
 
-        expectedChangeEvent = new TimeZoneChangeRecord(
+        expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 3,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 1000L,
@@ -373,14 +379,15 @@
                         /* newZoneId= */ "Europe/Paris",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
 
         // lastTrackedChangeEvent != null
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-        TimeZoneChangeRecord trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+        TimeZoneChangeRecord timeZoneChangeRecord2 =
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
-        assertEquals(STATUS_SUPERSEDED, trackedEvent1.getStatus());
-        assertEquals(expectedChangeEvent, trackedEvent2);
+        assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord1.getStatus());
+        assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord2);
         assertEquals(2, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(2);
     }
@@ -400,7 +407,7 @@
 
         enableNotificationsWithManualChangeTracking();
 
-        TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+        TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
                 /* id= */ 1,
                 new TimeZoneChangeEvent(
                         /* elapsedRealtimeMillis= */ 0,
@@ -411,15 +418,16 @@
                         /* newZoneId= */ "Europe/Rome",
                         /* newConfidence= */ 1,
                         /* cause= */ "NO_REASON"));
-        expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+        expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
 
         assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
 
         // lastTrackedChangeEvent == null
-        mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
-        TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+        mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+        TimeZoneChangeRecord timeZoneChangeRecord1 =
+                mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
 
-        assertEquals(expectedChangeEvent, trackedEvent1);
+        assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
         // No notification sent for the same UTC offset
         assertEquals(0, mNotificationManager.getNotifications().size());
         mHandler.assertTotalMessagesEnqueued(1);
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 9a01fa2..44232e7 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -63,7 +63,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.time.LocationTimeZoneAlgorithmStatus;
@@ -94,7 +93,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -114,6 +112,7 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
 
+    private static final long ARBITRARY_CURRENT_TIME_MILLIS = 1735689600; // 2025-01-01 00:00:00
     private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
     /** A time zone used for initialization that does not occur elsewhere in tests. */
     private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -1220,7 +1219,7 @@
                 .build();
 
         Script script = new Script()
-                .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
                 .simulateConfigurationInternalChange(config)
                 .resetConfigurationTracking();
@@ -1420,7 +1419,7 @@
                 .build();
 
         Script script = new Script()
-                .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
                 .simulateConfigurationInternalChange(config)
                 .resetConfigurationTracking();
@@ -1591,7 +1590,7 @@
                 .build();
 
         Script script = new Script()
-                .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
                 .simulateConfigurationInternalChange(config)
                 .resetConfigurationTracking();
@@ -1666,7 +1665,7 @@
     @Test
     public void testGetTimeZoneState() {
         Script script = new Script()
-                .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
                 .simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
                 .resetConfigurationTracking();
@@ -1688,7 +1687,7 @@
     @Test
     public void testSetTimeZoneState() {
         Script script = new Script()
-                .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
                 .simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
                 .resetConfigurationTracking();
@@ -1715,7 +1714,7 @@
     private void testConfirmTimeZone(ConfigurationInternal config) {
         String timeZoneId = "Europe/London";
         Script script = new Script()
-                .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
                 .initializeTimeZoneSetting(timeZoneId, TIME_ZONE_CONFIDENCE_LOW)
                 .simulateConfigurationInternalChange(config)
                 .resetConfigurationTracking();
@@ -1902,97 +1901,6 @@
                 mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
     }
 
-    static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
-
-        private final TestState<String> mTimeZoneId = new TestState<>();
-        private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
-        private final List<Runnable> mAsyncRunnables = new ArrayList<>();
-        private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
-
-        FakeEnvironment() {
-            // Ensure the fake environment starts with the defaults a fresh device would.
-            initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW);
-        }
-
-        void initializeClock(@ElapsedRealtimeLong long elapsedRealtimeMillis) {
-            mElapsedRealtimeMillis = elapsedRealtimeMillis;
-        }
-
-        void initializeTimeZoneSetting(String zoneId, @TimeZoneConfidence int timeZoneConfidence) {
-            mTimeZoneId.init(zoneId);
-            mTimeZoneConfidence.init(timeZoneConfidence);
-        }
-
-        void incrementClock() {
-            mElapsedRealtimeMillis++;
-        }
-
-        @Override
-        public String getDeviceTimeZone() {
-            return mTimeZoneId.getLatest();
-        }
-
-        @Override
-        public int getDeviceTimeZoneConfidence() {
-            return mTimeZoneConfidence.getLatest();
-        }
-
-        @Override
-        public void setDeviceTimeZoneAndConfidence(
-                String zoneId, @TimeZoneConfidence int confidence, String logInfo) {
-            mTimeZoneId.set(zoneId);
-            mTimeZoneConfidence.set(confidence);
-        }
-
-        void assertTimeZoneNotChanged() {
-            mTimeZoneId.assertHasNotBeenSet();
-            mTimeZoneConfidence.assertHasNotBeenSet();
-        }
-
-        void assertTimeZoneChangedTo(String timeZoneId, @TimeZoneConfidence int confidence) {
-            mTimeZoneId.assertHasBeenSet();
-            mTimeZoneId.assertChangeCount(1);
-            mTimeZoneId.assertLatestEquals(timeZoneId);
-
-            mTimeZoneConfidence.assertHasBeenSet();
-            mTimeZoneConfidence.assertChangeCount(1);
-            mTimeZoneConfidence.assertLatestEquals(confidence);
-        }
-
-        void commitAllChanges() {
-            mTimeZoneId.commitLatest();
-            mTimeZoneConfidence.commitLatest();
-        }
-
-        @Override
-        @ElapsedRealtimeLong
-        public long elapsedRealtimeMillis() {
-            return mElapsedRealtimeMillis;
-        }
-
-        @Override
-        public void addDebugLogEntry(String logMsg) {
-            // No-op for tests
-        }
-
-        @Override
-        public void dumpDebugLog(PrintWriter printWriter) {
-            // No-op for tests
-        }
-
-        @Override
-        public void runAsync(Runnable runnable) {
-            mAsyncRunnables.add(runnable);
-        }
-
-        public void runAsyncRunnables() {
-            for (Runnable runnable : mAsyncRunnables) {
-                runnable.run();
-            }
-            mAsyncRunnables.clear();
-        }
-    }
-
     private void assertStateChangeNotificationsSent(
             TestStateChangeListener stateChangeListener, int expectedCount) {
         // The fake environment needs to be told to run posted work.
@@ -2013,8 +1921,8 @@
             return this;
         }
 
-        Script initializeClock(long elapsedRealtimeMillis) {
-            mFakeEnvironment.initializeClock(elapsedRealtimeMillis);
+        Script initializeClock(long currentTimeMillis, long elapsedRealtimeMillis) {
+            mFakeEnvironment.initializeClock(currentTimeMillis, elapsedRealtimeMillis);
             return this;
         }
 
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 132f95b..b328fc2 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -11,19 +11,46 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-// Include all test java files.
+filegroup {
+    name: "wmtests-support-sources",
+    srcs: [
+        "src/com/android/server/wm/WindowManagerServiceTestSupport.kt",
+    ],
+    path: "src",
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "wmtests-support",
+    srcs: [":wmtests-support-sources"],
+    static_libs: [
+        "com.android.window.flags.window-aconfig-java",
+        "kotlin-stdlib",
+        "services.core",
+    ],
+    lint: {
+        test: true,
+    },
+    visibility: [
+        "//frameworks/base/services/tests/wmtests",
+        "//frameworks/opt/car/services/updatableServices/tests",
+    ],
+}
+
+// Include all test files, but exclude test support files.
 filegroup {
     name: "wmtests-sources",
-    srcs: [
-        "src/**/*.java",
-    ],
+    srcs: ["src/**/*.java"],
+    exclude_srcs: [":wmtests-support-sources"],
+    path: "src",
+    visibility: ["//visibility:private"],
 }
 
 java_genrule {
     name: "wmtests.protologsrc",
     srcs: [
-        ":protolog-impl",
         ":protolog-groups",
+        ":protolog-impl",
         ":wmtests-sources",
     ],
     tools: ["protologtool"],
@@ -52,33 +79,34 @@
     ],
 
     static_libs: [
-        "frameworks-base-testutils",
-        "services.core",
-        "service-permission.stubs.system_server",
-        "androidx.test.runner",
+        "CtsSurfaceValidatorLib",
+        "android.view.inputmethod.flags-aconfig-java",
         "androidx.test.rules",
+        "androidx.test.runner",
+        "com.android.window.flags.window-aconfig-java",
+        "flag-junit",
         "flickerlib",
+        "frameworks-base-testutils",
+        "hamcrest-library",
         "junit-params",
+        "mockito-kotlin2",
         "mockito-target-extended-minus-junit4",
+        "platform-compat-test-rules",
         "platform-test-annotations",
+        "service-permission.stubs.system_server",
+        "service-sdksandbox.impl",
+        "services.core",
         "servicestests-utils",
+        "testables",
         "testng",
         "truth",
-        "testables",
-        "hamcrest-library",
-        "flag-junit",
-        "platform-compat-test-rules",
-        "CtsSurfaceValidatorLib",
-        "service-sdksandbox.impl",
-        "com.android.window.flags.window-aconfig-java",
-        "android.view.inputmethod.flags-aconfig-java",
-        "flag-junit",
+        "wmtests-support",
     ],
 
     libs: [
         "android.hardware.power-V1-java",
-        "android.test.mock.stubs.system",
         "android.test.base.stubs.system",
+        "android.test.mock.stubs.system",
         "android.test.runner.stubs.system",
     ],
 
@@ -94,8 +122,8 @@
 
     platform_apis: true,
     test_suites: [
-        "device-tests",
         "automotive-tests",
+        "device-tests",
     ],
 
     certificate: "platform",
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 6fad82b..c88d515 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -692,7 +692,7 @@
 
         // Asserts fixed orientation request is not ignored, and the orientation is changed.
         assertNotEquals(activityCurOrientation, activity.getConfiguration().orientation);
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -721,13 +721,13 @@
         assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
 
         // Clear size compat.
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
         activity.ensureActivityConfiguration();
         mDisplayContent.sendNewConfiguration();
 
         // Relaunching the app should still respect the orientation request.
         assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -742,7 +742,7 @@
         assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
         // The app should be letterboxed.
         assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -755,7 +755,7 @@
         assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
         // Activity is not letterboxed.
         assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
-        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -770,7 +770,7 @@
         assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
         // Activity is not letterboxed.
         assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
-        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -785,7 +785,7 @@
         assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
         // Activity is not letterboxed.
         assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
-        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -3800,7 +3800,7 @@
                 .setResizeMode(RESIZE_MODE_RESIZEABLE)
                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                 .build();
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
         return activity;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 00c9691..2713644 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -180,7 +180,7 @@
 
     void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
         doReturn(enabled).when(mActivityStack.top().mAppCompatController
-                .getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
+                .getAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
     }
 
     void enableFullscreenCameraCompatTreatmentForTopActivity(boolean enabled) {
@@ -525,7 +525,7 @@
             activity.setRequestedOrientation(screenOrientation);
         }
         // Make sure to use the provided configuration to construct the size compat fields.
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
         activity.ensureActivityConfiguration();
         // Make sure the display configuration reflects the change of activity.
         if (activity.mDisplayContent.updateOrientation()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
index e046f7c..b5ba111 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
@@ -364,7 +364,7 @@
 
         @NonNull
         private AppCompatAspectRatioPolicy getAspectRatioPolicy() {
-            return activity().top().mAppCompatController.getAppCompatAspectRatioPolicy();
+            return activity().top().mAppCompatController.getAspectRatioPolicy();
         }
 
         @NonNull
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
index a0727a7..9e46c09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -275,7 +275,7 @@
         @Override
         void onPostActivityCreation(@NonNull ActivityRecord activity) {
             super.onPostActivityCreation(activity);
-            spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+            spyOn(activity.mAppCompatController.getAspectRatioPolicy());
         }
 
         // Useful to reduce timeout during tests
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 4faa714..93fb73e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -556,7 +556,7 @@
         void onPostActivityCreation(@NonNull ActivityRecord activity) {
             super.onPostActivityCreation(activity);
             spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
-            spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+            spyOn(activity.mAppCompatController.getAspectRatioPolicy());
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index a5b2cb3..bfd533a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -252,7 +252,7 @@
         @Override
         void onPostActivityCreation(@NonNull ActivityRecord activity) {
             super.onPostActivityCreation(activity);
-            spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+            spyOn(activity.mAppCompatController.getAspectRatioPolicy());
         }
 
         @Override
@@ -272,13 +272,13 @@
 
         void setIsLetterboxedForFixedOrientationAndAspectRatio(
                 boolean forFixedOrientationAndAspectRatio) {
-            when(activity().top().mAppCompatController.getAppCompatAspectRatioPolicy()
+            when(activity().top().mAppCompatController.getAspectRatioPolicy()
                     .isLetterboxedForFixedOrientationAndAspectRatio())
                         .thenReturn(forFixedOrientationAndAspectRatio);
         }
 
         void setIsLetterboxedForAspectRatioOnly(boolean forAspectRatio) {
-            when(activity().top().mAppCompatController.getAppCompatAspectRatioPolicy()
+            when(activity().top().mAppCompatController.getAspectRatioPolicy()
                     .isLetterboxedForAspectRatioOnly()).thenReturn(forAspectRatio);
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index b6f4424..576d17a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -606,7 +606,7 @@
                 .setOnTop(true)
                 .setTask(task)
                 .build();
-        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
         spyOn(mActivity.info);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index 347d1bc..a7e8ce9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -216,7 +216,7 @@
         final Rect activityBounds = new Rect(mFirstActivity.getBounds());
 
         // DAG is portrait (860x1200), so Task and Activity fill DAG.
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
         assertThat(taskBounds).isEqualTo(dagBounds);
@@ -241,7 +241,7 @@
                 new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds());
 
         // DAG is landscape (1200x860), no fixed orientation letterbox
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
         assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
@@ -266,12 +266,12 @@
         mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
 
         prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
 
         rotateDisplay(mDisplay, ROTATION_90);
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
     }
@@ -289,7 +289,7 @@
 
         // DAG is portrait (860x1200), and activity is letterboxed for fixed orientation
         // (860x[860x860/1200=616]). Task fills DAG.
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
         assertThat(taskBounds).isEqualTo(dagBounds);
@@ -307,7 +307,7 @@
         mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
 
         prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
     }
 
@@ -318,7 +318,7 @@
         mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
 
         prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED);
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
     }
 
@@ -338,7 +338,7 @@
         final Rect newActivityBounds = new Rect(mFirstActivity.getBounds());
 
         // DAG is landscape (1200x860), no fixed orientation letterbox
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
         assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
@@ -364,7 +364,7 @@
 
         rotateDisplay(mDisplay, ROTATION_90);
 
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
     }
@@ -527,7 +527,7 @@
         assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
         assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
         assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
 
@@ -540,14 +540,14 @@
         assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
         assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
         assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
-        assertThat(mSecondActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mSecondActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mSecondActivity.inSizeCompatMode()).isFalse();
 
         // First activity is letterboxed in portrait as requested.
         assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
         assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
-        assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 7e62b89..fc4f54a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -535,7 +535,7 @@
         final Rect bounds = new Rect(task.getBounds());
         bounds.scale(0.5f);
         task.setBounds(bounds);
-        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(task.autoRemoveRecents).isFalse();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 07ee09a..b19af0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -673,7 +673,7 @@
         assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
 
         // Recompute the natural configuration without resolving size compat configuration.
-        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
         mActivity.onConfigurationChanged(mTask.getConfiguration());
         // It should keep non-attachable because the resolved bounds will be computed according to
         // the aspect ratio that won't match its parent bounds.
@@ -765,7 +765,7 @@
                         / originalBounds.width()));
 
         // Recompute the natural configuration in the new display.
-        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
         mActivity.ensureActivityConfiguration();
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
@@ -1187,7 +1187,7 @@
         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
 
         // Activity max bounds should not be sandboxed, even though it is letterboxed.
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
                 .isEqualTo(activity.getDisplayArea().getBounds());
@@ -1229,7 +1229,7 @@
         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
 
         // Activity max bounds should not be sandboxed, even though it is letterboxed.
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
                 .isEqualTo(activity.getDisplayArea().getBounds());
@@ -1253,7 +1253,7 @@
         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
 
         // Activity max bounds should not be sandboxed, even though it is letterboxed.
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
                 .isEqualTo(activity.getDisplayArea().getBounds());
@@ -1507,7 +1507,7 @@
 
         // After changing the orientation to portrait the override should be applied.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1531,7 +1531,7 @@
 
         // After changing the orientation to portrait the override should be applied.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1554,7 +1554,7 @@
 
         // After changing the orientation to landscape the override shouldn't be applied.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1754,7 +1754,7 @@
         addWindowToActivity(mActivity);
 
         // App should launch in fullscreen.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -1769,7 +1769,7 @@
         assertTrue(rotatedDisplayBounds.width() < rotatedDisplayBounds.height());
 
         // App should be in size compat.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertDownScaled();
         assertThat(mActivity.inSizeCompatMode()).isTrue();
@@ -1882,7 +1882,7 @@
         assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertActivityMaxBoundsSandboxed();
@@ -1913,7 +1913,7 @@
         assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -1943,7 +1943,7 @@
         assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -1973,7 +1973,7 @@
         assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -2070,7 +2070,7 @@
         assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -2094,7 +2094,7 @@
 
         // App should launch in fixed orientation letterbox.
         // Activity bounds should be 700x1400 with the ratio as the display.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFitted();
         assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
@@ -2134,7 +2134,7 @@
 
         // App should launch in fixed orientation letterbox.
         // Activity bounds should be 700x1400 with the ratio as the display.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFitted();
         assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
@@ -2169,7 +2169,7 @@
         final Rect activityBounds = new Rect(mActivity.getBounds());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         // Checking that there is no size compat mode.
         assertFitted();
@@ -2703,7 +2703,7 @@
         final Rect activityBounds = new Rect(mActivity.getBounds());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         // Checking that there is no size compat mode.
         assertFitted();
@@ -2747,7 +2747,7 @@
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         // Checking that there is no size compat mode.
         assertFitted();
@@ -2782,7 +2782,7 @@
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         // Checking that there is no size compat mode.
         assertFitted();
@@ -2808,7 +2808,7 @@
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         // Checking that there is no size compat mode.
         assertFitted();
@@ -2834,7 +2834,7 @@
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
         // App should launch in fixed orientation letterbox.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         // Checking that there is no size compat mode.
         assertFitted();
@@ -2863,7 +2863,7 @@
         assertTrue(displayBounds.width() < displayBounds.height());
 
         // App should be in size compat.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(mActivity.inSizeCompatMode()).isTrue();
         assertEquals(activityBounds.width(), newActivityBounds.width());
@@ -2880,7 +2880,7 @@
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
         // App should launch in fullscreen.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         // Activity inherits max bounds from TaskDisplayArea.
@@ -2894,7 +2894,7 @@
         assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
 
         // App should be in size compat.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertDownScaled();
         assertThat(mActivity.inSizeCompatMode()).isTrue();
@@ -2915,7 +2915,7 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -2938,7 +2938,7 @@
 
         // Task and display bounds should be equal while activity should be letterboxed and
         // has 700x1400 bounds with the ratio as the display.
-        assertTrue(newActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(newActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(newActivity.inSizeCompatMode());
         // Activity max bounds are sandboxed due to size compat mode.
@@ -2959,7 +2959,7 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -2980,7 +2980,7 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -3011,7 +3011,7 @@
         assertActivityMaxBoundsSandboxed();
 
         // Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(newActivity.inSizeCompatMode());
         assertEquals(displayBounds.height(), newActivityBounds.height());
@@ -3030,7 +3030,7 @@
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
         clearInvocations(mActivity);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
@@ -3038,7 +3038,7 @@
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
         // App should be in size compat.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(mActivity.inSizeCompatMode()).isTrue();
         // Activity max bounds are sandboxed due to size compat mode.
@@ -3050,10 +3050,10 @@
 
         // App still in size compat, and the bounds don't change.
         final AppCompatSizeCompatModePolicy scmPolicy = mActivity.mAppCompatController
-                .getAppCompatSizeCompatModePolicy();
+                .getSizeCompatModePolicy();
         spyOn(scmPolicy);
         verify(scmPolicy, never()).clearSizeCompatMode();
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(mActivity.inSizeCompatMode()).isTrue();
         assertEquals(activityBounds, mActivity.getBounds());
@@ -3071,7 +3071,7 @@
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
         // In fixed orientation letterbox
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertActivityMaxBoundsSandboxed();
@@ -3080,7 +3080,7 @@
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertThat(mActivity.inSizeCompatMode()).isTrue();
         assertActivityMaxBoundsSandboxed();
@@ -3089,7 +3089,7 @@
         rotateDisplay(display, ROTATION_180);
 
         // In activity letterbox
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertActivityMaxBoundsSandboxed();
@@ -3108,7 +3108,7 @@
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
 
         // In fixed orientation letterbox
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertActivityMaxBoundsSandboxed();
@@ -3117,7 +3117,7 @@
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertDownScaled();
         assertActivityMaxBoundsSandboxed();
@@ -3126,7 +3126,7 @@
         rotateDisplay(display, ROTATION_180);
 
         // In fixed orientation letterbox
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertActivityMaxBoundsSandboxed();
@@ -3325,7 +3325,7 @@
         assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
         assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
         assertFitted();
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertActivityMaxBoundsSandboxed();
 
@@ -3352,7 +3352,7 @@
 
         // Resizable activity is not in size compat mode but in the letterbox for fixed orientation.
         assertFitted();
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -3389,7 +3389,7 @@
         assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
         assertEquals(ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
         assertFitted();
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertActivityMaxBoundsSandboxed();
 
@@ -3889,7 +3889,7 @@
 
     private void recomputeNaturalConfigurationOfUnresizableActivity() {
         // Recompute the natural configuration of the non-resizable activity and the split screen.
-        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         // Draw letterbox.
         mActivity.setVisible(false);
@@ -4031,7 +4031,7 @@
         // orientation is not respected with insets as insets have been decoupled.
         final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
         final Rect displayBounds = display.getBounds();
-        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertNotNull(appBounds);
         assertEquals(displayBounds.width(), appBounds.width());
@@ -4063,7 +4063,7 @@
 
         final Rect bounds = activity.getBounds();
         // Activity should be letterboxed and should have portrait app bounds
-        assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertTrue(bounds.height() > bounds.width());
     }
@@ -4098,7 +4098,7 @@
         assertNotNull(activity.getAppCompatDisplayInsets());
         // Activity is not letterboxed for fixed orientation because orientation is respected
         // with insets, and should not be in size compat mode
-        assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(activity.inSizeCompatMode());
     }
@@ -4504,7 +4504,7 @@
                 .getUserMinAspectRatioOverrideCode();
 
         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
-        assertFalse(appCompatController.getAppCompatAspectRatioPolicy().isAspectRatioApplied());
+        assertFalse(appCompatController.getAspectRatioPolicy().isAspectRatioApplied());
     }
 
     @Test
@@ -4527,7 +4527,7 @@
                 .getUserMinAspectRatioOverrideCode();
 
         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ true);
-        assertTrue(appCompatController.getAppCompatAspectRatioPolicy().isAspectRatioApplied());
+        assertTrue(appCompatController.getAspectRatioPolicy().isAspectRatioApplied());
     }
 
     @Test
@@ -4540,8 +4540,7 @@
         setUpDisplaySizeWithApp(2500, 1600, taskBuilder);
         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
 
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
-                .isAspectRatioApplied());
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied());
     }
 
     @Test
@@ -4554,8 +4553,7 @@
         setUpDisplaySizeWithApp(2500, 1600, taskBuilder);
         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ true);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
-                .isAspectRatioApplied());
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied());
     }
 
     private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -4604,7 +4602,7 @@
         verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
@@ -4621,7 +4619,7 @@
         // ActivityRecord#resolveSizeCompatModeConfiguration because mCompatDisplayInsets aren't
         // null but activity doesn't enter size compat mode. Checking that areBoundsLetterboxed()
         // still returns true because of the aspect ratio restrictions.
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
@@ -4649,7 +4647,7 @@
 
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
@@ -4668,7 +4666,7 @@
 
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertTrue(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
@@ -4730,7 +4728,7 @@
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
 
         assertFalse(mActivity.isEligibleForLetterboxEducation());
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -4789,7 +4787,7 @@
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
         assertTrue(mActivity.isEligibleForLetterboxEducation());
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
@@ -4804,7 +4802,7 @@
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
         assertTrue(mActivity.isEligibleForLetterboxEducation());
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertTrue(mActivity.inSizeCompatMode());
     }
@@ -5007,7 +5005,7 @@
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOverrideOrientation());
         assertEquals(mActivity.getTask().getBounds(), mActivity.getBounds());
         final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivity.mAppCompatController
-                .getAppCompatAspectRatioPolicy();
+                .getAspectRatioPolicy();
         assertEquals(0, aspectRatioPolicy.getMaxAspectRatio(), 0 /* delta */);
         assertEquals(0, aspectRatioPolicy.getMinAspectRatio(), 0 /* delta */);
 
@@ -5090,7 +5088,7 @@
         assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
 
         // Activity should exit size compat with new density.
-        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
 
         assertFitted();
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
@@ -5274,7 +5272,7 @@
             activity.setRequestedOrientation(screenOrientation);
         }
         // Make sure to use the provided configuration to construct the size compat fields.
-        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+        activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
         activity.ensureActivityConfiguration();
         // Make sure the display configuration reflects the change of activity.
         if (activity.mDisplayContent.updateOrientation()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a12831e..a95093d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -43,6 +43,7 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.withSettings;
 
+import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
@@ -67,7 +68,6 @@
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
-import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
@@ -75,7 +75,6 @@
 import android.view.DisplayInfo;
 import android.view.InputChannel;
 import android.view.SurfaceControl;
-import android.window.ConfigurationChangeSetting;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.os.BackgroundThread;
@@ -96,11 +95,9 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.testutils.StubTransaction;
 import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.window.flags.Flags;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -144,13 +141,15 @@
     private ActivityTaskManagerService mAtmService;
     private WindowManagerService mWmService;
     private InputManagerService mImService;
-    private Runnable mOnBeforeServicesCreated;
+    @Nullable
+    private final Runnable mOnBeforeServicesCreated;
+
     /**
      * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
      */
     SurfaceControl.Transaction mTransaction;
 
-    public SystemServicesTestRule(Runnable onBeforeServicesCreated) {
+    public SystemServicesTestRule(@Nullable Runnable onBeforeServicesCreated) {
         mOnBeforeServicesCreated = onBeforeServicesCreated;
     }
 
@@ -398,15 +397,13 @@
     }
 
     private void setUpWindowManagerService() {
-        TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy();
-        TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider =
-                new TestDisplayWindowSettingsProvider();
-        // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
-        DisplayThread.getHandler().post(StrictMode::allowThreadDiskWritesMask);
-        mWmService = WindowManagerService.main(
-                mContext, mImService, false, wmPolicy, mAtmService,
-                testDisplayWindowSettingsProvider, StubTransaction::new,
-                MockSurfaceControlBuilder::new, mAppCompat);
+        // Use a spied Transaction class to prevent native code calls and verify interactions.
+        mTransaction = spy(StubTransaction.class);
+
+        mWmService = WindowManagerServiceTestSupport.setUpService(mContext, mImService,
+                new TestWindowManagerPolicy(), mAtmService, new TestDisplayWindowSettingsProvider(),
+                mTransaction, new MockSurfaceControlBuilder(), mAppCompat);
+
         spyOn(mWmService);
         spyOn(mWmService.mRoot);
         // Invoked during {@link ActivityStack} creation.
@@ -418,10 +415,6 @@
         spyOn(mWmService.mDisplayWindowSettings);
         spyOn(mWmService.mDisplayWindowSettingsProvider);
 
-        // Setup factory classes to prevent calls to native code.
-        mTransaction = spy(StubTransaction.class);
-        // Return a spied Transaction class than can be used to verify calls.
-        mWmService.mTransactionFactory = () -> mTransaction;
         mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
                 null, null, mTransaction, mWmService.mPowerManagerInternal);
 
@@ -488,12 +481,12 @@
     }
 
     private static void tearDownLocalServices() {
+        WindowManagerServiceTestSupport.tearDownService();
+
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
-        LocalServices.removeServiceForTest(WindowManagerInternal.class);
-        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
@@ -501,12 +494,7 @@
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(UserManagerInternal.class);
-        LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy.class);
         LocalServices.removeServiceForTest(GrammaticalInflectionManagerInternal.class);
-        if (Flags.condenseConfigurationChangeForSimpleMode()) {
-            LocalServices.removeServiceForTest(
-                    ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class);
-        }
     }
 
     Description getDescription() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 2997173..0d97724 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -759,13 +759,13 @@
         // Assert fixed orientation request is ignored for activity in ActivityEmbedding split.
         activity0.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        assertFalse(activity0.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity0.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
 
         activity1.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
-        assertFalse(activity1.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity1.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
 
@@ -773,9 +773,9 @@
         mDisplayContent.setIgnoreOrientationRequest(true);
         task.onConfigurationChanged(task.getParent().getConfiguration());
 
-        assertFalse(activity0.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity0.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
-        assertFalse(activity1.mAppCompatController.getAppCompatAspectRatioPolicy()
+        assertFalse(activity1.mAppCompatController.getAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index f1180ff..9cd302e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -354,7 +354,7 @@
                     ta.launchTransparentActivityInTask();
                     a.assertNotNullOnTopActivity(ActivityRecord::getAppCompatDisplayInsets);
                     a.applyToTopActivity((top) -> {
-                        top.mAppCompatController.getAppCompatSizeCompatModePolicy()
+                        top.mAppCompatController.getSizeCompatModePolicy()
                                 .clearSizeCompatMode();
                     });
                     a.assertNullOnTopActivity(ActivityRecord::getAppCompatDisplayInsets);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
new file mode 100644
index 0000000..a165d20
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.wm
+
+import android.content.Context
+import android.os.StrictMode
+import android.view.SurfaceControl
+import android.window.ConfigurationChangeSetting
+import com.android.server.DisplayThread
+import com.android.server.LocalServices
+import com.android.server.input.InputManagerService
+import com.android.server.policy.WindowManagerPolicy
+import com.android.window.flags.Flags
+
+/**
+ * Provides support for tests that require a [WindowManagerService].
+ *
+ * It provides functionalities for setting up and tearing down the service with proper dependencies,
+ * which can be used across different test modules.
+ */
+object WindowManagerServiceTestSupport {
+
+    /**
+     * Sets up and initializes a [WindowManagerService] instance with the provided dependencies.
+     *
+     * This method constructs a [WindowManagerService] using the provided dependencies for testing.
+     * It's marked as `internal` due to the package-private classes [DisplayWindowSettingsProvider]
+     * and [AppCompatConfiguration]. The `@JvmName` annotation is used to bypass name mangling and
+     * allow access from Java via `WindowManagerServiceTestSupport.setUpService`.
+     *
+     * **Important:** Before calling this method, ensure that any previous [WindowManagerService]
+     * instance and its related services are properly torn down. In your test's setup, it is
+     * recommended to call [tearDownService] before calling [setUpService] to handle cases where a
+     * previous test might have crashed and left services in an inconsistent state. This is crucial
+     * for test reliability.
+     *
+     * Example usage in a test's `setUp()` method:
+     * ```
+     * @Before
+     * fun setUp() {
+     *     WindowManagerServiceTestSupport.tearDownService() // Clean up before setup.
+     *     mWindowManagerService = WindowManagerServiceTestSupport.setUpService(...)
+     *     // ... rest of your setup logic ...
+     * }
+     * ```
+     *
+     * @param context the [Context] for the service.
+     * @param im the [InputManagerService] to use.
+     * @param policy the [WindowManagerPolicy] to use.
+     * @param atm the [ActivityTaskManagerService] to use.
+     * @param displayWindowSettingsProvider the [DisplayWindowSettingsProvider] to use.
+     * @param surfaceControlTransaction the [SurfaceControl.Transaction] instance to use.
+     * @param surfaceControlBuilder the [SurfaceControl.Builder] instance to use.
+     * @param appCompat the [AppCompatConfiguration] to use.
+     * @return the created [WindowManagerService] instance.
+     */
+    @JvmStatic
+    @JvmName("setUpService")
+    internal fun setUpService(
+        context: Context,
+        im: InputManagerService,
+        policy: WindowManagerPolicy,
+        atm: ActivityTaskManagerService,
+        displayWindowSettingsProvider: DisplayWindowSettingsProvider,
+        surfaceControlTransaction: SurfaceControl.Transaction,
+        surfaceControlBuilder: SurfaceControl.Builder,
+        appCompat: AppCompatConfiguration,
+    ): WindowManagerService {
+        // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
+        DisplayThread.getHandler().post { StrictMode.allowThreadDiskWritesMask() }
+
+        return WindowManagerService.main(
+            context,
+            im,
+            false, /* showBootMsgs */
+            policy,
+            atm,
+            displayWindowSettingsProvider,
+            { surfaceControlTransaction },
+            { surfaceControlBuilder },
+            appCompat,
+        )
+    }
+
+    /** Tears down the [WindowManagerService] and removes related local services. */
+    @JvmStatic
+    fun tearDownService() {
+        LocalServices.removeServiceForTest(WindowManagerPolicy::class.java)
+        LocalServices.removeServiceForTest(WindowManagerInternal::class.java)
+        LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy::class.java)
+
+        if (Flags.condenseConfigurationChangeForSimpleMode()) {
+            LocalServices.removeServiceForTest(
+                ConfigurationChangeSetting.ConfigurationChangeSettingInternal::class.java,
+            )
+        }
+    }
+}