Merge "Add idletimeout to AF exe pool" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index d4d4b24..dc7ccd4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8077,6 +8077,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@Nullable android.content.ComponentName);
     method @Deprecated public boolean getAutoTimeRequired();
     method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeZoneEnabled(@Nullable android.content.ComponentName);
+    method @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public int getAutoTimeZonePolicy();
     method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName);
     method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public boolean getCameraDisabled(@Nullable android.content.ComponentName);
@@ -8234,6 +8235,7 @@
     method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public void setAutoTimeEnabled(@Nullable android.content.ComponentName, boolean);
     method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean);
     method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZoneEnabled(@Nullable android.content.ComponentName, boolean);
+    method @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZonePolicy(int);
     method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public void setCameraDisabled(@Nullable android.content.ComponentName, boolean);
@@ -8352,6 +8354,9 @@
     field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+    field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_DISABLED = 1; // 0x1
+    field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_ENABLED = 2; // 0x2
+    field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
     field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_DISABLED = 1; // 0x1
     field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_ENABLED = 2; // 0x2
     field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
@@ -40702,7 +40707,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
     field public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 2; // 0x2
     field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
-    field public static final int FLAG_SUPPORTS_FILL_DIALOG = 64; // 0x40
+    field @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public static final int FLAG_SUPPORTS_FILL_DIALOG = 64; // 0x40
   }
 
   public final class FillResponse implements android.os.Parcelable {
@@ -56543,13 +56548,13 @@
     method public void notifyViewExited(@NonNull android.view.View, int);
     method public void notifyViewVisibilityChanged(@NonNull android.view.View, boolean);
     method public void notifyViewVisibilityChanged(@NonNull android.view.View, int, boolean);
-    method public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray<android.view.autofill.VirtualViewFillInfo>);
+    method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray<android.view.autofill.VirtualViewFillInfo>);
     method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     method public void requestAutofill(@NonNull android.view.View);
     method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
     method public void setUserData(@Nullable android.service.autofill.UserData);
-    method public boolean showAutofillDialog(@NonNull android.view.View);
-    method public boolean showAutofillDialog(@NonNull android.view.View, int);
+    method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public boolean showAutofillDialog(@NonNull android.view.View);
+    method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public boolean showAutofillDialog(@NonNull android.view.View, int);
     method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a152c22..62c8a34 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1368,6 +1368,7 @@
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @FlaggedApi("android.app.admin.flags.remove_managed_profile_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean removeManagedProfile();
     method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8245,6 +8246,26 @@
     method public void onSetMain(boolean);
   }
 
+  @FlaggedApi("android.media.tv.flags.tif_extension_standardization") public final class TvInputServiceExtensionManager {
+    method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public int registerExtensionIBinder(@NonNull String, @NonNull android.os.IBinder);
+    field public static final String IBROADCAST_TIME = "android.media.tv.extension.time.BroadcastTime";
+    field public static final String ICAM_APP_INFO_SERVICE = "android.media.tv.extension.cam.ICamAppInfoService";
+    field public static final String ICLIENT_TOKEN = "android.media.tv.extension.clienttoken.IClientToken";
+    field public static final String IDATA_SERVICE_SIGNAL_INFO = "android.media.tv.extension.teletext.IDataServiceSignalInfo";
+    field public static final String IEVENT_MONITOR = "android.media.tv.extension.event.IEventMonitor";
+    field public static final String IHDMI_SIGNAL_INTERFACE = "android.media.tv.extension.signal.IHdmiSignalInterface";
+    field public static final String IOAD_UPDATE_INTERFACE = "android.media.tv.extension.oad.IOadUpdateInterface";
+    field public static final String IRATING_INTERFACE = "android.media.tv.extension.rating.IRatingInterface";
+    field public static final String IRECORDED_CONTENTS = "android.media.tv.extension.pvr.IRecordedContents";
+    field public static final String ISCAN_INTERFACE = "android.media.tv.extension.scan.IScanInterface";
+    field public static final String ISCREEN_MODE_SETTINGS = "android.media.tv.extension.screenmode.IScreenModeSettings";
+    field public static final String ISERVICE_LIST_EDIT_LISTENER = "android.media.tv.extension.servicedb.IServiceListEditListener";
+    field public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2; // 0x2
+    field public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1; // 0x1
+    field public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3; // 0x3
+    field public static final int REGISTER_SUCCESS = 0; // 0x0
+  }
+
   public abstract static class TvRecordingClient.RecordingCallback {
     method public void onEvent(String, String, android.os.Bundle);
   }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b447897..1b707f7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1017,6 +1017,12 @@
     public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 1 << 6;
 
     /**
+     * @hide
+     * Process is guaranteed cpu time (IE. it will not be frozen).
+     */
+    public static final int PROCESS_CAPABILITY_CPU_TIME = 1 << 7;
+
+    /**
      * @hide all capabilities, the ORing of all flags in {@link ProcessCapability}.
      *
      * Don't expose it as TestApi -- we may add new capabilities any time, which could
@@ -1028,7 +1034,8 @@
             | PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
             | PROCESS_CAPABILITY_BFSL
             | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK
-            | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
+            | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL
+            | PROCESS_CAPABILITY_CPU_TIME;
 
     /**
      * All implicit capabilities. This capability set is currently only used for processes under
@@ -1053,6 +1060,7 @@
         pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-');
         pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-');
         pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-');
+        pw.print((caps & PROCESS_CAPABILITY_CPU_TIME) != 0 ? 'T' : '-');
     }
 
     /** @hide */
@@ -1065,6 +1073,7 @@
         sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-');
         sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-');
         sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-');
+        sb.append((caps & PROCESS_CAPABILITY_CPU_TIME) != 0 ? 'T' : '-');
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 102540c..6939bb6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -55,6 +55,7 @@
 import static android.Manifest.permission.SET_TIME_ZONE;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
 import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_REMOVE_MANAGED_PROFILE_ENABLED;
 import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
 import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
 import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
@@ -8968,12 +8969,9 @@
     /**
      * Called by a device owner, a profile owner for the primary user or a profile
      * owner of an organization-owned managed profile to turn auto time zone on and off.
-     * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
-     * to prevent the user from changing this setting.
      * <p>
-     * If user restriction {@link UserManager#DISALLOW_CONFIG_DATE_TIME} is used,
-     * no user will be able set the date and time zone. Instead, the network date
-     * and time zone will be used.
+     * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} to prevent the
+     * user from changing this setting, that way no user will be able set the date and time zone.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with or Null if the
      *              caller is not a device admin.
@@ -8981,13 +8979,17 @@
      * @throws SecurityException if caller is not a device owner, a profile owner for the
      * primary user, or a profile owner of an organization-owned managed profile.
      */
-    @SupportsCoexistence
     @RequiresPermission(value = SET_TIME_ZONE, conditional = true)
     public void setAutoTimeZoneEnabled(@Nullable ComponentName admin, boolean enabled) {
         throwIfParentInstance("setAutoTimeZone");
         if (mService != null) {
             try {
-                mService.setAutoTimeZoneEnabled(admin, mContext.getPackageName(), enabled);
+                if (Flags.setAutoTimeZoneEnabledCoexistence()) {
+                    mService.setAutoTimeZonePolicy(mContext.getPackageName(),
+                            enabled ? AUTO_TIME_ZONE_ENABLED : AUTO_TIME_ZONE_DISABLED );
+                } else {
+                    mService.setAutoTimeZoneEnabled(admin, mContext.getPackageName(), enabled);
+                }
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -9017,6 +9019,96 @@
     }
 
     /**
+     * Specifies that the auto time zone state is not controlled by device policy.
+     *
+     * @see #setAutoTimeZonePolicy(int)
+     */
+    @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+    public static final int AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY = 0;
+
+    /**
+     * Specifies the "disabled" auto time zone state.
+     *
+     * @see #setAutoTimeZonePolicy(int)
+     */
+    @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+    public static final int AUTO_TIME_ZONE_DISABLED = 1;
+
+    /**
+     * Specifies the "enabled" auto time zone state.
+     *
+     * @see #setAutoTimeZonePolicy(int)
+     */
+    @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+    public static final int AUTO_TIME_ZONE_ENABLED = 2;
+
+    /**
+     * Flags supplied to {@link #setAutoTimeZonePolicy}(int)}.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "AUTO_TIME_ZONE_" }, value = {
+            AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY,
+            AUTO_TIME_ZONE_DISABLED,
+            AUTO_TIME_ZONE_ENABLED
+    })
+    public @interface AutoTimeZonePolicy {}
+
+    /**
+     * Called by a device owner, a profile owner for the primary user or a profile owner of an
+     * organization-owned managed profile to turn auto time zone on and off.
+     * <p>
+     * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} to prevent the
+     * user from changing this setting, that way no user will be able set the date and time zone.
+     *
+     * @param policy The desired state among {@link #AUTO_TIME_ZONE_ENABLED} to enable it,
+     * {@link #AUTO_TIME_ZONE_DISABLED} to disable it or
+     * {@link #AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY} to unset the policy.
+     * @throws SecurityException if caller is not a device owner, a profile owner for the primary
+     * user, or a profile owner of an organization-owned managed profile, or if the caller does not
+     * hold the required permission.
+     */
+    @SupportsCoexistence
+    @RequiresPermission(value = SET_TIME_ZONE, conditional = true)
+    @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+    public void setAutoTimeZonePolicy(@AutoTimeZonePolicy int policy) {
+        throwIfParentInstance("setAutoTimeZonePolicy");
+        if (mService != null) {
+            try {
+                mService.setAutoTimeZonePolicy(mContext.getPackageName(), policy);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns auto time zone policy's current state.
+     *
+     * @return One of {@link #AUTO_TIME_ZONE_ENABLED} if enabled, {@link #AUTO_TIME_ZONE_DISABLED}
+     *         if disabled and {@link #AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY} if the state is not
+     *         controlled by policy.
+     * @throws SecurityException if caller is not a device owner, a profile owner for the
+     * primary user, or a profile owner of an organization-owned managed profile, or if the caller
+     * does not hold the required permission.
+     */
+    @SupportsCoexistence
+    @RequiresPermission(anyOf = {SET_TIME_ZONE, QUERY_ADMIN_POLICY}, conditional = true)
+    @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+    public @AutoTimeZonePolicy int getAutoTimeZonePolicy() {
+        throwIfParentInstance("getAutoTimeZonePolicy");
+        if (mService != null) {
+            try {
+                return mService.getAutoTimeZonePolicy(mContext.getPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY;
+    }
+
+    /**
      * TODO (b/137101239): remove this method in follow-up CL
      * since it's only used for split system user.
      * Called by a device owner to set whether all users created on the device should be ephemeral.
@@ -16962,6 +17054,30 @@
     }
 
     /**
+     * Removes a manged profile from the device only when called from a managed profile's context
+     *
+     * @param user UserHandle of the profile to be removed
+     * @return {@code true} when removal of managed profile was successful, {@code false} when
+     * removal was unsuccessful or throws IllegalArgumentException when provided user was not a
+     * managed profile
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware
+    @FlaggedApi(FLAG_REMOVE_MANAGED_PROFILE_ENABLED)
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public boolean removeManagedProfile() {
+        if (mService == null) {
+            throw new IllegalStateException("Could not find DevicePolicyManagerService");
+        }
+        try {
+            return mService.removeManagedProfile(myUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called when a managed profile has been provisioned.
      *
      * @throws SecurityException if the caller does not hold
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a4e2b8f..0b8f538 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -378,6 +378,9 @@
     void setAutoTimeZoneEnabled(in ComponentName who, String callerPackageName, boolean enabled);
     boolean getAutoTimeZoneEnabled(in ComponentName who, String callerPackageName);
 
+    void setAutoTimeZonePolicy(String callerPackageName, int policy);
+    int getAutoTimeZonePolicy(String callerPackageName);
+
     void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers);
     boolean getForceEphemeralUsers(in ComponentName who);
 
@@ -567,6 +570,8 @@
 
     void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount);
 
+    boolean removeManagedProfile(int userId);
+
     void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType);
     int getDeviceOwnerType(in ComponentName admin);
 
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index be24bfa..5f868be 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -343,13 +343,20 @@
 }
 
 flag {
-  name: "user_provisioning_same_state"
-  namespace: "enterprise"
-  description: "Handle exceptions while setting same provisioning state."
-  bug: "326441417"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
+    name: "user_provisioning_same_state"
+    namespace: "enterprise"
+    description: "Handle exceptions while setting same provisioning state."
+    bug: "326441417"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "remove_managed_profile_enabled"
+    namespace: "enterprise"
+    description: "API that removes a given managed profile."
+    bug: "372652841"
 }
 
 flag {
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index 8ffda72..4a142bb 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -113,7 +113,7 @@
     /** @return the description for this wallpaper */
     @NonNull
     public List<CharSequence> getDescription() {
-        return new ArrayList<>();
+        return mDescription;
     }
 
     /** @return the {@link Uri} for the action associated with the wallpaper, or {@code null} if not
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3d89ce1..813208d 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -330,6 +330,17 @@
   is_fixed_read_only: true
 }
 
+flag {
+    name: "cache_user_info_read_only"
+    namespace: "multiuser"
+    description: "Cache UserInfo to avoid unnecessary binder calls"
+    bug: "161915546"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
 # This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
 flag {
     name: "enable_private_space_features"
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index ca20801..be4629a 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -16,6 +16,9 @@
 
 package android.service.autofill;
 
+import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -100,7 +103,12 @@
     /**
      * Indicates the request supports fill dialog presentation for the fields, the
      * system will send the request when the activity just started.
+     *
+     * @deprecated All requests would support fill dialog by default.
+     * Presence of this flag isn't needed.
      */
+    @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+    @Deprecated
     public static final @RequestFlags int FLAG_SUPPORTS_FILL_DIALOG = 0x40;
 
     /**
@@ -588,10 +596,10 @@
     };
 
     @DataClass.Generated(
-            time = 1701010178309L,
+            time = 1730991738865L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
-            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.annotation.FlaggedApi @java.lang.Deprecated @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 9714896..2d2f79d 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -23,6 +23,7 @@
 import android.annotation.UiThread;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.Flags;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.inputmethod.InputMethodDebug;
@@ -150,6 +151,17 @@
         if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
             return InputMethodManager.DISPATCH_NOT_HANDLED;
         }
+        if (Flags.refactorInsetsController() && event instanceof KeyEvent keyEvent
+                && keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            final var insetsController = mViewRootImpl.getInsetsController();
+            if (insetsController.getAnimationType(WindowInsets.Type.ime())
+                    == InsetsController.ANIMATION_TYPE_HIDE
+                    || insetsController.isPredictiveBackImeHideAnimInProgress()) {
+                // if there is an ongoing hide animation, the back event should not be dispatched
+                // to the IME.
+                return InputMethodManager.DISPATCH_NOT_HANDLED;
+            }
+        }
         final InputMethodManager imm =
                 mViewRootImpl.mContext.getSystemService(InputMethodManager.class);
         if (imm == null) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 26ca813..b0813f3 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1910,7 +1910,8 @@
         mImeSourceConsumer.onWindowFocusLost();
     }
 
-    @VisibleForTesting
+    /** Returns the current {@link AnimationType} of an {@link InsetsType}. */
+    @VisibleForTesting(visibility = PACKAGE)
     public @AnimationType int getAnimationType(@InsetsType int type) {
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
             InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1a45939..52c5af8 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -25,12 +25,14 @@
 import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
 import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
+import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
 import static android.view.ContentInfo.SOURCE_AUTOFILL;
 import static android.view.autofill.Helper.sDebug;
 import static android.view.autofill.Helper.sVerbose;
 import static android.view.autofill.Helper.toList;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1607,7 +1609,12 @@
      *             the virtual view in the host view.
      *
      * @throws IllegalArgumentException if the {@code infos} was empty
+     *
+     * @deprecated This function will not do anything. Showing fill dialog is now fully controlled
+     * by the framework and the autofill provider.
      */
+    @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+    @Deprecated
     public void notifyVirtualViewsReady(
             @NonNull View view, @NonNull SparseArray<VirtualViewFillInfo> infos) {
         Objects.requireNonNull(infos);
@@ -4034,8 +4041,13 @@
      *             receiving a focus event. The autofill suggestions shown will include content for
      *             related views as well.
      * @return {@code true} if the autofill dialog is being shown
+     *
+     * @deprecated This function will not do anything. Showing fill dialog is now fully controlled
+     * by the framework and the autofill provider.
      */
     // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
+    @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+    @Deprecated
     public boolean showAutofillDialog(@NonNull View view) {
         Objects.requireNonNull(view);
         if (shouldShowAutofillDialog(view, view.getAutofillId())) {
@@ -4073,7 +4085,12 @@
      *            suggestions.
      * @param virtualId id identifying the virtual view inside the host view.
      * @return {@code true} if the autofill dialog is being shown
+     *
+     * @deprecated This function will not do anything. Showing fill dialog is now fully controlled
+     * by the framework and the autofill provider.
      */
+    @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+    @Deprecated
     public boolean showAutofillDialog(@NonNull View view, int virtualId) {
         Objects.requireNonNull(view);
         if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
diff --git a/core/res/res/values/stoppable_fgs_system_apps.xml b/core/res/res/values/stoppable_fgs_system_apps.xml
index 165ff61..06843f4 100644
--- a/core/res/res/values/stoppable_fgs_system_apps.xml
+++ b/core/res/res/values/stoppable_fgs_system_apps.xml
@@ -19,6 +19,7 @@
 <resources>
     <!-- A list of system apps whose FGS can be stopped in the task manager. -->
     <string-array translatable="false" name="stoppable_fgs_system_apps">
+        <item>com.android.virtualization.terminal</item>
     </string-array>
     <!-- stoppable_fgs_system_apps which is supposed to be overridden by vendor -->
     <string-array translatable="false" name="vendor_stoppable_fgs_system_apps">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 0fd98ed..39dc267 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1295,6 +1295,14 @@
             // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded
             // so re-expand to whatever is selected.
             showExpandedViewForBubbleBar();
+            if (bubbleKey.equals(selectedBubbleKey)) {
+                // We dragged the selected bubble to dismiss, log switch event
+                if (mBubbleData.getSelectedBubble() instanceof Bubble) {
+                    // Log only bubbles as overflow can't be dragged
+                    mLogger.log((Bubble) mBubbleData.getSelectedBubble(),
+                            BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+                }
+            }
         }
     }
 
@@ -1337,10 +1345,16 @@
     public void expandStackAndSelectBubbleFromLauncher(String key, int topOnScreen) {
         mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen);
 
+        boolean wasExpanded = (mLayerView != null && mLayerView.isExpanded());
+
         if (BubbleOverflow.KEY.equals(key)) {
             mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
             mLayerView.showExpandedView(mBubbleData.getOverflow());
-            mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+            if (wasExpanded) {
+                mLogger.log(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+            } else {
+                mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+            }
             return;
         }
 
@@ -1352,7 +1366,11 @@
             // already in the stack
             mBubbleData.setSelectedBubbleFromLauncher(b);
             mLayerView.showExpandedView(b);
-            mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+            if (wasExpanded) {
+                mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+            } else {
+                mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+            }
         } else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) {
             // TODO: (b/271468319) handle overflow
         } else {
@@ -2081,6 +2099,9 @@
             // Only need to update the layer view if we're currently expanded for selection changes.
             if (mLayerView != null && mLayerView.isExpanded()) {
                 mLayerView.showExpandedView(selectedBubble);
+                if (selectedBubble instanceof Bubble bubble) {
+                    mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+                }
             }
         }
     };
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index bc09183..e901c39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -322,7 +322,7 @@
             mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
             mPipBoundsState.setBounds(toBounds);
         }
-        t.setBounds(mPipTransitionState.mPipTaskToken, mPipBoundsState.getBounds());
+        t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
     }
 
     private void setDisplayLayout(DisplayLayout layout) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 607de0e..5438a01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -25,10 +25,13 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsState;
@@ -48,11 +51,13 @@
     private final ShellExecutor mMainExecutor;
     private final PipTransitionState mPipTransitionState;
     private PipTransitionController mPipTransitionController;
-    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+    private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
 
     @Nullable private Runnable mUpdateMovementBoundsRunnable;
 
+    private PipAlphaAnimatorSupplier mPipAlphaAnimatorSupplier;
+
     public PipScheduler(Context context,
             PipBoundsState pipBoundsState,
             ShellExecutor mainExecutor,
@@ -64,10 +69,7 @@
 
         mSurfaceControlTransactionFactory =
                 new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
-    }
-
-    ShellExecutor getMainExecutor() {
-        return mMainExecutor;
+        mPipAlphaAnimatorSupplier = PipAlphaAnimator::new;
     }
 
     void setPipTransitionController(PipTransitionController pipTransitionController) {
@@ -76,27 +78,29 @@
 
     @Nullable
     private WindowContainerTransaction getExitPipViaExpandTransaction() {
-        if (mPipTransitionState.mPipTaskToken == null) {
+        WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+        if (pipTaskToken == null) {
             return null;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         // final expanded bounds to be inherited from the parent
-        wct.setBounds(mPipTransitionState.mPipTaskToken, null);
+        wct.setBounds(pipTaskToken, null);
         // if we are hitting a multi-activity case
         // windowing mode change will reparent to original host task
-        wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
+        wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
         return wct;
     }
 
     @Nullable
     private WindowContainerTransaction getRemovePipTransaction() {
-        if (mPipTransitionState.mPipTaskToken == null) {
+        WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+        if (pipTaskToken == null) {
             return null;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setBounds(mPipTransitionState.mPipTaskToken, null);
-        wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
-        wct.reorder(mPipTransitionState.mPipTaskToken, false);
+        wct.setBounds(pipTaskToken, null);
+        wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
+        wct.reorder(pipTaskToken, false);
         return wct;
     }
 
@@ -117,7 +121,7 @@
     /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */
     public void removePipAfterAnimation() {
         SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
-        PipAlphaAnimator animator = new PipAlphaAnimator(mContext,
+        PipAlphaAnimator animator = mPipAlphaAnimatorSupplier.get(mContext,
                 mPipTransitionState.getPinnedTaskLeash(), tx, PipAlphaAnimator.FADE_OUT);
         animator.setAnimationEndCallback(this::scheduleRemovePipImmediately);
         animator.start();
@@ -159,13 +163,14 @@
      *                    for running the animator will get this as an extra.
      */
     public void scheduleAnimateResizePip(Rect toBounds, boolean configAtEnd, int duration) {
-        if (mPipTransitionState.mPipTaskToken == null || !mPipTransitionState.isInPip()) {
+        WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+        if (pipTaskToken == null || !mPipTransitionState.isInPip()) {
             return;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+        wct.setBounds(pipTaskToken, toBounds);
         if (configAtEnd) {
-            wct.deferConfigToTransitionEnd(mPipTransitionState.mPipTaskToken);
+            wct.deferConfigToTransitionEnd(pipTaskToken);
         }
         mPipTransitionController.startResizeTransition(wct, duration);
     }
@@ -204,7 +209,7 @@
             return;
         }
         SurfaceControl leash = mPipTransitionState.getPinnedTaskLeash();
-        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
 
         Matrix transformTensor = new Matrix();
         final float[] mMatrixTmp = new float[9];
@@ -218,7 +223,7 @@
         tx.apply();
     }
 
-    void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
+    void setUpdateMovementBoundsRunnable(@Nullable Runnable updateMovementBoundsRunnable) {
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
     }
 
@@ -235,4 +240,23 @@
         mPipBoundsState.setBounds(newBounds);
         maybeUpdateMovementBounds();
     }
+
+    @VisibleForTesting
+    void setSurfaceControlTransactionFactory(
+            @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+        mSurfaceControlTransactionFactory = factory;
+    }
+
+    @VisibleForTesting
+    interface PipAlphaAnimatorSupplier {
+        PipAlphaAnimator get(@NonNull Context context,
+                SurfaceControl leash,
+                SurfaceControl.Transaction tx,
+                @PipAlphaAnimator.Fade int direction);
+    }
+
+    @VisibleForTesting
+    void setPipAlphaAnimatorSupplier(@NonNull PipAlphaAnimatorSupplier supplier) {
+        mPipAlphaAnimatorSupplier = supplier;
+    }
 }
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 3caad09..02f5955 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
@@ -543,7 +543,7 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        WindowContainerToken pipToken = mPipTransitionState.mPipTaskToken;
+        WindowContainerToken pipToken = mPipTransitionState.getPipTaskToken();
 
         TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
         if (pipChange == null) {
@@ -773,11 +773,11 @@
     }
 
     private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
-        if (mPipTransitionState.mPipTaskToken == null) {
+        if (mPipTransitionState.getPipTaskToken() == null) {
             // PiP removal makes sense if enter-PiP has cached a valid pinned task token.
             return false;
         }
-        TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.mPipTaskToken);
+        TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.getPipTaskToken());
         if (pipChange == null) {
             // Search for the PiP change by token since the windowing mode might be FULLSCREEN now.
             return false;
@@ -859,18 +859,18 @@
                 Preconditions.checkState(extra != null,
                         "No extra bundle for " + mPipTransitionState);
 
-                mPipTransitionState.mPipTaskToken = extra.getParcelable(
-                        PIP_TASK_TOKEN, WindowContainerToken.class);
+                mPipTransitionState.setPipTaskToken(extra.getParcelable(
+                        PIP_TASK_TOKEN, WindowContainerToken.class));
                 mPipTransitionState.setPinnedTaskLeash(extra.getParcelable(
                         PIP_TASK_LEASH, SurfaceControl.class));
-                boolean hasValidTokenAndLeash = mPipTransitionState.mPipTaskToken != null
+                boolean hasValidTokenAndLeash = mPipTransitionState.getPipTaskToken() != null
                         && mPipTransitionState.getPinnedTaskLeash() != null;
 
                 Preconditions.checkState(hasValidTokenAndLeash,
                         "Unexpected bundle for " + mPipTransitionState);
                 break;
             case PipTransitionState.EXITED_PIP:
-                mPipTransitionState.mPipTaskToken = null;
+                mPipTransitionState.setPipTaskToken(null);
                 mPipTransitionState.setPinnedTaskLeash(null);
                 break;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 03e06f9..8e90bfe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -138,7 +138,7 @@
 
     // pinned PiP task's WC token
     @Nullable
-    WindowContainerToken mPipTaskToken;
+    private WindowContainerToken mPipTaskToken;
 
     // pinned PiP task's leash
     @Nullable
@@ -304,6 +304,14 @@
         mSwipePipToHomeAppBounds.setEmpty();
     }
 
+    @Nullable WindowContainerToken getPipTaskToken() {
+        return mPipTaskToken;
+    }
+
+    public void setPipTaskToken(@Nullable WindowContainerToken token) {
+        mPipTaskToken = token;
+    }
+
     @Nullable SurfaceControl getPinnedTaskLeash() {
         return mPinnedTaskLeash;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index f739d65..49cf8ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -63,6 +63,8 @@
             "Bubbles"),
     WM_SHELL_COMPAT_UI(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM_COMPAT_UI),
+    WM_SHELL_APP_COMPAT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM_APP_COMPAT),
     TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
 
     private final boolean mEnabled;
@@ -131,6 +133,7 @@
         private static final String TAG_WM_SPLIT_SCREEN = "ShellSplitScreen";
         private static final String TAG_WM_DESKTOP_MODE = "ShellDesktopMode";
         private static final String TAG_WM_COMPAT_UI = "CompatUi";
+        private static final String TAG_WM_APP_COMPAT = "AppCompat";
 
         private static final boolean ENABLE_DEBUG = true;
         private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
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 9016c45..417a655 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
@@ -1286,6 +1286,16 @@
             if (mFinishCB == null
                     || (Flags.enableShellTopTaskTracking() && mPendingFinishTransition != null)) {
                 Slog.e(TAG, "Duplicate call to finish");
+                if (runnerFinishCb != null) {
+                    try {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "[%d] RecentsController.finishInner: calling finish callback",
+                                mInstanceId);
+                        runnerFinishCb.send(0, null);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Failed to report transition finished", e);
+                    }
+                }
                 return;
             }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
new file mode 100644
index 0000000..cab6252
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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.wm.shell.pip2.phone;
+
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.MatchersKt.eq;
+import static org.mockito.kotlin.VerificationKt.times;
+import static org.mockito.kotlin.VerificationKt.verify;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test against {@link PipScheduler}
+ */
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipSchedulerTest {
+    private static final int TEST_RESIZE_DURATION = 1;
+    private static final Rect TEST_STARTING_BOUNDS = new Rect(0, 0, 10, 10);
+    private static final Rect TEST_BOUNDS = new Rect(0, 0, 20, 20);
+
+    @Mock private Context mMockContext;
+    @Mock private Resources mMockResources;
+    @Mock private PipBoundsState mMockPipBoundsState;
+    @Mock private ShellExecutor mMockMainExecutor;
+    @Mock private PipTransitionState mMockPipTransitionState;
+    @Mock private PipTransitionController mMockPipTransitionController;
+    @Mock private Runnable mMockUpdateMovementBoundsRunnable;
+    @Mock private WindowContainerToken mMockPipTaskToken;
+    @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+    @Mock private SurfaceControl.Transaction mMockTransaction;
+    @Mock private PipAlphaAnimator mMockAlphaAnimator;
+
+    @Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
+    @Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor;
+
+    private PipScheduler mPipScheduler;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockResources.getInteger(anyInt())).thenReturn(0);
+        when(mMockPipBoundsState.getBounds()).thenReturn(TEST_STARTING_BOUNDS);
+        when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
+        when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+                .thenReturn(mMockTransaction);
+
+        mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
+                mMockPipTransitionState);
+        mPipScheduler.setPipTransitionController(mMockPipTransitionController);
+        mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
+        mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) ->
+                mMockAlphaAnimator);
+
+        SurfaceControl testLeash = new SurfaceControl.Builder()
+                .setContainerLayer()
+                .setName("PipSchedulerTest")
+                .setCallsite("PipSchedulerTest")
+                .build();
+        when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(testLeash);
+    }
+
+    @Test
+    public void scheduleExitPipViaExpand_nullTaskToken_noop() {
+        setNullPipTaskToken();
+
+        mPipScheduler.scheduleExitPipViaExpand();
+
+        verify(mMockMainExecutor, never()).execute(any());
+    }
+
+    @Test
+    public void scheduleExitPipViaExpand_exitTransitionCalled() {
+        setMockPipTaskToken();
+
+        mPipScheduler.scheduleExitPipViaExpand();
+
+        verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
+        assertNotNull(mRunnableArgumentCaptor.getValue());
+        mRunnableArgumentCaptor.getValue().run();
+
+        verify(mMockPipTransitionController, times(1))
+                .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull());
+    }
+
+    @Test
+    public void removePipAfterAnimation() {
+        //TODO: Update once this is changed to run animation as part of transition
+        setMockPipTaskToken();
+
+        mPipScheduler.removePipAfterAnimation();
+        verify(mMockAlphaAnimator, times(1))
+                .setAnimationEndCallback(mRunnableArgumentCaptor.capture());
+        assertNotNull(mRunnableArgumentCaptor.getValue());
+        verify(mMockAlphaAnimator, times(1)).start();
+
+        mRunnableArgumentCaptor.getValue().run();
+
+        verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
+        assertNotNull(mRunnableArgumentCaptor.getValue());
+
+        mRunnableArgumentCaptor.getValue().run();
+
+        verify(mMockPipTransitionController, times(1))
+                .startExitTransition(eq(TRANSIT_REMOVE_PIP), any(), isNull());
+    }
+
+    @Test
+    public void scheduleAnimateResizePip_bounds_nullTaskToken_noop() {
+        setNullPipTaskToken();
+
+        mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS);
+
+        verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+    }
+
+    @Test
+    public void scheduleAnimateResizePip_boundsConfig_nullTaskToken_noop() {
+        setNullPipTaskToken();
+
+        mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true);
+
+        verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+    }
+
+    @Test
+    public void scheduleAnimateResizePip_boundsConfig_setsConfigAtEnd() {
+        setMockPipTaskToken();
+        when(mMockPipTransitionState.isInPip()).thenReturn(true);
+
+        mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true);
+
+        verify(mMockPipTransitionController, times(1))
+                .startResizeTransition(mWctArgumentCaptor.capture(), anyInt());
+        assertNotNull(mWctArgumentCaptor.getValue());
+        assertNotNull(mWctArgumentCaptor.getValue().getChanges());
+        boolean hasConfigAtEndChange = false;
+        for (WindowContainerTransaction.Change change :
+                mWctArgumentCaptor.getValue().getChanges().values()) {
+            if (change.getConfigAtTransitionEnd()) {
+                hasConfigAtEndChange = true;
+                break;
+            }
+        }
+        assertTrue(hasConfigAtEndChange);
+    }
+
+    @Test
+    public void scheduleAnimateResizePip_boundsConfigDuration_nullTaskToken_noop() {
+        setNullPipTaskToken();
+
+        mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
+
+        verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+    }
+
+    @Test
+    public void scheduleAnimateResizePip_notInPip_noop() {
+        setMockPipTaskToken();
+        when(mMockPipTransitionState.isInPip()).thenReturn(false);
+
+        mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
+
+        verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+    }
+
+    @Test
+    public void scheduleAnimateResizePip_resizeTransition() {
+        setMockPipTaskToken();
+        when(mMockPipTransitionState.isInPip()).thenReturn(true);
+
+        mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
+
+        verify(mMockPipTransitionController, times(1))
+                .startResizeTransition(any(), eq(TEST_RESIZE_DURATION));
+    }
+
+    @Test
+    public void scheduleUserResizePip_emptyBounds_noop() {
+        setMockPipTaskToken();
+
+        mPipScheduler.scheduleUserResizePip(new Rect());
+
+        verify(mMockTransaction, never()).apply();
+    }
+
+    @Test
+    public void scheduleUserResizePip_rotation_emptyBounds_noop() {
+        setMockPipTaskToken();
+
+        mPipScheduler.scheduleUserResizePip(new Rect(), 90);
+
+        verify(mMockTransaction, never()).apply();
+    }
+
+    @Test
+    public void scheduleUserResizePip_applyTransaction() {
+        setMockPipTaskToken();
+
+        mPipScheduler.scheduleUserResizePip(TEST_BOUNDS, 90);
+
+        verify(mMockTransaction, times(1)).apply();
+    }
+
+    @Test
+    public void finishResize_movementBoundsRunnableCalled() {
+        mPipScheduler.setUpdateMovementBoundsRunnable(mMockUpdateMovementBoundsRunnable);
+        mPipScheduler.scheduleFinishResizePip(TEST_BOUNDS);
+
+        verify(mMockUpdateMovementBoundsRunnable, times(1)).run();
+    }
+
+    private void setNullPipTaskToken() {
+        when(mMockPipTransitionState.getPipTaskToken()).thenReturn(null);
+    }
+
+    private void setMockPipTaskToken() {
+        when(mMockPipTransitionState.getPipTaskToken()).thenReturn(mMockPipTaskToken);
+    }
+}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 472d798..f07ef87 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -33,6 +33,7 @@
      */
     public interface BaseParameters {
         String PARAMETER_ID = "_id";
+        String PARAMETER_TYPE = "_type";
         String PARAMETER_NAME = "_name";
         String PARAMETER_PACKAGE = "_package";
         String PARAMETER_INPUT_ID = "_input_id";
@@ -43,7 +44,7 @@
      * Parameters picture quality.
      * @hide
      */
-    public static final class PictureQuality {
+    public static final class PictureQuality implements BaseParameters {
         /**
          * The brightness.
          *
diff --git a/media/java/android/media/tv/TvInputServiceExtensionManager.java b/media/java/android/media/tv/TvInputServiceExtensionManager.java
index c514f6e..9442726 100644
--- a/media/java/android/media/tv/TvInputServiceExtensionManager.java
+++ b/media/java/android/media/tv/TvInputServiceExtensionManager.java
@@ -20,7 +20,9 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.media.tv.flags.Flags;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -43,6 +45,7 @@
  *
  * @hide
  */
+@SystemApi
 @FlaggedApi(Flags.FLAG_TIF_EXTENSION_STANDARDIZATION)
 public final class TvInputServiceExtensionManager {
     private static final String TAG = "TvInputServiceExtensionManager";
@@ -186,8 +189,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StandardizedExtensionName {}
     /**
-     * Interface responsible for creating scan session and obtain parameters.
-     * @hide
+     * Interface responsible for creating scan session and obtaining related parameters.
      */
     public static final String ISCAN_INTERFACE = SCAN_PACKAGE + "IScanInterface";
     /**
@@ -286,12 +288,10 @@
     public static final String ISCAN_SAT_SEARCH = SCAN_PACKAGE + "IScanSatSearch";
     /**
      * Interface for Over-the-Air Download.
-     * @hide
      */
     public static final String IOAD_UPDATE_INTERFACE = OAD_PACKAGE + "IOadUpdateInterface";
     /**
      * Interface for handling conditional access module app related information.
-     * @hide
      */
     public static final String ICAM_APP_INFO_SERVICE = CAM_PACKAGE + "ICamAppInfoService";
     /**
@@ -406,8 +406,7 @@
     public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = RATING_PACKAGE
             + "IDownloadableRatingTableMonitor";
     /**
-     * Interface for handling RRT rating related information.
-     * @hide
+     * Interface for handling Region Rating Table rating system related information.
      */
     public static final String IRATING_INTERFACE = RATING_PACKAGE + "IRatingInterface";
     /**
@@ -442,12 +441,10 @@
     public static final String IPROGRAM_INFO_LISTENER = RATING_PACKAGE + "IProgramInfoListener";
     /**
      * Interface for getting broadcast time related information.
-     * @hide
      */
     public static final String IBROADCAST_TIME = TIME_PACKAGE + "BroadcastTime";
     /**
      * Interface for handling data service signal information on teletext.
-     * @hide
      */
     public static final String IDATA_SERVICE_SIGNAL_INFO = TELETEXT_PACKAGE
             + "IDataServiceSignalInfo";
@@ -476,17 +473,14 @@
             + "IScanBackgroundServiceUpdateListener";
     /**
      * Interface for generating client token.
-     * @hide
      */
     public static final String ICLIENT_TOKEN = CLIENT_TOKEN_PACKAGE + "IClientToken";
     /**
      * Interfaces for handling screen mode information.
-     * @hide
      */
     public static final String ISCREEN_MODE_SETTINGS = SCREEN_MODE_PACKAGE + "IScreenModeSettings";
     /**
      * Interfaces for handling HDMI signal information update.
-     * @hide
      */
     public static final String IHDMI_SIGNAL_INTERFACE = SIGNAL_PACKAGE + "IHdmiSignalInterface";
     /**
@@ -529,7 +523,6 @@
     public static final String ISERVICE_LIST_EDIT = SERVICE_DATABASE_PACKAGE + "IServiceListEdit";
     /**
      * Interfaces for changes on service database updates.
-     * @hide
      */
     public static final String ISERVICE_LIST_EDIT_LISTENER = SERVICE_DATABASE_PACKAGE
             + "IServiceListEditListener";
@@ -587,8 +580,7 @@
     public static final String ICHANNEL_LIST_TRANSFER = SERVICE_DATABASE_PACKAGE
             + "IChannelListTransfer";
     /**
-     * Interfaces for record contents updates.
-     * @hide
+     * Interface for operations related to recorded contents.
      */
     public static final String IRECORDED_CONTENTS = PVR_PACKAGE + "IRecordedContents";
     /**
@@ -605,7 +597,6 @@
             + "IGetInfoRecordedContentsCallback";
     /**
      * Interfaces for monitoring present event information.
-     * @hide
      */
     public static final String IEVENT_MONITOR = EVENT_PACKAGE + "IEventMonitor";
     /**
@@ -784,20 +775,22 @@
     }
 
     /**
-     * This function should be used by OEM to register IBinder objects that implement
-     * standardized AIDL interfaces.
+     * Registers IBinder objects that implement standardized AIDL interfaces.
+     * <p>This function should be used by SoCs/OEMs
      *
      * @param extensionName Extension Interface Name
      * @param binder        IBinder object to be registered
-     * @return REGISTER_SUCCESS on success of registering IBinder object
-     *         REGISTER_FAIL_NAME_NOT_STANDARDIZED on failure due to registering extension with
-     *              non-standardized name
-     *         REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED on failure due to IBinder not
+     * @return {@link #REGISTER_SUCCESS} on success of registering IBinder object
+     *         {@link #REGISTER_FAIL_NAME_NOT_STANDARDIZED} on failure due to registering extension
+     *              with non-standardized name
+     *         {@link #REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED} on failure due to IBinder not
      *              implementing standardized AIDL interface
-     *         REGISTER_FAIL_REMOTE_EXCEPTION on failure due to remote exception
+     *         {@link #REGISTER_FAIL_REMOTE_EXCEPTION} on failure due to remote exception
      *
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
     @RegisterResult
     public int registerExtensionIBinder(@StandardizedExtensionName @NonNull String extensionName,
             @NonNull IBinder binder) {
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index bde4217..a2b826a 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -166,10 +166,6 @@
         }
         return null
     }
-
-    /** Returns all the [PreferenceHierarchyNode]s appear in the hierarchy. */
-    fun getAllPreferences(): List<PreferenceHierarchyNode> =
-        mutableListOf<PreferenceHierarchyNode>().apply { forEachRecursively { add(it) } }
 }
 
 /**
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 49acc1d..6b7be91 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -108,6 +108,9 @@
     }
 }
 
+/** Interface indicates that a virtual [Preference] should be created for binding. */
+interface PreferenceBindingPlaceholder
+
 /** Abstract preference screen to provide preference hierarchy and binding factory. */
 interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenProvider {
 
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index fbe8927..cfe6089 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -218,34 +218,47 @@
             preferenceScreen: PreferenceScreen,
             preferenceBindingFactory: PreferenceBindingFactory,
             preferenceHierarchy: PreferenceHierarchy,
-        ) =
-            preferenceScreen.bindRecursively(
-                preferenceBindingFactory,
-                preferenceHierarchy.getAllPreferences().associateBy { it.metadata.key },
-            )
-
-        private fun PreferenceGroup.bindRecursively(
-            preferenceBindingFactory: PreferenceBindingFactory,
-            preferences: Map<String, PreferenceHierarchyNode>,
-            storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
         ) {
-            preferences[key]?.let { preferenceBindingFactory.bind(this, it) }
-            val count = preferenceCount
-            for (index in 0 until count) {
-                val preference = getPreference(index)
-                if (preference is PreferenceGroup) {
-                    preference.bindRecursively(preferenceBindingFactory, preferences, storages)
-                } else {
-                    preferences[preference.key]?.let {
-                        val metadata = it.metadata
-                        (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
-                            preference.preferenceDataStore =
-                                storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+            val preferences = mutableMapOf<String, PreferenceHierarchyNode>()
+            preferenceHierarchy.forEachRecursively {
+                val metadata = it.metadata
+                preferences[metadata.key] = it
+            }
+            val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>()
+
+            fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) {
+                (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
+                    preferenceDataStore =
+                        storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+                }
+            }
+
+            fun PreferenceGroup.bindRecursively() {
+                preferences.remove(key)?.let { preferenceBindingFactory.bind(this, it) }
+                val count = preferenceCount
+                for (index in 0 until count) {
+                    val preference = getPreference(index)
+                    if (preference is PreferenceGroup) {
+                        preference.bindRecursively()
+                    } else {
+                        preferences.remove(preference.key)?.let {
+                            preference.setPreferenceDataStore(it.metadata)
+                            preferenceBindingFactory.bind(preference, it)
                         }
-                        preferenceBindingFactory.bind(preference, it)
                     }
                 }
             }
+
+            preferenceScreen.bindRecursively()
+            for (node in preferences.values) {
+                val metadata = node.metadata
+                val binding = preferenceBindingFactory.getPreferenceBinding(metadata)
+                if (binding !is PreferenceBindingPlaceholder) continue
+                val preference = binding.createWidget(preferenceScreen.context)
+                preference.setPreferenceDataStore(metadata)
+                preferenceBindingFactory.bind(preference, node, binding)
+                preferenceScreen.addPreference(preference)
+            }
         }
     }
 }
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 81a2e6a..bf419cc 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -74,16 +74,6 @@
 }
 
 flag {
-    name: "volume_panel_broadcast_fix"
-    namespace: "systemui"
-    description: "Make the volume panel's repository listen for the new ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED broadcast instead of ACTION_NOTIFICATION_POLICY_CHANGED"
-    bug: "347707024"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "volume_dialog_audio_sharing_fix"
     namespace: "cross_device_experiences"
     description: "Gates whether to show separate volume bars during audio sharing"
@@ -111,6 +101,14 @@
 }
 
 flag {
+    name: "write_system_preference_permission_enabled"
+    is_fixed_read_only: true
+    namespace: "android_settings"
+    description: "Enable WRITE_SYSTEM_PREFERENCE permission and appop"
+    bug: "375193223"
+}
+
+flag {
   name: "asha_profile_access_profile_enabled_true"
   namespace: "accessibility"
   description: "Changes the return value of HearingAidProfile.accessProfileEnabled() to true"
@@ -166,3 +164,10 @@
     description: "Enable the ambient volume control in device details and hearing devices dialog."
     bug: "357878944"
 }
+
+flag {
+    name: "settings_preference_write_consent_enabled"
+    namespace: "android_settings"
+    description: "Enable the user consent prompt before writing sensitive preferences via service"
+    bug: "378552675"
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
index 7fdbcda..f446bb8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
@@ -93,33 +93,23 @@
                     IntentFilter().apply {
                         addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
                         addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
-                        if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi())
+                        if (android.app.Flags.modesApi())
                             addAction(
-                                NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED)
+                                NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED
+                            )
                     },
                     /* broadcastPermission = */ null,
-                    /* scheduler = */ if (Flags.volumePanelBroadcastFix()) {
-                        backgroundHandler
-                    } else {
-                        null
-                    },
+                    /* scheduler = */ backgroundHandler,
                 )
 
                 awaitClose { context.unregisterReceiver(receiver) }
             }
-            .let {
-                if (Flags.volumePanelBroadcastFix()) {
-                    // Share the flow to avoid having multiple broadcasts.
-                    it.flowOn(backgroundCoroutineContext)
-                        .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
-                } else {
-                    it.shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
-                }
-            }
+            .flowOn(backgroundCoroutineContext)
+            .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
     }
 
     override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> by lazy {
-        if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi())
+        if (android.app.Flags.modesApi())
             flowFromBroadcast(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED) {
                 // If available, get the value from extras to avoid a potential binder call.
                 it?.extras?.getParcelable(EXTRA_NOTIFICATION_POLICY)
@@ -161,11 +151,13 @@
                     contentResolver.registerContentObserver(
                         Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
                         /* notifyForDescendants= */ false,
-                        observer)
+                        observer,
+                    )
                     contentResolver.registerContentObserver(
                         Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG),
                         /* notifyForDescendants= */ false,
-                        observer)
+                        observer,
+                    )
 
                     awaitClose { contentResolver.unregisterContentObserver(observer) }
                 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
index c136644..388af61 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
@@ -23,12 +23,10 @@
 import android.content.Intent
 import android.database.ContentObserver
 import android.os.Parcelable
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.provider.Settings.Global
 import androidx.test.filters.SmallTest
-import com.android.settingslib.flags.Flags
 import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.settingslib.notification.modes.ZenMode
 import com.android.settingslib.notification.modes.ZenModesBackend
@@ -93,26 +91,7 @@
             )
     }
 
-    @DisableFlags(Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
-    @Test
-    fun consolidatedPolicyChanges_repositoryEmits_flagsOff() {
-        testScope.runTest {
-            val values = mutableListOf<NotificationManager.Policy?>()
-            `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1)
-            underTest.consolidatedNotificationPolicy
-                .onEach { values.add(it) }
-                .launchIn(backgroundScope)
-            runCurrent()
-
-            `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2)
-            triggerIntent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
-            runCurrent()
-
-            assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder()
-        }
-    }
-
-    @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
     @Test
     fun consolidatedPolicyChanges_repositoryEmits_flagsOn() {
         testScope.runTest {
@@ -131,7 +110,7 @@
         }
     }
 
-    @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
     @Test
     fun consolidatedPolicyChanges_repositoryEmitsFromExtras() {
         testScope.runTest {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 8c62797..c33d655 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -25,7 +25,6 @@
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.util.fastCoerceIn
 import com.android.compose.animation.scene.content.Content
-import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
 import com.android.compose.nestedscroll.OnStopScope
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
@@ -543,19 +542,6 @@
 
     val connection: PriorityNestedScrollConnection = nestedScrollConnection()
 
-    private fun resolveSwipe(
-        isUpOrLeft: Boolean,
-        pointersDown: PointersInfo.PointersDown?,
-    ): Swipe.Resolved {
-        return resolveSwipe(
-            orientation = draggableHandler.orientation,
-            isUpOrLeft = isUpOrLeft,
-            pointersDown = pointersDown,
-            fromSource =
-                pointersDown?.let { draggableHandler.resolveSwipeSource(it.startedPosition) },
-        )
-    }
-
     private fun nestedScrollConnection(): PriorityNestedScrollConnection {
         // If we performed a long gesture before entering priority mode, we would have to avoid
         // moving on to the next scene.
@@ -563,23 +549,8 @@
 
         var lastPointersDown: PointersInfo.PointersDown? = null
 
-        fun hasNextScene(amount: Float): Boolean {
-            val transitionState = layoutState.transitionState
-            val scene = transitionState.currentScene
-            val fromScene = layoutImpl.scene(scene)
-            val resolvedSwipe =
-                when {
-                    amount < 0f -> resolveSwipe(isUpOrLeft = true, lastPointersDown)
-                    amount > 0f -> resolveSwipe(isUpOrLeft = false, lastPointersDown)
-                    else -> null
-                }
-            val nextScene = resolvedSwipe?.let { fromScene.findActionResultBestMatch(it) }
-            if (nextScene != null) return true
-
-            if (transitionState !is TransitionState.Idle) return false
-
-            val overscrollSpec = layoutImpl.state.transitions.overscrollSpec(scene, orientation)
-            return overscrollSpec != null
+        fun shouldEnableSwipes(): Boolean {
+            return layoutImpl.contentForUserActions().shouldEnableSwipes(orientation)
         }
 
         var isIntercepting = false
@@ -650,17 +621,17 @@
                     when (behavior) {
                         NestedScrollBehavior.EdgeNoPreview -> {
                             canChangeScene = isZeroOffset
-                            isZeroOffset && hasNextScene(offsetAvailable)
+                            isZeroOffset && shouldEnableSwipes()
                         }
 
                         NestedScrollBehavior.EdgeWithPreview -> {
                             canChangeScene = isZeroOffset
-                            hasNextScene(offsetAvailable)
+                            shouldEnableSwipes()
                         }
 
                         NestedScrollBehavior.EdgeAlways -> {
                             canChangeScene = true
-                            hasNextScene(offsetAvailable)
+                            shouldEnableSwipes()
                         }
                     }
 
@@ -693,7 +664,7 @@
                     }
                 lastPointersDown = pointersDown
 
-                val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
+                val canStart = behavior.canStartOnPostFling && shouldEnableSwipes()
                 if (canStart) {
                     isIntercepting = false
                 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 7d40e10..a448ee4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -56,7 +56,7 @@
 }
 
 /** Whether swipe should be enabled in the given [orientation]. */
-private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
+internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
     if (userActions.isEmpty()) {
         return false
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index 68f8491..e3118d67 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Stable
@@ -381,7 +382,7 @@
                     val progressSpec =
                         spring(
                             stiffness = swipeSpec.stiffness,
-                            dampingRatio = swipeSpec.dampingRatio,
+                            dampingRatio = Spring.DampingRatioNoBouncy,
                             visibilityThreshold = ProgressVisibilityThreshold,
                         )
                     animatable.animateTo(0f, progressSpec)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index b44796c..7e6f3a8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -1676,4 +1676,33 @@
         assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
         assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
     }
+
+    @Test
+    fun replaceOverlayNestedScroll() = runGestureTest {
+        layoutState.showOverlay(OverlayA, animationScope = testScope)
+        advanceUntilIdle()
+
+        // Initial state.
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA)
+
+        // Swipe down to replace overlay A by overlay B.
+
+        val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
+        nestedScroll.scroll(downOffset(0.1f))
+        val transition = assertThat(layoutState.transitionState).isReplaceOverlayTransition()
+        assertThat(transition).hasCurrentScene(SceneA)
+        assertThat(transition).hasFromOverlay(OverlayA)
+        assertThat(transition).hasToOverlay(OverlayB)
+        assertThat(transition).hasCurrentOverlays(OverlayA)
+        assertThat(transition).hasProgress(0.1f)
+
+        nestedScroll.preFling(Velocity(0f, velocityThreshold))
+        advanceUntilIdle()
+        // Commit the gesture. The overlays are instantly swapped in the set of current overlays.
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
deleted file mode 100644
index dd58ea7..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class KeyguardDisplayManagerTest extends SysuiTestCase {
-
-    @Mock
-    private NavigationBarController mNavigationBarController;
-    @Mock
-    private ConnectedDisplayKeyguardPresentation.Factory
-            mConnectedDisplayKeyguardPresentationFactory;
-    @Mock
-    private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
-    @Mock
-    private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
-    @Mock
-    private KeyguardStateController mKeyguardStateController;
-
-    private Executor mMainExecutor = Runnable::run;
-    private Executor mBackgroundExecutor = Runnable::run;
-    private KeyguardDisplayManager mManager;
-    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-    // The default and secondary displays are both in the default group
-    private Display mDefaultDisplay;
-    private Display mSecondaryDisplay;
-
-    // This display is in a different group from the default and secondary displays.
-    private Display mAlwaysUnlockedDisplay;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
-                mDisplayTracker, mMainExecutor, mBackgroundExecutor, mDeviceStateHelper,
-                mKeyguardStateController, mConnectedDisplayKeyguardPresentationFactory));
-        doReturn(mConnectedDisplayKeyguardPresentation).when(
-                mConnectedDisplayKeyguardPresentationFactory).create(any());
-        doReturn(mConnectedDisplayKeyguardPresentation).when(mManager)
-                .createPresentation(any());
-        mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
-                new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
-        mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(),
-                Display.DEFAULT_DISPLAY + 1,
-                new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
-
-        DisplayInfo alwaysUnlockedDisplayInfo = new DisplayInfo();
-        alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2;
-        alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED;
-        mAlwaysUnlockedDisplay = new Display(DisplayManagerGlobal.getInstance(),
-                Display.DEFAULT_DISPLAY,
-                alwaysUnlockedDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-    }
-
-    @Test
-    public void testShow_defaultDisplayOnly() {
-        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay});
-        mManager.show();
-        verify(mManager, never()).createPresentation(any());
-    }
-
-    @Test
-    public void testShow_includeSecondaryDisplay() {
-        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
-        mManager.show();
-        verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
-    }
-
-    @Test
-    public void testShow_includeAlwaysUnlockedDisplay() {
-        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
-
-        mManager.show();
-        verify(mManager, never()).createPresentation(any());
-    }
-
-    @Test
-    public void testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
-        mDisplayTracker.setAllDisplays(
-                new Display[]{mDefaultDisplay, mSecondaryDisplay, mAlwaysUnlockedDisplay});
-
-        mManager.show();
-        verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
-    }
-
-    @Test
-    public void testShow_concurrentDisplayActive_occluded() {
-        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
-
-        when(mDeviceStateHelper.isConcurrentDisplayActive(mSecondaryDisplay)).thenReturn(true);
-        when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        verify(mManager, never()).createPresentation(eq(mSecondaryDisplay));
-    }
-
-    @Test
-    public void testShow_presentationCreated() {
-        when(mManager.createPresentation(any())).thenCallRealMethod();
-        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
-
-        mManager.show();
-
-        verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay));
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
new file mode 100644
index 0000000..57a6797
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
@@ -0,0 +1,225 @@
+/*
+ * 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.keyguard
+
+import android.hardware.display.DisplayManagerGlobal
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display
+import android.view.DisplayAdjustments
+import android.view.DisplayInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardDisplayManager.DeviceStateHelper
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.data.repository.FakeShadePositionRepository
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import java.util.concurrent.Executor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
+class KeyguardDisplayManagerTest : SysuiTestCase() {
+    @Mock private val navigationBarController = mock(NavigationBarController::class.java)
+    @Mock
+    private val presentationFactory = mock(ConnectedDisplayKeyguardPresentation.Factory::class.java)
+    @Mock
+    private val connectedDisplayKeyguardPresentation =
+        mock(ConnectedDisplayKeyguardPresentation::class.java)
+    @Mock private val deviceStateHelper = mock(DeviceStateHelper::class.java)
+    @Mock private val keyguardStateController = mock(KeyguardStateController::class.java)
+    private val shadePositionRepository = FakeShadePositionRepository()
+
+    private val mainExecutor = Executor { it.run() }
+    private val backgroundExecutor = Executor { it.run() }
+    private lateinit var manager: KeyguardDisplayManager
+    private val displayTracker = FakeDisplayTracker(mContext)
+    // The default and secondary displays are both in the default group
+    private lateinit var defaultDisplay: Display
+    private lateinit var secondaryDisplay: Display
+
+    private val testScope = TestScope(UnconfinedTestDispatcher())
+
+    // This display is in a different group from the default and secondary displays.
+    private lateinit var alwaysUnlockedDisplay: Display
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        manager =
+            KeyguardDisplayManager(
+                mContext,
+                { navigationBarController },
+                displayTracker,
+                mainExecutor,
+                backgroundExecutor,
+                deviceStateHelper,
+                keyguardStateController,
+                presentationFactory,
+                { shadePositionRepository },
+                testScope.backgroundScope,
+            )
+        whenever(presentationFactory.create(any())).doReturn(connectedDisplayKeyguardPresentation)
+
+        defaultDisplay =
+            Display(
+                DisplayManagerGlobal.getInstance(),
+                Display.DEFAULT_DISPLAY,
+                DisplayInfo(),
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
+            )
+        secondaryDisplay =
+            Display(
+                DisplayManagerGlobal.getInstance(),
+                Display.DEFAULT_DISPLAY + 1,
+                DisplayInfo(),
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
+            )
+
+        val alwaysUnlockedDisplayInfo = DisplayInfo()
+        alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2
+        alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED
+        alwaysUnlockedDisplay =
+            Display(
+                DisplayManagerGlobal.getInstance(),
+                Display.DEFAULT_DISPLAY,
+                alwaysUnlockedDisplayInfo,
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
+            )
+    }
+
+    @Test
+    fun testShow_defaultDisplayOnly() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay)
+        manager.show()
+        verify(presentationFactory, never()).create(any())
+    }
+
+    @Test
+    fun testShow_includeSecondaryDisplay() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+        manager.show()
+        verify(presentationFactory).create(eq(secondaryDisplay))
+    }
+
+    @Test
+    fun testShow_includeAlwaysUnlockedDisplay() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, alwaysUnlockedDisplay)
+
+        manager.show()
+        verify(presentationFactory, never()).create(any())
+    }
+
+    @Test
+    fun testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
+        displayTracker.allDisplays =
+            arrayOf(defaultDisplay, secondaryDisplay, alwaysUnlockedDisplay)
+
+        manager.show()
+        verify(presentationFactory).create(eq(secondaryDisplay))
+    }
+
+    @Test
+    fun testShow_concurrentDisplayActive_occluded() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+        whenever(deviceStateHelper.isConcurrentDisplayActive(secondaryDisplay)).thenReturn(true)
+        whenever(keyguardStateController.isOccluded).thenReturn(true)
+        verify(presentationFactory, never()).create(eq(secondaryDisplay))
+    }
+
+    @Test
+    fun testShow_presentationCreated() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+        manager.show()
+
+        verify(presentationFactory).create(eq(secondaryDisplay))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun show_shadeMovesDisplay_newPresentationCreated() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+        // Shade in the default display, we expect the presentation to be in the secondary only
+        shadePositionRepository.setDisplayId(defaultDisplay.displayId)
+
+        manager.show()
+
+        verify(presentationFactory).create(eq(secondaryDisplay))
+        verify(presentationFactory, never()).create(eq(defaultDisplay))
+        reset(presentationFactory)
+        whenever(presentationFactory.create(any())).thenReturn(connectedDisplayKeyguardPresentation)
+
+        // Let's move it to the secondary display. We expect it will be added in the default
+        // one.
+        shadePositionRepository.setDisplayId(secondaryDisplay.displayId)
+        testScope.advanceUntilIdle()
+
+        verify(presentationFactory).create(eq(defaultDisplay))
+        reset(presentationFactory)
+        whenever(presentationFactory.create(any())).thenReturn(connectedDisplayKeyguardPresentation)
+
+        // Let's move it back! it should be re-created (it means it was removed before)
+        shadePositionRepository.setDisplayId(defaultDisplay.displayId)
+        testScope.advanceUntilIdle()
+
+        verify(presentationFactory).create(eq(secondaryDisplay))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun show_shadeInSecondaryDisplay_defaultOneHasPresentation() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+        shadePositionRepository.setDisplayId(secondaryDisplay.displayId)
+
+        manager.show()
+
+        verify(presentationFactory).create(eq(defaultDisplay))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun show_shadeInDefaultDisplay_secondaryOneHasPresentation() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+        shadePositionRepository.setDisplayId(defaultDisplay.displayId)
+
+        manager.show()
+
+        verify(presentationFactory).create(eq(secondaryDisplay))
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
index 5df9b7b..2efa2f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
@@ -85,63 +85,62 @@
     @Test
     fun connectKeyboard_delayElapse_launchForKeyboard() =
         testScope.runTest {
-            launchAndAssert(TutorialType.KEYBOARD)
-
             keyboardRepository.setIsAnyKeyboardConnected(true)
             advanceTimeBy(LAUNCH_DELAY)
+
+            launchAndAssert(TutorialType.KEYBOARD)
         }
 
     @Test
     fun connectBothDevices_delayElapse_launchForBoth() =
         testScope.runTest {
-            launchAndAssert(TutorialType.BOTH)
-
             keyboardRepository.setIsAnyKeyboardConnected(true)
             touchpadRepository.setIsAnyTouchpadConnected(true)
             advanceTimeBy(LAUNCH_DELAY)
+
+            launchAndAssert(TutorialType.BOTH)
         }
 
     @Test
     fun connectBothDevice_delayNotElapse_launchNothing() =
         testScope.runTest {
-            launchAndAssert(TutorialType.NONE)
-
             keyboardRepository.setIsAnyKeyboardConnected(true)
             touchpadRepository.setIsAnyTouchpadConnected(true)
             advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+
+            launchAndAssert(TutorialType.NONE)
         }
 
     @Test
     fun nothingConnect_delayElapse_launchNothing() =
         testScope.runTest {
-            launchAndAssert(TutorialType.NONE)
-
             keyboardRepository.setIsAnyKeyboardConnected(false)
             touchpadRepository.setIsAnyTouchpadConnected(false)
             advanceTimeBy(LAUNCH_DELAY)
+
+            launchAndAssert(TutorialType.NONE)
         }
 
     @Test
     fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() =
         testScope.runTest {
-            launchAndAssert(TutorialType.BOTH)
-
             keyboardRepository.setIsAnyKeyboardConnected(true)
             advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
             touchpadRepository.setIsAnyTouchpadConnected(true)
             advanceTimeBy(REMAINING_TIME)
+
+            launchAndAssert(TutorialType.BOTH)
         }
 
     @Test
     fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() =
         testScope.runTest {
-            launchAndAssert(TutorialType.NONE)
-
             keyboardRepository.setIsAnyKeyboardConnected(true)
             advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
             touchpadRepository.setIsAnyTouchpadConnected(true)
             keyboardRepository.setIsAnyKeyboardConnected(false)
             advanceTimeBy(REMAINING_TIME)
+            launchAndAssert(TutorialType.NONE)
         }
 
     private suspend fun launchAndAssert(expectedTutorial: TutorialType) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index a862fdf..778e822 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -19,6 +19,8 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.view.Display
 import android.view.DisplayCutout
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,6 +31,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -1051,7 +1054,8 @@
     }
 
     @Test
-    fun onMaxBoundsChanged_beforeStart_listenerNotNotified() {
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun onMaxBoundsChanged_beforeStart_flagEnabled_listenerNotNotified() {
         // Start out with an existing configuration with bounds
         configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
         configurationController.onConfigurationChanged(configuration)
@@ -1083,7 +1087,41 @@
     }
 
     @Test
-    fun onDensityOrFontScaleChanged_beforeStart_listenerNotNotified() {
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun onMaxBoundsChanged_beforeStart_flagDisabled_listenerNotNotified() {
+        // Start out with an existing configuration with bounds
+        configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
+        configurationController.onConfigurationChanged(configuration)
+        val provider =
+            StatusBarContentInsetsProviderImpl(
+                contextMock,
+                configurationController,
+                mock<DumpManager>(),
+                mock<CommandRegistry>(),
+                mock<SysUICutoutProvider>(),
+            )
+        val listener =
+            object : StatusBarContentInsetsChangedListener {
+                var triggered = false
+
+                override fun onStatusBarContentInsetsChanged() {
+                    triggered = true
+                }
+            }
+        provider.addCallback(listener)
+
+        // WHEN the config is updated with new bounds
+        // but provider is not started
+        configuration.windowConfiguration.setMaxBounds(0, 0, 456, 789)
+        configurationController.onConfigurationChanged(configuration)
+
+        // THEN the listener is notified
+        assertThat(listener.triggered).isTrue()
+    }
+
+    @Test
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun onDensityOrFontScaleChanged_beforeStart_flagEnabled_listenerNotNotified() {
         configuration.densityDpi = 12
         val provider =
             StatusBarContentInsetsProviderImpl(
@@ -1112,6 +1150,36 @@
     }
 
     @Test
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun onDensityOrFontScaleChanged_beforeStart_flagDisabled_listenerNotified() {
+        configuration.densityDpi = 12
+        val provider =
+            StatusBarContentInsetsProviderImpl(
+                contextMock,
+                configurationController,
+                mock<DumpManager>(),
+                mock<CommandRegistry>(),
+                mock<SysUICutoutProvider>(),
+            )
+        val listener =
+            object : StatusBarContentInsetsChangedListener {
+                var triggered = false
+
+                override fun onStatusBarContentInsetsChanged() {
+                    triggered = true
+                }
+            }
+        provider.addCallback(listener)
+
+        // WHEN the config is updated, but the provider is not started
+        configuration.densityDpi = 20
+        configurationController.onConfigurationChanged(configuration)
+
+        // THEN the listener is notified
+        assertThat(listener.triggered).isTrue()
+    }
+
+    @Test
     fun onDensityOrFontScaleChanged_afterStart_listenerNotified() {
         configuration.densityDpi = 12
         val provider =
@@ -1169,7 +1237,8 @@
     }
 
     @Test
-    fun onThemeChanged_beforeStart_listenerNotNotified() {
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun onThemeChanged_beforeStart_flagEnabled_listenerNotNotified() {
         val provider =
             StatusBarContentInsetsProviderImpl(
                 contextMock,
@@ -1193,6 +1262,32 @@
         assertThat(listener.triggered).isFalse()
     }
 
+    @Test
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun onThemeChanged_beforeStart_flagDisabled_listenerNotified() {
+        val provider =
+            StatusBarContentInsetsProviderImpl(
+                contextMock,
+                configurationController,
+                mock<DumpManager>(),
+                mock<CommandRegistry>(),
+                mock<SysUICutoutProvider>(),
+            )
+        val listener =
+            object : StatusBarContentInsetsChangedListener {
+                var triggered = false
+
+                override fun onStatusBarContentInsetsChanged() {
+                    triggered = true
+                }
+            }
+        provider.addCallback(listener)
+
+        configurationController.notifyThemeChanged()
+
+        assertThat(listener.triggered).isTrue()
+    }
+
     private fun assertRects(
         expected: Rect,
         actual: Rect,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
index cdc7aa2..9888574 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
@@ -32,6 +32,8 @@
     override fun bind(
         view: View,
         viewModel: HomeStatusBarViewModel,
+        systemEventChipAnimateIn: ((View) -> Unit)?,
+        systemEventChipAnimateOut: ((View) -> Unit)?,
         listener: StatusBarVisibilityChangeListener,
     ) {
         this.listener = listener
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 02c1540..eef5753 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -19,6 +19,7 @@
 import android.view.View
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,11 +54,14 @@
             )
         )
 
-    override val isSystemInfoVisible =
+    override val systemInfoCombinedVis =
         MutableStateFlow(
-            HomeStatusBarViewModel.VisibilityModel(
-                visibility = View.GONE,
-                shouldAnimateChange = false,
+            HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
+                HomeStatusBarViewModel.VisibilityModel(
+                    visibility = View.GONE,
+                    shouldAnimateChange = false,
+                ),
+                Idle,
             )
         )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index b3a73d8..c4d2569 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -60,11 +60,16 @@
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
@@ -90,6 +95,7 @@
     private val activeNotificationListRepository = kosmos.activeNotificationListRepository
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
     private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository
+    private val systemStatusEventAnimationRepository = kosmos.systemStatusEventAnimationRepository
 
     private lateinit var underTest: HomeStatusBarViewModel
 
@@ -546,25 +552,50 @@
     @Test
     fun isSystemInfoVisible_allowedByDisableFlags_visible() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.isSystemInfoVisible)
+            val latest by collectLastValue(underTest.systemInfoCombinedVis)
             transitionKeyguardToGone()
 
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
 
-            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
         }
 
     @Test
     fun isSystemInfoVisible_notAllowedByDisableFlags_gone() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.isSystemInfoVisible)
+            val latest by collectLastValue(underTest.systemInfoCombinedVis)
             transitionKeyguardToGone()
 
             disableFlagsRepository.disableFlags.value =
                 DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE)
 
-            assertThat(latest!!.visibility).isEqualTo(View.GONE)
+            assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    fun systemInfoCombineVis_animationsPassThrough() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.systemInfoCombinedVis)
+            transitionKeyguardToGone()
+
+            assertThat(latest!!.baseVisibility)
+                .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false))
+            assertThat(latest!!.animationState).isEqualTo(Idle)
+
+            // WHEN the animation state changes, but the visibility state doesn't change
+            systemStatusEventAnimationRepository.animationState.value = AnimatingIn
+
+            // THEN the visibility is the same
+            assertThat(latest!!.baseVisibility)
+                .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false))
+            // THEN the animation state updates
+            assertThat(latest!!.animationState).isEqualTo(AnimatingIn)
+
+            systemStatusEventAnimationRepository.animationState.value = AnimatingOut
+            assertThat(latest!!.baseVisibility)
+                .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false))
+            assertThat(latest!!.animationState).isEqualTo(AnimatingOut)
         }
 
     @Test
@@ -573,7 +604,7 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             keyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.GONE,
@@ -583,7 +614,7 @@
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -592,13 +623,13 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -607,7 +638,7 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             keyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -617,7 +648,7 @@
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -626,13 +657,13 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -641,7 +672,7 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             keyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -651,7 +682,7 @@
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
         }
 
     @Test
@@ -660,14 +691,14 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
             kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
         }
 
     @Test
@@ -676,13 +707,13 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             transitionKeyguardToGone()
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
         }
 
     @Test
@@ -691,14 +722,14 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
             transitionKeyguardToGone()
 
             kosmos.shadeTestUtil.setShadeExpansion(0f)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
         }
 
     @Test
@@ -707,13 +738,13 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             kosmos.sceneContainerRepository.snapToScene(Scenes.Gone)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
         }
 
     @Test
@@ -722,14 +753,14 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
             transitionKeyguardToGone()
 
             kosmos.shadeTestUtil.setShadeExpansion(1f)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -738,14 +769,14 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
             transitionKeyguardToGone()
 
             kosmos.sceneContainerRepository.snapToScene(Scenes.Shade)
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -754,7 +785,7 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             // Secure camera is an occluding activity
             keyguardTransitionRepository.sendTransitionSteps(
@@ -766,7 +797,7 @@
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     @Test
@@ -775,7 +806,7 @@
         testScope.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
-            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
 
             kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
             // Secure camera is an occluding activity
@@ -784,7 +815,7 @@
 
             assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
             assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
-            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index e90f055f..f187ce6 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,50 +13,58 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/volume_dialog_container"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="right"
-    android:divider="@drawable/volume_dialog_floating_sliders_spacer"
-    android:orientation="horizontal"
-    android:showDividers="middle|end|beginning"
-    android:theme="@style/volume_dialog_theme">
+    android:id="@+id/volume_dialog_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
 
     <LinearLayout
-        android:id="@+id/volume_dialog_floating_sliders_container"
+        android:id="@+id/volume_dialog_container"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
-        android:gravity="bottom"
-        android:orientation="horizontal"
-        android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
-        android:showDividers="middle" />
-
-    <LinearLayout
-        android:id="@+id/volume_dialog"
-        android:layout_width="@dimen/volume_dialog_width"
         android:layout_height="wrap_content"
-        android:background="@drawable/volume_dialog_background"
-        android:divider="@drawable/volume_dialog_spacer"
-        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
-        android:gravity="center_horizontal"
-        android:orientation="vertical"
-        android:showDividers="middle">
+        android:layout_gravity="center_vertical|end"
+        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+        android:orientation="horizontal"
+        android:showDividers="middle|end|beginning">
 
-        <include layout="@layout/volume_ringer_drawer" />
+        <LinearLayout
+            android:id="@+id/volume_dialog_floating_sliders_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+            android:gravity="bottom"
+            android:orientation="horizontal"
+            android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
+            android:showDividers="middle" />
 
-        <include layout="@layout/volume_dialog_slider" />
+        <LinearLayout
+            android:id="@+id/volume_dialog"
+            android:layout_width="@dimen/volume_dialog_width"
+            android:layout_height="wrap_content"
+            android:background="@drawable/volume_dialog_background"
+            android:clipChildren="false"
+            android:clipToOutline="false"
+            android:clipToPadding="false"
+            android:divider="@drawable/volume_dialog_spacer"
+            android:gravity="center_horizontal"
+            android:orientation="vertical"
+            android:paddingVertical="@dimen/volume_dialog_vertical_padding"
+            android:showDividers="middle">
 
-        <ImageButton
-            android:id="@+id/volume_dialog_settings"
-            android:layout_width="@dimen/volume_dialog_button_size"
-            android:layout_height="@dimen/volume_dialog_button_size"
-            android:background="@drawable/ripple_drawable_20dp"
-            android:contentDescription="@string/accessibility_volume_settings"
-            android:soundEffectsEnabled="false"
-            android:src="@drawable/horizontal_ellipsis"
-            android:tint="?androidprv:attr/materialColorPrimary" />
+            <include layout="@layout/volume_ringer_drawer" />
+
+            <include layout="@layout/volume_dialog_slider" />
+
+            <ImageButton
+                android:id="@+id/volume_dialog_settings"
+                android:layout_width="@dimen/volume_dialog_button_size"
+                android:layout_height="@dimen/volume_dialog_button_size"
+                android:background="@drawable/ripple_drawable_20dp"
+                android:contentDescription="@string/accessibility_volume_settings"
+                android:soundEffectsEnabled="false"
+                android:src="@drawable/horizontal_ellipsis"
+                android:tint="?androidprv:attr/materialColorPrimary" />
+        </LinearLayout>
     </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7d071cd..c69b98c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -557,6 +557,16 @@
         <item name="android:showWhenLocked">true</item>
     </style>
 
+    <style name="Theme.SystemUI.Dialog.Volume">
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:showWhenLocked">true</item>
+        <item name="android:windowBackground">@color/transparent</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowIsFloating">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
     <style name="SystemUI.Material3.Slider.Volume">
         <item name="trackHeight">40dp</item>
         <item name="thumbHeight">52dp</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1342dd0..95830b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,6 +15,8 @@
  */
 package com.android.keyguard;
 
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
 import android.annotation.NonNull;
 import android.app.Presentation;
 import android.content.Context;
@@ -36,18 +38,24 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.data.repository.ShadePositionRepository;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import dagger.Lazy;
 
+import kotlinx.coroutines.CoroutineScope;
+
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 @SysUISingleton
 public class KeyguardDisplayManager {
@@ -58,6 +66,7 @@
     private final DisplayManager mDisplayService;
     private final DisplayTracker mDisplayTracker;
     private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
+    private final Provider<ShadePositionRepository> mShadePositionRepositoryProvider;
     private final ConnectedDisplayKeyguardPresentation.Factory
             mConnectedDisplayKeyguardPresentationFactory;
     private final Context mContext;
@@ -102,9 +111,12 @@
             DeviceStateHelper deviceStateHelper,
             KeyguardStateController keyguardStateController,
             ConnectedDisplayKeyguardPresentation.Factory
-                    connectedDisplayKeyguardPresentationFactory) {
+                    connectedDisplayKeyguardPresentationFactory,
+            Provider<ShadePositionRepository> shadePositionRepositoryProvider,
+            @Application CoroutineScope appScope) {
         mContext = context;
         mNavigationBarControllerLazy = navigationBarControllerLazy;
+        mShadePositionRepositoryProvider = shadePositionRepositoryProvider;
         uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
         mDisplayService = mContext.getSystemService(DisplayManager.class);
         mDisplayTracker = displayTracker;
@@ -112,6 +124,17 @@
         mDeviceStateHelper = deviceStateHelper;
         mKeyguardStateController = keyguardStateController;
         mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
+        if (ShadeWindowGoesAround.isEnabled()) {
+            collectFlow(appScope, shadePositionRepositoryProvider.get().getDisplayId(),
+                    (id) -> onShadeWindowMovedToDisplayId(id));
+        }
+    }
+
+    private void onShadeWindowMovedToDisplayId(int shadeDisplayId) {
+        if (mShowing) {
+            hidePresentation(shadeDisplayId);
+            updateDisplays(/* showing= */ true);
+        }
     }
 
     private boolean isKeyguardShowable(Display display) {
@@ -119,9 +142,20 @@
             if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
             return false;
         }
-        if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
-            if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
-            return false;
+        if (ShadeWindowGoesAround.isEnabled()) {
+            int shadeDisplayId = mShadePositionRepositoryProvider.get().getDisplayId().getValue();
+            if (display.getDisplayId() == shadeDisplayId) {
+                if (DEBUG) {
+                    Log.i(TAG,
+                            "Do not show KeyguardPresentation on the shade window display");
+                }
+                return false;
+            }
+        } else {
+            if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
+                if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
+                return false;
+            }
         }
         display.getDisplayInfo(mTmpDisplayInfo);
         if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 4a369e7..7758dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -17,12 +17,14 @@
 package com.android.systemui.inputdevice.tutorial.domain.interactor
 
 import android.os.SystemProperties
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
 import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.LAUNCH_DELAY
 import com.android.systemui.keyboard.data.repository.KeyboardRepository
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -35,6 +37,7 @@
 import kotlin.time.toKotlinDuration
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flow
@@ -96,6 +99,9 @@
     private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
         isAnyDeviceConnected[deviceType]!!.filter { it }.first()
 
+    // Only for testing notifications. This should behave independently from scheduling
+    @VisibleForTesting val commandTutorials = MutableStateFlow(TutorialType.NONE)
+
     // Merging two flows ensures that tutorial is launched consecutively to avoid race condition
     val tutorials: Flow<TutorialType> =
         merge(touchpadScheduleFlow, keyboardScheduleFlow).map {
@@ -146,6 +152,15 @@
                         pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}")
                         pw.println("         launch time = ${repo.launchTime(TOUCHPAD)}")
                     }
+                "notify" -> {
+                    if (args.size != 2) help(pw)
+                    when (args[1]) {
+                        "keyboard" -> commandTutorials.value = TutorialType.KEYBOARD
+                        "touchpad" -> commandTutorials.value = TutorialType.TOUCHPAD
+                        "both" -> commandTutorials.value = TutorialType.BOTH
+                        else -> help(pw)
+                    }
+                }
                 else -> help(pw)
             }
         }
@@ -155,6 +170,7 @@
             pw.println("Available commands:")
             pw.println("  clear")
             pw.println("  info")
+            pw.println("  notify [keyboard|touchpad|both]")
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
index 3b26f2f..9dae649 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
@@ -24,6 +24,7 @@
 import android.content.Intent
 import android.os.Bundle
 import androidx.core.app.NotificationCompat
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -41,7 +42,7 @@
 import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.merge
 
 /** When the scheduler is due, show a notification to launch tutorial */
 @SysUISingleton
@@ -56,7 +57,11 @@
 ) {
     fun start() {
         backgroundScope.launch {
-            tutorialSchedulerInteractor.tutorials.collect { showNotification(it) }
+            merge(
+                    tutorialSchedulerInteractor.tutorials,
+                    tutorialSchedulerInteractor.commandTutorials,
+                )
+                .collect { showNotification(it) }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
index fa49415..fee08b3 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
@@ -27,6 +27,7 @@
 import androidx.lifecycle.Lifecycle.State.STARTED
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.theme.PlatformTheme
 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
@@ -40,7 +41,6 @@
 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
 import java.util.Optional
 import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
@@ -90,6 +90,7 @@
         setContent {
             PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) }
         }
+        // TODO(b/376692701): Update launchTime when the activity is launched by Companion App
         if (savedInstanceState == null) {
             metricsLogger.logPeripheralTutorialLaunched(
                 intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 7292dda..b30e1e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -26,6 +26,7 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -44,7 +45,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 object KeyguardClockViewBinder {
     private val TAG = KeyguardClockViewBinder::class.simpleName!!
@@ -133,15 +133,26 @@
                     launch {
                         if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         aodBurnInViewModel.movement.collect { burnInModel ->
-                            viewModel.currentClock.value?.let {
-                                it.largeClock.layout.applyAodBurnIn(
+                            viewModel.currentClock.value
+                                ?.largeClock
+                                ?.layout
+                                ?.applyAodBurnIn(
                                     AodClockBurnInModel(
                                         translationX = burnInModel.translationX.toFloat(),
                                         translationY = burnInModel.translationY.toFloat(),
                                         scale = burnInModel.scale,
                                     )
                                 )
-                            }
+                        }
+                    }
+
+                    launch {
+                        if (!MigrateClocksToBlueprint.isEnabled) return@launch
+                        viewModel.largeClockTextSize.collect { fontSizePx ->
+                            viewModel.currentClock.value
+                                ?.largeClock
+                                ?.events
+                                ?.onFontSettingChanged(fontSizePx = fontSizePx.toFloat())
                         }
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 82adced..3a7a640 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -187,6 +187,9 @@
     val largeClockTopMargin: Flow<Int> =
         configurationInteractor.onAnyConfigurationChange.map { getLargeClockTopMargin() }
 
+    val largeClockTextSize: Flow<Int> =
+        configurationInteractor.dimensionPixelSize(customR.dimen.large_clock_text_size)
+
     enum class ClockLayout {
         LARGE_CLOCK,
         SMALL_CLOCK,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 4a51bf0..177a5be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.panels.ui.compose.infinitegrid
 
 import android.graphics.drawable.Animatable
+import android.graphics.drawable.Drawable
 import android.text.TextUtils
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
@@ -30,7 +31,9 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.material3.MaterialTheme
@@ -68,6 +71,8 @@
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconHeight
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconWidth
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
 import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
 import com.android.systemui.res.R
@@ -79,6 +84,7 @@
     label: String,
     secondaryLabel: String?,
     icon: Icon,
+    sideDrawable: Drawable?,
     colors: TileColors,
     squishiness: () -> Float,
     accessibilityUiState: AccessibilityUiState? = null,
@@ -135,6 +141,14 @@
             colors = colors,
             accessibilityUiState = accessibilityUiState,
         )
+
+        if (sideDrawable != null) {
+            Image(
+                painter = rememberDrawablePainter(sideDrawable),
+                contentDescription = null,
+                modifier = Modifier.width(SideIconWidth).height(SideIconHeight),
+            )
+        }
     }
 }
 
@@ -229,6 +243,8 @@
 object CommonTileDefaults {
     val IconSize = 32.dp
     val LargeTileIconSize = 28.dp
+    val SideIconWidth = 32.dp
+    val SideIconHeight = 20.dp
     val ToggleTargetSize = 56.dp
     val TileHeight = 72.dp
     val TilePadding = 8.dp
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 9bbf290..7ff43c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -199,6 +199,7 @@
                     label = uiState.label,
                     secondaryLabel = uiState.secondaryLabel,
                     icon = icon,
+                    sideDrawable = uiState.sideDrawable,
                     colors = colors,
                     iconShape = iconShape,
                     toggleClick = secondaryClick,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 56675e4..2fc7f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.panels.ui.viewmodel
 
 import android.content.res.Resources
+import android.graphics.drawable.Drawable
 import android.service.quicksettings.Tile
 import android.text.TextUtils
 import android.widget.Switch
@@ -36,6 +37,7 @@
     val handlesLongClick: Boolean,
     val handlesSecondaryClick: Boolean,
     val icon: Supplier<QSTile.Icon?>,
+    val sideDrawable: Drawable?,
     val accessibilityUiState: AccessibilityUiState,
 )
 
@@ -90,6 +92,7 @@
         handlesLongClick = handlesLongClick,
         handlesSecondaryClick = handlesSecondaryClick,
         icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
+        sideDrawable = sideViewCustomDrawable,
         AccessibilityUiState(
             contentDescription?.toString() ?: "",
             stateDescription.toString(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index 63510b8..e15830e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -157,7 +157,6 @@
 
     @SysUISingleton
     @Provides
-    @ShadeDisplayAware
     fun provideShadePositionRepository(impl: ShadePositionRepositoryImpl): ShadePositionRepository {
         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
         return impl
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt
new file mode 100644
index 0000000..37210b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.shade.data.repository
+
+import android.view.Display
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeShadePositionRepository : ShadePositionRepository {
+    private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+
+    override fun setDisplayId(displayId: Int) {
+        _displayId.value = displayId
+    }
+
+    override val displayId: StateFlow<Int>
+        get() = _displayId
+
+    override fun resetDisplayId() {
+        _displayId.value = Display.DEFAULT_DISPLAY
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
index 6f492cf..c23ff53 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
@@ -32,7 +32,7 @@
 
     /** Is the refactor enabled */
     @JvmStatic
-    inline val isEnabled
+    inline val isEnabled: Boolean
         get() = Flags.shadeWindowGoesAround()
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 564d52a..1cb4c44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -27,7 +27,10 @@
 interface SystemStatusAnimationScheduler :
     CallbackController<SystemStatusAnimationCallback>, Dumpable {
 
-    /** StateFlow holding the current [SystemEventAnimationState] at any time. */
+    /**
+     * The current state of the animation. This can be used from compose functions to coordinate
+     * their animations with the chip
+     */
     val animationState: StateFlow<SystemEventAnimationState>
 
     fun onStatusEvent(event: StatusEvent)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt
new file mode 100644
index 0000000..971f5d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.statusbar.events.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** Repository to expose the [SystemStatusAnimationScheduler] state via flows */
+interface SystemStatusEventAnimationRepository {
+    val animationState: StateFlow<SystemEventAnimationState>
+}
+
+@SysUISingleton
+class SystemStatusEventAnimationRepositoryImpl
+@Inject
+constructor(scheduler: SystemStatusAnimationScheduler) : SystemStatusEventAnimationRepository {
+    override val animationState = scheduler.animationState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt
new file mode 100644
index 0000000..3e30642
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.statusbar.events.domain.interactor
+
+import android.view.View
+import androidx.core.animation.Animator
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepository
+import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Interactor for dealing with system status event animations. This class can be used to monitor the
+ * current [animationState], and defines some common animation functions that an handle hiding
+ * system chrome in order to make space for the event chips
+ */
+@SysUISingleton
+class SystemStatusEventAnimationInteractor
+@Inject
+constructor(
+    repo: SystemStatusEventAnimationRepository,
+    configurationInteractor: ConfigurationInteractor,
+    @Application scope: CoroutineScope,
+) {
+    private val chipAnimateInTranslationX =
+        configurationInteractor
+            .dimensionPixelSize(R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
+            .stateIn(scope, SharingStarted.Eagerly, 0)
+
+    private val chipAnimateOutTranslationX =
+        configurationInteractor
+            .dimensionPixelSize(R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
+            .stateIn(scope, SharingStarted.Eagerly, 0)
+
+    val animationState = repo.animationState
+
+    private fun getDefaultStatusBarAnimationForChipEnter(
+        setX: (Float) -> Unit,
+        setAlpha: (Float) -> Unit,
+    ): Animator {
+        return StatusBarSystemEventDefaultAnimator.getDefaultStatusBarAnimationForChipEnter(
+            chipAnimateInTranslationX.value,
+            setX,
+            setAlpha,
+        )
+    }
+
+    private fun getDefaultStatusBarAnimationForChipExit(
+        setX: (Float) -> Unit,
+        setAlpha: (Float) -> Unit,
+    ): Animator {
+        return StatusBarSystemEventDefaultAnimator.getDefaultStatusBarAnimationForChipExit(
+            chipAnimateOutTranslationX.value,
+            setX,
+            setAlpha,
+        )
+    }
+
+    fun animateStatusBarContentForChipEnter(v: View) {
+        getDefaultStatusBarAnimationForChipEnter(setX = v::setTranslationX, setAlpha = v::setAlpha)
+            .start()
+    }
+
+    fun animateStatusBarContentForChipExit(v: View) {
+        v.translationX = chipAnimateOutTranslationX.value.toFloat()
+        getDefaultStatusBarAnimationForChipExit(setX = v::setTranslationX, setAlpha = v::setAlpha)
+            .start()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index d991b1d..41db5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -174,6 +174,14 @@
     private val dumpableName = TAG + nameSuffix
     private val commandName = StatusBarInsetsCommand.NAME + nameSuffix
 
+    init {
+        if (!StatusBarConnectedDisplays.isEnabled) {
+            // Call start(), since it is not called when the flag is disabled, to keep the old
+            // behavior as it was.
+            start()
+        }
+    }
+
     override fun start() {
         configurationController.addCallback(this)
         dumpManager.registerDumpable(dumpableName, this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index c55a63c..23b4b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -376,7 +376,11 @@
         mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
 
         mHomeStatusBarViewBinder.bind(
-                mStatusBar, mHomeStatusBarViewModel, mStatusBarVisibilityChangeListener);
+                mStatusBar,
+                mHomeStatusBarViewModel,
+                /* systemEventChipAnimateIn */ null,
+                /* systemEventChipAnimateOut */ null,
+                mStatusBarVisibilityChangeListener);
     }
 
     private String getDumpableName() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
index 1f9ea08..fd7bce0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -71,49 +71,87 @@
 
     override fun onSystemEventAnimationBegin(): Animator {
         isAnimationRunning = true
-        val moveOut =
-            ValueAnimator.ofFloat(0f, 1f).apply {
-                duration = 23.frames
-                interpolator = STATUS_BAR_X_MOVE_OUT
-                addUpdateListener {
-                    onTranslationXChanged(-(translationXIn * animatedValue as Float))
-                }
-            }
-        val alphaOut =
-            ValueAnimator.ofFloat(1f, 0f).apply {
-                duration = 8.frames
-                interpolator = null
-                addUpdateListener { onAlphaChanged(animatedValue as Float) }
-            }
-
-        val animSet = AnimatorSet()
-        animSet.playTogether(moveOut, alphaOut)
-        return animSet
+        return getDefaultStatusBarAnimationForChipEnter(
+            translationXIn,
+            onTranslationXChanged,
+            onAlphaChanged,
+        )
     }
 
     override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
         onTranslationXChanged(translationXOut.toFloat())
-        val moveIn =
-            ValueAnimator.ofFloat(1f, 0f).apply {
-                duration = 23.frames
-                startDelay = 7.frames
-                interpolator = STATUS_BAR_X_MOVE_IN
-                addUpdateListener {
-                    onTranslationXChanged(translationXOut * animatedValue as Float)
-                }
-            }
-        val alphaIn =
-            ValueAnimator.ofFloat(0f, 1f).apply {
-                duration = 5.frames
-                startDelay = 11.frames
-                interpolator = null
-                addUpdateListener { onAlphaChanged(animatedValue as Float) }
-            }
+        val anim =
+            getDefaultStatusBarAnimationForChipExit(
+                translationXOut,
+                onTranslationXChanged,
+                onAlphaChanged,
+            )
+        anim.doOnEnd { isAnimationRunning = false }
+        anim.doOnCancel { isAnimationRunning = false }
 
-        val animatorSet = AnimatorSet()
-        animatorSet.playTogether(moveIn, alphaIn)
-        animatorSet.doOnEnd { isAnimationRunning = false }
-        animatorSet.doOnCancel { isAnimationRunning = false }
-        return animatorSet
+        return anim
+    }
+
+    /** Static definition of these animations so we can use them more easily from view binders */
+    companion object {
+        /**
+         * Chip: coming in. Animated view: going out.
+         *
+         * Implements the exact spec for animating any status bar elements OUT to make space for the
+         * chip IN animation.
+         */
+        fun getDefaultStatusBarAnimationForChipEnter(
+            targetTranslation: Int,
+            setX: (Float) -> Unit,
+            setAlpha: (Float) -> Unit,
+        ): Animator {
+            val moveOut =
+                ValueAnimator.ofFloat(0f, 1f).apply {
+                    duration = 23.frames
+                    interpolator = STATUS_BAR_X_MOVE_OUT
+                    addUpdateListener { setX(-(targetTranslation * animatedValue as Float)) }
+                }
+            val alphaOut =
+                ValueAnimator.ofFloat(1f, 0f).apply {
+                    duration = 8.frames
+                    interpolator = null
+                    addUpdateListener { setAlpha(animatedValue as Float) }
+                }
+
+            val animSet = AnimatorSet()
+            animSet.playTogether(moveOut, alphaOut)
+            return animSet
+        }
+
+        /**
+         * Chip: going out. Animated view: coming in.
+         *
+         * Implements the exact spec for animating any status bar elements IN as the chip is
+         * animating OUT
+         */
+        fun getDefaultStatusBarAnimationForChipExit(
+            targetTranslation: Int,
+            setX: (Float) -> Unit,
+            setAlpha: (Float) -> Unit,
+        ): Animator {
+            val moveIn =
+                ValueAnimator.ofFloat(1f, 0f).apply {
+                    duration = 23.frames
+                    startDelay = 7.frames
+                    interpolator = STATUS_BAR_X_MOVE_IN
+                    addUpdateListener { setX(targetTranslation * animatedValue as Float) }
+                }
+            val alphaIn =
+                ValueAnimator.ofFloat(0f, 1f).apply {
+                    duration = 5.frames
+                    startDelay = 11.frames
+                    interpolator = null
+                    addUpdateListener { setAlpha(animatedValue as Float) }
+                }
+
+            val animatorSet = AnimatorSet()
+            animatorSet.playTogether(moveIn, alphaIn)
+            return animatorSet
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 935b101..bfdc8bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -23,6 +23,8 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepository
+import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepositoryImpl
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -85,6 +87,11 @@
     abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
 
     @Binds
+    abstract fun systemStatusEventAnimationRepository(
+        impl: SystemStatusEventAnimationRepositoryImpl
+    ): SystemStatusEventAnimationRepository
+
+    @Binds
     abstract fun realDeviceBasedSatelliteRepository(
         impl: DeviceBasedSatelliteRepositoryImpl
     ): RealDeviceBasedSatelliteRepository
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 2177e02..72df027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -32,6 +32,10 @@
 import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
@@ -46,10 +50,16 @@
     /**
      * Binds the view to the view-model. [listener] will be notified whenever an event that may
      * change the status bar visibility occurs.
+     *
+     * Null chip animations are used when [StatusBarRootModernization] is off (i.e., when we are
+     * binding from the fragment). If non-null, they control the animation of the system icon area
+     * to support the chip animations.
      */
     fun bind(
         view: View,
         viewModel: HomeStatusBarViewModel,
+        systemEventChipAnimateIn: ((View) -> Unit)?,
+        systemEventChipAnimateOut: ((View) -> Unit)?,
         listener: StatusBarVisibilityChangeListener,
     )
 }
@@ -59,6 +69,8 @@
     override fun bind(
         view: View,
         viewModel: HomeStatusBarViewModel,
+        systemEventChipAnimateIn: ((View) -> Unit)?,
+        systemEventChipAnimateOut: ((View) -> Unit)?,
         listener: StatusBarVisibilityChangeListener,
     ) {
         view.repeatWhenAttached {
@@ -95,6 +107,7 @@
                                 when (primaryChipModel) {
                                     is OngoingActivityChipModel.Shown ->
                                         primaryChipView.show(shouldAnimateChange = true)
+
                                     is OngoingActivityChipModel.Hidden ->
                                         primaryChipView.hide(
                                             state = View.GONE,
@@ -109,6 +122,7 @@
                                             hasSecondaryOngoingActivity = false,
                                             shouldAnimate = true,
                                         )
+
                                     is OngoingActivityChipModel.Hidden ->
                                         listener.onOngoingActivityStatusChanged(
                                             hasPrimaryOngoingActivity = false,
@@ -175,10 +189,30 @@
                         view.requireViewById<View>(R.id.status_bar_end_side_content)
                     // TODO(b/364360986): Also handle operator name view.
                     launch {
-                        viewModel.isSystemInfoVisible.collect {
-                            systemInfoView.adjustVisibility(it)
-                            // TODO(b/364360986): The system info view has a custom alpha controller
-                            // in CollapsedStatusBarFragment.
+                        viewModel.systemInfoCombinedVis.collect { (baseVis, animState) ->
+                            // Broadly speaking, the baseVis controls the view.visibility, and
+                            // the animation state uses only alpha to achieve its effect. This
+                            // means that we can always modify the visibility, and if we're
+                            // animating we can use the animState to handle it. If we are not
+                            // animating, then we can use the baseVis default animation
+                            if (animState.isAnimatingChip()) {
+                                // Just apply the visibility of the view, but don't animate
+                                systemInfoView.visibility = baseVis.visibility
+                                // Now apply the animation state, with its animator
+                                when (animState) {
+                                    AnimatingIn -> {
+                                        systemEventChipAnimateIn?.invoke(systemInfoView)
+                                    }
+                                    AnimatingOut -> {
+                                        systemEventChipAnimateOut?.invoke(systemInfoView)
+                                    }
+                                    else -> {
+                                        // Nothing to do here
+                                    }
+                                }
+                            } else {
+                                systemInfoView.adjustVisibility(baseVis)
+                            }
                         }
                     }
                 }
@@ -186,6 +220,14 @@
         }
     }
 
+    private fun SystemEventAnimationState.isAnimatingChip() =
+        when (this) {
+            AnimatingIn,
+            AnimatingOut,
+            RunningChipAnim -> true
+            else -> false
+        }
+
     private fun OngoingActivityChipModel.toVisibilityModel(): VisibilityModel {
         return VisibilityModel(
             visibility = if (this is OngoingActivityChipModel.Shown) View.VISIBLE else View.GONE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index a9c2f72..1faa9f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
+import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
 import com.android.systemui.statusbar.phone.NotificationIconContainer
 import com.android.systemui.statusbar.phone.PhoneStatusBarView
@@ -59,6 +60,7 @@
     private val iconController: StatusBarIconController,
     private val ongoingCallController: OngoingCallController,
     private val darkIconDispatcherStore: DarkIconDispatcherStore,
+    private val eventAnimationInteractor: SystemStatusEventAnimationInteractor,
 ) {
     fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView {
         val composeView = ComposeView(root.context)
@@ -73,6 +75,7 @@
                     iconController = iconController,
                     ongoingCallController = ongoingCallController,
                     darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId),
+                    eventAnimationInteractor = eventAnimationInteractor,
                     onViewCreated = andThen,
                 )
             }
@@ -102,6 +105,7 @@
     iconController: StatusBarIconController,
     ongoingCallController: OngoingCallController,
     darkIconDispatcher: DarkIconDispatcher,
+    eventAnimationInteractor: SystemStatusEventAnimationInteractor,
     onViewCreated: (ViewGroup) -> Unit,
 ) {
     // None of these methods are used when [StatusBarRootModernization] is on.
@@ -181,6 +185,8 @@
                         statusBarViewBinder.bind(
                             phoneStatusBarView,
                             statusBarViewModel,
+                            eventAnimationInteractor::animateStatusBarContentForChipEnter,
+                            eventAnimationInteractor::animateStatusBarContentForChipExit,
                             nopVisibilityChangeListener,
                         )
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 4277a8b..6a9b43c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -35,6 +35,9 @@
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
+import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
@@ -95,7 +98,12 @@
 
     val isClockVisible: Flow<VisibilityModel>
     val isNotificationIconContainerVisible: Flow<VisibilityModel>
-    val isSystemInfoVisible: Flow<VisibilityModel>
+    /**
+     * Pair of (system info visibility, event animation state). The animation state can be used to
+     * respond to the system event chip animations. In all cases, system info visibility correctly
+     * models the View.visibility for the system info area
+     */
+    val systemInfoCombinedVis: StateFlow<SystemInfoCombinedVisibilityModel>
 
     /**
      * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
@@ -114,6 +122,12 @@
         /** True if a visibility change should be animated. */
         val shouldAnimateChange: Boolean,
     )
+
+    /** The combined visibility + animation state for the system info status bar area */
+    data class SystemInfoCombinedVisibilityModel(
+        val baseVisibility: VisibilityModel,
+        val animationState: SystemEventAnimationState,
+    )
 }
 
 @SysUISingleton
@@ -129,6 +143,7 @@
     sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
     shadeInteractor: ShadeInteractor,
     ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
+    animations: SystemStatusEventAnimationInteractor,
     @Application coroutineScope: CoroutineScope,
 ) : HomeStatusBarViewModel {
     override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
@@ -228,7 +243,7 @@
                 visibilityViaDisableFlags.animate,
             )
         }
-    override val isSystemInfoVisible: Flow<VisibilityModel> =
+    private val isSystemInfoVisible =
         combine(
             shouldHomeStatusBarBeVisible,
             collapsedStatusBarInteractor.visibilityViaDisableFlags,
@@ -238,6 +253,22 @@
             VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate)
         }
 
+    override val systemInfoCombinedVis =
+        combine(isSystemInfoVisible, animations.animationState) { sysInfoVisible, animationState ->
+                HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
+                    sysInfoVisible,
+                    animationState,
+                )
+            }
+            .stateIn(
+                coroutineScope,
+                SharingStarted.WhileSubscribed(),
+                HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
+                    VisibilityModel(View.VISIBLE, false),
+                    Idle,
+                ),
+            )
+
     @View.Visibility
     private fun Boolean.toVisibilityInt(): Int {
         return if (this) View.VISIBLE else View.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index b79d39e..36d64a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -39,7 +39,7 @@
 import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
 
 /**
- * Status bar view.
+ * Status bar view
  * We now extend WindowRootView so that we can host Compose views
  */
 public class StatusBarWindowView extends FrameLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 4fc9a7c..5c0cc81 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -18,9 +18,13 @@
 
 import android.app.Dialog
 import android.content.Context
+import android.graphics.PixelFormat
 import android.os.Bundle
 import android.view.MotionEvent
+import android.view.ViewGroup
+import android.view.WindowManager
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder
@@ -32,10 +36,34 @@
     @Application context: Context,
     private val viewBinder: VolumeDialogViewBinder,
     private val visibilityInteractor: VolumeDialogVisibilityInteractor,
-) : Dialog(context) {
+) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
+
+    init {
+        with(window!!) {
+            addFlags(
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+            )
+            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
+            setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+            setWindowAnimations(-1)
+            setFormat(PixelFormat.TRANSLUCENT)
+
+            attributes =
+                attributes.apply {
+                    title = "VolumeDialog" // Not the same as Window#setTitle
+                }
+            setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+        }
+        setCanceledOnTouchOutside(true)
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        setContentView(R.layout.volume_dialog)
         viewBinder.bind(this)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 78eabb2..d9a945c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -17,21 +17,22 @@
 package com.android.systemui.volume.dialog.ui.binder
 
 import android.app.Dialog
-import android.graphics.Color
-import android.graphics.PixelFormat
-import android.graphics.drawable.ColorDrawable
+import android.graphics.Rect
+import android.graphics.Region
 import android.view.Gravity
 import android.view.View
 import android.view.ViewGroup
-import android.view.Window
-import android.view.WindowManager
+import android.view.ViewTreeObserver
+import android.view.ViewTreeObserver.InternalInsetsInfo
+import android.widget.FrameLayout
+import androidx.annotation.GravityInt
 import com.android.internal.view.RotationPolicy
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.viewModel
 import com.android.systemui.res.R
+import com.android.systemui.util.children
 import com.android.systemui.volume.SystemUIInterpolators
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
 import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
 import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
@@ -52,6 +53,8 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
 
 /** Binds the root view of the Volume Dialog. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -61,65 +64,41 @@
 constructor(
     private val volumeResources: VolumeDialogResources,
     private val gravityViewModel: VolumeDialogGravityViewModel,
-    private val viewModelFactory: VolumeDialogViewModel.Factory,
+    private val dialogViewModelFactory: VolumeDialogViewModel.Factory,
     private val jankListenerFactory: JankListenerFactory,
     private val tracer: VolumeTracer,
-    @VolumeDialog private val coroutineScope: CoroutineScope,
     private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
     private val slidersViewBinder: VolumeDialogSlidersViewBinder,
     private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
 ) {
 
     fun bind(dialog: Dialog) {
-        setupDialog(dialog)
-        val view: View = dialog.requireViewById(R.id.volume_dialog_container)
-        view.alpha = 0f
-        view.repeatWhenAttached {
-            view.viewModel(
+        // Root view of the Volume Dialog.
+        val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root)
+        // Volume Dialog container view that contains the dialog itself without the floating sliders
+        val container: View = root.requireViewById(R.id.volume_dialog_container)
+        container.alpha = 0f
+        container.repeatWhenAttached {
+            root.viewModel(
                 traceName = "VolumeDialogViewBinder",
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
-                factory = { viewModelFactory.create() },
+                factory = { dialogViewModelFactory.create() },
             ) { viewModel ->
-                viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
+                animateVisibility(container, dialog, viewModel.dialogVisibilityModel)
 
-                animateVisibility(view, dialog, viewModel.dialogVisibilityModel)
+                viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
+                gravityViewModel.dialogGravity
+                    .onEach { container.setLayoutGravity(it) }
+                    .launchIn(this)
+
+                launch { root.viewTreeObserver.computeInternalInsetsListener(root) }
 
                 awaitCancellation()
             }
         }
-        volumeDialogRingerViewBinder.bind(view)
-        slidersViewBinder.bind(view)
-        settingsButtonViewBinder.bind(view)
-    }
-
-    /** Configures [Window] for the [Dialog]. */
-    private fun setupDialog(dialog: Dialog) {
-        with(dialog.window!!) {
-            clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
-            addFlags(
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
-                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
-                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
-            )
-            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
-
-            requestFeature(Window.FEATURE_NO_TITLE)
-            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-            setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
-            setWindowAnimations(-1)
-            setFormat(PixelFormat.TRANSLUCENT)
-
-            attributes =
-                attributes.apply {
-                    title = "VolumeDialog" // Not the same as Window#setTitle
-                }
-            setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-
-            gravityViewModel.dialogGravity.onEach { setGravity(it) }.launchIn(coroutineScope)
-        }
-        dialog.setContentView(R.layout.volume_dialog)
-        dialog.setCanceledOnTouchOutside(true)
+        volumeDialogRingerViewBinder.bind(root)
+        slidersViewBinder.bind(root)
+        settingsButtonViewBinder.bind(root)
     }
 
     private fun CoroutineScope.animateVisibility(
@@ -209,4 +188,33 @@
         }
         animator.suspendAnimate(jankListenerFactory.dismiss(this, duration))
     }
+
+    private suspend fun ViewTreeObserver.computeInternalInsetsListener(viewGroup: ViewGroup) =
+        suspendCancellableCoroutine<Unit> { continuation ->
+            val listener =
+                ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
+                    viewGroup.fillTouchableBounds(inoutInfo)
+                }
+            addOnComputeInternalInsetsListener(listener)
+            continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) }
+        }
+
+    private fun ViewGroup.fillTouchableBounds(internalInsetsInfo: InternalInsetsInfo) {
+        for (child in children) {
+            val boundsRect = Rect()
+            internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION)
+
+            child.getBoundsInWindow(boundsRect, false)
+            internalInsetsInfo.touchableRegion.op(boundsRect, Region.Op.UNION)
+        }
+        val boundsRect = Rect()
+        getBoundsInWindow(boundsRect, false)
+    }
+
+    private fun View.setLayoutGravity(@GravityInt newGravity: Int) {
+        val frameLayoutParams =
+            layoutParams as? FrameLayout.LayoutParams
+                ?: error("View must be a child of a FrameLayout")
+        layoutParams = frameLayoutParams.apply { gravity = newGravity }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fc318d5..856333e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -49,6 +49,7 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -2507,17 +2508,54 @@
 
     @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
     @Test
-    public void testEventLogging_bubbleBar_dragBubbleToDismiss() {
+    public void testEventLogging_bubbleBar_dragSelectedBubbleToDismiss() {
         mBubbleProperties.mIsBubbleBarEnabled = true;
         mPositioner.setIsLargeScreen(true);
         FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
         mBubbleController.registerBubbleStateListener(bubbleStateListener);
 
         mEntryListener.onEntryAdded(mRow);
-        mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0);
 
+        clearInvocations(mBubbleLogger);
+
+        // Dismiss selected bubble
+        mBubbleController.startBubbleDrag(mRow2.getKey());
+        mBubbleController.dragBubbleToDismiss(mRow2.getKey(), System.currentTimeMillis());
+
+        // Log bubble dismissed via drag and new bubble selected
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow2.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED));
+
+        verifyNoMoreInteractions(mBubbleLogger);
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_dragOtherBubbleToDismiss() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0);
+
+        clearInvocations(mBubbleLogger);
+
+        // Dismiss other bubble
+        mBubbleController.startBubbleDrag(mRow.getKey());
+        mBubbleController.dragBubbleToDismiss(mRow.getKey(), System.currentTimeMillis());
+
+        // Log bubble dismissed via drag, but no switch event
         verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
                 eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+
+        verifyNoMoreInteractions(mBubbleLogger);
     }
 
     @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@@ -2643,6 +2681,32 @@
                 eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
     }
 
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_switchBubble() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow.getKey(), 0);
+
+        // First select is expand
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+        verify(mBubbleLogger, never()).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED));
+
+        // Second select is switch
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0);
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow2.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED));
+        verify(mBubbleLogger, never()).log(eqBubbleWithKey(mRow2.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt
new file mode 100644
index 0000000..92eeef9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.statusbar.events.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import kotlinx.coroutines.flow.MutableStateFlow
+
+val Kosmos.systemStatusEventAnimationRepository: FakeSystemStatusEventAnimationRepository by
+    Kosmos.Fixture { FakeSystemStatusEventAnimationRepository() }
+
+class FakeSystemStatusEventAnimationRepository : SystemStatusEventAnimationRepository {
+    override val animationState = MutableStateFlow(SystemEventAnimationState.Idle)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt
new file mode 100644
index 0000000..7513fea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.statusbar.events.domain.interactor
+
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository
+
+val Kosmos.systemStatusEventAnimationInteractor by
+    Kosmos.Fixture {
+        SystemStatusEventAnimationInteractor(
+            repo = systemStatusEventAnimationRepository,
+            configurationInteractor = configurationInteractor,
+            scope = applicationCoroutineScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index 3a7ada2..03e4c89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
+import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor
 import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor
@@ -40,6 +41,7 @@
             sceneContainerOcclusionInteractor,
             shadeInteractor,
             ongoingActivityChipsViewModel,
+            systemStatusEventAnimationInteractor,
             applicationCoroutineScope,
         )
     }
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 3726ca9..b389a67 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -30,7 +30,7 @@
 EOF
 }
 
-source "${0%/*}"/../../common.sh
+source "${0%/*}"/../common.sh
 
 SCRIPT_NAME="${0##*/}"
 
@@ -61,7 +61,6 @@
 done
 shift $(($OPTIND - 1))
 
-
 # Build the dump files, which are the input of this test.
 run m  dump-jar tiny-framework-dump-test
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 0bd879b..8567ccb 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -187,6 +187,11 @@
     // Simple flag to enable/disable debug logging.
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
+    // String constants for XML schema migration related to changes in keyguard package.
+    private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
+    private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
+    private static final int KEYGUARD_HOST_ID = 0x4b455947;
+
     // Filename for app widgets state persisted on disk.
     private static final String STATE_FILENAME = "appwidgets.xml";
 
@@ -206,7 +211,6 @@
     private static final int UNKNOWN_USER_ID = -10;
 
     // Version of XML schema for app widgets. Bump if the stored widgets need to be upgraded.
-    // Version 1 introduced in 2014 - Android 5.0
     private static final int CURRENT_VERSION = 1;
 
     // Every widget update request is associated which an increasing sequence number. This is
@@ -607,7 +611,7 @@
                     // ... and see if these are hosts we've been awaiting.
                     // NOTE: We are backing up and restoring only the owner.
                     // TODO: http://b/22388012
-                    if (newPackageAdded && userId == mUserManager.getMainUser().getIdentifier()) {
+                    if (newPackageAdded && userId == UserHandle.USER_SYSTEM) {
                         final int uid = getUidForPackage(pkgName, userId);
                         if (uid >= 0 ) {
                             resolveHostUidLocked(pkgName, uid);
@@ -4424,11 +4428,19 @@
 
         int version = fromVersion;
 
-        // Update 1: From version 0 to 1, was used from Android 4 to Android 5. It updated the
-        // location of the keyguard widget database. No modern device will have db version 0.
+        // Update 1: keyguard moved from package "android" to "com.android.keyguard"
         if (version == 0) {
-            Slog.e(TAG, "Found widget database with version 0, this should not be possible,"
-                    + " forcing upgrade to version 1");
+            HostId oldHostId = new HostId(Process.myUid(),
+                    KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE);
+
+            Host host = lookupHostLocked(oldHostId);
+            if (host != null) {
+                final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
+                        UserHandle.USER_SYSTEM);
+                if (uid >= 0) {
+                    host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
+                }
+            }
 
             version = 1;
         }
@@ -4438,8 +4450,24 @@
         }
     }
 
+    private static File getStateFile(int userId) {
+        return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME);
+    }
+
     private static AtomicFile getSavedStateFile(int userId) {
-        return new AtomicFile(new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME));
+        File dir = Environment.getUserSystemDirectory(userId);
+        File settingsFile = getStateFile(userId);
+        if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            // Migrate old data
+            File oldFile = new File("/data/system/" + STATE_FILENAME);
+            // Method doesn't throw an exception on failure. Ignore any errors
+            // in moving the file (like non-existence)
+            oldFile.renameTo(settingsFile);
+        }
+        return new AtomicFile(settingsFile);
     }
 
     void onUserStopped(int userId) {
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 1dc3b73..bd46deb 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -22,3 +22,11 @@
     description: "Guards against Autofill-Credman Phase1 developer integration via new APIs"
     bug: "320730001"
 }
+
+flag {
+    name: "fill_dialog_improvements"
+    is_exported: true
+    namespace: "autofill"
+    description: "Improvements for Fill Dialog, including deprecation of pre-trigger API's"
+    bug: "336223371"
+}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 416c110..da5b1fd 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -101,7 +101,7 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 
-public final class CachedAppOptimizer {
+public class CachedAppOptimizer {
 
     // Flags stored in the DeviceConfig API.
     @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c067662..b84bf6b9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
@@ -155,6 +156,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -469,7 +471,6 @@
             }
             Process.setThreadPriority(tid, priority);
         }
-
     }
 
     // TODO(b/346822474): hook up global state usage.
@@ -499,7 +500,8 @@
     }
 
     OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
-            ServiceThread adjusterThread, GlobalState globalState, Injector injector) {
+            ServiceThread adjusterThread, GlobalState globalState,
+            CachedAppOptimizer cachedAppOptimizer, Injector injector) {
         mService = service;
         mGlobalState = globalState;
         mInjector = injector;
@@ -508,7 +510,7 @@
         mActiveUids = activeUids;
 
         mConstants = mService.mConstants;
-        mCachedAppOptimizer = new CachedAppOptimizer(mService);
+        mCachedAppOptimizer = cachedAppOptimizer;
         mCacheOomRanker = new CacheOomRanker(service);
 
         mLogger = new OomAdjusterDebugLogger(this, mService.mConstants);
@@ -2597,6 +2599,7 @@
         }
 
         capability |= getDefaultCapability(app, procState);
+        capability |= getCpuCapability(app, now);
 
         // Procstates below BFGS should never have this capability.
         if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
@@ -2739,8 +2742,12 @@
             if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
                     app.mOptRecord.shouldNotFreezeReason()
                     | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
-                // Bail out early, as we only care about the return value for a dryrun.
-                return true;
+                if (Flags.useCpuTimeCapability()) {
+                    // Do nothing, capability updated check will handle the dryrun output.
+                } else {
+                    // Bail out early, as we only care about the return value for a dryrun.
+                    return true;
+                }
             }
         }
 
@@ -2751,6 +2758,8 @@
         // we check the final procstate, and remove it if the procsate is below BFGS.
         capability |= getBfslCapabilityFromClient(client);
 
+        capability |= getCpuCapabilityFromClient(client);
+
         if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) {
             if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
                 capability |= cstate.getCurCapability();
@@ -2809,9 +2818,14 @@
                             app.mOptRecord.shouldNotFreezeReason()
                             | ProcessCachedOptimizerRecord
                             .SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT, mAdjSeq)) {
-                        // Bail out early, as we only care about the return value for a dryrun.
-                        return true;
+                        if (Flags.useCpuTimeCapability()) {
+                            // Do nothing, capability updated check will handle the dryrun output.
+                        } else {
+                            // Bail out early, as we only care about the return value for a dryrun.
+                            return true;
+                        }
                     }
+                    capability |= PROCESS_CAPABILITY_CPU_TIME;
                 }
                 // Not doing bind OOM management, so treat
                 // this guy more like a started service.
@@ -3053,9 +3067,14 @@
                         app.mOptRecord.shouldNotFreezeReason()
                         | ProcessCachedOptimizerRecord
                         .SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY, mAdjSeq)) {
-                    // Bail out early, as we only care about the return value for a dryrun.
-                    return true;
+                    if (Flags.useCpuTimeCapability()) {
+                        // Do nothing, capability updated check will handle the dryrun output.
+                    } else {
+                        // Bail out early, as we only care about the return value for a dryrun.
+                        return true;
+                    }
                 }
+                capability |= PROCESS_CAPABILITY_CPU_TIME;
             }
         }
         if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
@@ -3108,9 +3127,24 @@
             capability &= ~PROCESS_CAPABILITY_BFSL;
         }
         if (!updated) {
-            updated = adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup
-                || (capability != prevCapability
-                        && (capability & prevCapability) == prevCapability);
+            if (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup) {
+                updated = true;
+            }
+
+            if (Flags.useCpuTimeCapability()) {
+                if ((capability != prevCapability)
+                        && ((capability & prevCapability) == prevCapability)) {
+                    updated = true;
+                }
+            } else {
+                // Ignore PROCESS_CAPABILITY_CPU_TIME in capability comparison
+                final int curFiltered = capability & ~PROCESS_CAPABILITY_CPU_TIME;
+                final int prevFiltered = prevCapability & ~PROCESS_CAPABILITY_CPU_TIME;
+                if ((curFiltered != prevFiltered)
+                        && ((curFiltered & prevFiltered) == prevFiltered)) {
+                    updated = true;
+                }
+            }
         }
 
         if (dryRun) {
@@ -3186,6 +3220,8 @@
         // we check the final procstate, and remove it if the procsate is below BFGS.
         capability |= getBfslCapabilityFromClient(client);
 
+        capability |= getCpuCapabilityFromClient(client);
+
         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
             // If the other app is cached for any reason, for purposes here
             // we are going to consider it empty.
@@ -3196,8 +3232,12 @@
             if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
                     app.mOptRecord.shouldNotFreezeReason()
                     | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
-                // Bail out early, as we only care about the return value for a dryrun.
-                return true;
+                if (Flags.useCpuTimeCapability()) {
+                    // Do nothing, capability updated check will handle the dryrun output.
+                } else {
+                    // Bail out early, as we only care about the return value for a dryrun.
+                    return true;
+                }
             }
         }
 
@@ -3273,10 +3313,25 @@
             capability &= ~PROCESS_CAPABILITY_BFSL;
         }
 
-        if (dryRun && (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup
-                || (capability != prevCapability
-                        && (capability & prevCapability) == prevCapability))) {
-            return true;
+        if (dryRun) {
+            if (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup) {
+                return true;
+            }
+
+            if (Flags.useCpuTimeCapability()) {
+                if ((capability != prevCapability)
+                        && ((capability & prevCapability) == prevCapability)) {
+                    return true;
+                }
+            } else {
+                // Ignore PROCESS_CAPABILITY_CPU_TIME in capability comparison
+                final int curFiltered = capability & ~PROCESS_CAPABILITY_CPU_TIME;
+                final int prevFiltered = prevCapability & ~PROCESS_CAPABILITY_CPU_TIME;
+                if ((curFiltered != prevFiltered)
+                        && ((curFiltered & prevFiltered) == prevFiltered)) {
+                    return true;
+                }
+            }
         }
 
         if (adj < prevRawAdj) {
@@ -3328,6 +3383,29 @@
         return baseCapabilities | networkCapabilities;
     }
 
+    private static int getCpuCapability(ProcessRecord app, long nowUptime) {
+        final UidRecord uidRec = app.getUidRecord();
+        if (uidRec != null && uidRec.isCurAllowListed()) {
+            // Process has user visible activities.
+            return PROCESS_CAPABILITY_CPU_TIME;
+        }
+        if (UserHandle.isCore(app.uid)) {
+            // Make sure all system components are not frozen.
+            return PROCESS_CAPABILITY_CPU_TIME;
+        }
+        if (app.mState.getCachedHasVisibleActivities()) {
+            // Process has user visible activities.
+            return PROCESS_CAPABILITY_CPU_TIME;
+        }
+        if (app.mServices.hasUndemotedShortForegroundService(nowUptime)) {
+            // It running a short fgs, just give it cpu time.
+            return PROCESS_CAPABILITY_CPU_TIME;
+        }
+        // TODO(b/370817323): Populate this method with all of the reasons to keep a process
+        //  unfrozen.
+        return 0;
+    }
+
     /**
      * @return the BFSL capability from a client (of a service binding or provider).
      */
@@ -3376,6 +3454,15 @@
     }
 
     /**
+     * @return the CPU capability from a client (of a service binding or provider).
+     */
+    private static int getCpuCapabilityFromClient(ProcessRecord client) {
+        // Just grant CPU capability every time
+        // TODO(b/370817323): Populate with reasons to not propagate cpu capability across bindings.
+        return client.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME;
+    }
+
+    /**
      * Checks if for the given app and client, there's a cycle that should skip over the client
      * for now or use partial values to evaluate the effect of the client binding.
      * @param app
@@ -3955,6 +4042,39 @@
         mCacheOomRanker.dump(pw);
     }
 
+    /**
+     * Return whether or not a process should be frozen.
+     */
+    boolean getFreezePolicy(ProcessRecord proc) {
+        // Reasons to not freeze:
+        if (Flags.useCpuTimeCapability()) {
+            if ((proc.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+                /// App is important enough (see {@link #getCpuCapability}) or bound by something
+                /// important enough to not be frozen.
+                return false;
+            }
+        } else {
+            // The CPU capability handling covers all setShouldNotFreeze paths. Must check
+            // shouldNotFreeze, if the CPU capability is not being used.
+            if (proc.mOptRecord.shouldNotFreeze()) {
+                return false;
+            }
+        }
+
+        if (proc.mOptRecord.isFreezeExempt()) {
+            return false;
+        }
+
+        // Reasons to freeze:
+        if (proc.mState.getCurAdj() >= FREEZER_CUTOFF_ADJ) {
+            // Oomscore is in a high enough state, it is safe to freeze.
+            return true;
+        }
+
+        // Default, do not freeze a process.
+        return false;
+    }
+
     @GuardedBy({"mService", "mProcLock"})
     void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason,
             boolean immediate, int oldOomAdj) {
@@ -3969,43 +4089,44 @@
                     (state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ)
                     || oldOomAdj == UNKNOWN_ADJ;
             final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
-            if ((oomAdjChanged || shouldNotFreezeChanged)
+            final boolean hasCpuCapability =
+                    (PROCESS_CAPABILITY_CPU_TIME & app.mState.getCurCapability())
+                            == PROCESS_CAPABILITY_CPU_TIME;
+            final boolean usedToHaveCpuCapability =
+                    (PROCESS_CAPABILITY_CPU_TIME & app.mState.getSetCapability())
+                            == PROCESS_CAPABILITY_CPU_TIME;
+            final boolean cpuCapabilityChanged = hasCpuCapability != usedToHaveCpuCapability;
+            if ((oomAdjChanged || shouldNotFreezeChanged || cpuCapabilityChanged)
                     && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                 Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                         CachedAppOptimizer.ATRACE_FREEZER_TRACK,
                         "updateAppFreezeStateLSP " + app.processName
+                        + " pid: " + app.getPid()
                         + " isFreezeExempt: " + opt.isFreezeExempt()
                         + " isFrozen: " + opt.isFrozen()
                         + " shouldNotFreeze: " + opt.shouldNotFreeze()
                         + " shouldNotFreezeReason: " + opt.shouldNotFreezeReason()
                         + " curAdj: " + state.getCurAdj()
                         + " oldOomAdj: " + oldOomAdj
-                        + " immediate: " + immediate);
+                        + " immediate: " + immediate
+                        + " cpuCapability: " + hasCpuCapability);
             }
         }
 
-        if (app.mOptRecord.isFreezeExempt()) {
-            return;
-        }
-
-        // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
-        if (opt.isFrozen() && opt.shouldNotFreeze()) {
-            mCachedAppOptimizer.unfreezeAppLSP(app,
-                    CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
-            return;
-        }
-
-        // Use current adjustment when freezing, set adjustment when unfreezing.
-        if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen()
-                && !opt.shouldNotFreeze()) {
-            if (!immediate) {
-                mCachedAppOptimizer.freezeAppAsyncLSP(app);
-            } else {
+        if (getFreezePolicy(app)) {
+            // This process should be frozen.
+            if (immediate && !opt.isFrozen()) {
+                // And it will be frozen immediately.
                 mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app);
+            } else if (!opt.isFrozen() || !opt.isPendingFreeze()) {
+                mCachedAppOptimizer.freezeAppAsyncLSP(app);
             }
-        } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) {
-            mCachedAppOptimizer.unfreezeAppLSP(app,
-                    CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
+        } else {
+            // This process should not be frozen.
+            if (opt.isFrozen() || opt.isPendingFreeze()) {
+                mCachedAppOptimizer.unfreezeAppLSP(app,
+                        CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
+            }
         }
     }
 
@@ -4029,7 +4150,8 @@
         final int size = processes.size();
         for (int i = 0; i < size; i++) {
             ProcessRecord proc = processes.get(i);
-            mCachedAppOptimizer.unfreezeTemporarily(proc, reason);
+            mCachedAppOptimizer.unfreezeTemporarily(proc,
+                    CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(reason));
         }
         processes.clear();
     }
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 8b66055..1b7e8f0 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -758,8 +758,9 @@
 
     OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
             ActiveUids activeUids, ServiceThread adjusterThread, GlobalState globalState,
-            Injector injector) {
-        super(service, processList, activeUids, adjusterThread, globalState, injector);
+            CachedAppOptimizer cachedAppOptimizer, Injector injector) {
+        super(service, processList, activeUids, adjusterThread, globalState, cachedAppOptimizer,
+                injector);
     }
 
     private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes(
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 5cb8b95..3644974 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -256,18 +256,24 @@
         }
         // Now we need to look at all short-FGS within the process and see if all of them are
         // procstate-timed-out or not.
+        return !hasUndemotedShortForegroundService(nowUptime);
+    }
+
+    boolean hasUndemotedShortForegroundService(long nowUptime) {
         for (int i = mServices.size() - 1; i >= 0; i--) {
             final ServiceRecord sr = mServices.valueAt(i);
             if (!sr.isShortFgs() || !sr.hasShortFgsInfo()) {
                 continue;
             }
             if (sr.getShortFgsInfo().getProcStateDemoteTime() >= nowUptime) {
-                return false;
+                // This short fgs has not timed out yet.
+                return true;
             }
         }
-        return true;
+        return false;
     }
 
+
     int getReportedForegroundServiceTypes() {
         return mRepFgServiceTypes;
     }
diff --git a/services/core/java/com/android/server/am/ProcessStateController.java b/services/core/java/com/android/server/am/ProcessStateController.java
index 01468c6..5789922 100644
--- a/services/core/java/com/android/server/am/ProcessStateController.java
+++ b/services/core/java/com/android/server/am/ProcessStateController.java
@@ -29,6 +29,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.ServiceThread;
 
 /**
@@ -44,13 +45,14 @@
     private final GlobalState mGlobalState = new GlobalState();
 
     private ProcessStateController(ActivityManagerService ams, ProcessList processList,
-            ActiveUids activeUids, ServiceThread handlerThread, OomAdjuster.Injector oomAdjInjector,
+            ActiveUids activeUids, ServiceThread handlerThread,
+            CachedAppOptimizer cachedAppOptimizer, OomAdjuster.Injector oomAdjInjector,
             boolean useOomAdjusterModernImpl) {
         mOomAdjuster = useOomAdjusterModernImpl
                 ? new OomAdjusterModernImpl(ams, processList, activeUids, handlerThread,
-                mGlobalState, oomAdjInjector)
+                mGlobalState, cachedAppOptimizer, oomAdjInjector)
                 : new OomAdjuster(ams, processList, activeUids, handlerThread, mGlobalState,
-                        oomAdjInjector);
+                        cachedAppOptimizer, oomAdjInjector);
     }
 
     /**
@@ -594,6 +596,7 @@
         private final ActiveUids mActiveUids;
 
         private ServiceThread mHandlerThread = null;
+        private CachedAppOptimizer mCachedAppOptimizer = null;
         private OomAdjuster.Injector mOomAdjInjector = null;
         private boolean mUseOomAdjusterModernImpl = false;
 
@@ -610,24 +613,38 @@
             if (mHandlerThread == null) {
                 mHandlerThread = OomAdjuster.createAdjusterThread();
             }
+            if (mCachedAppOptimizer == null) {
+                mCachedAppOptimizer = new CachedAppOptimizer(mAms);
+            }
             if (mOomAdjInjector == null) {
                 mOomAdjInjector = new OomAdjuster.Injector();
             }
             return new ProcessStateController(mAms, mProcessList, mActiveUids, mHandlerThread,
-                    mOomAdjInjector, mUseOomAdjusterModernImpl);
+                    mCachedAppOptimizer, mOomAdjInjector, mUseOomAdjusterModernImpl);
         }
 
         /**
          * For Testing Purposes. Set what thread OomAdjuster will offload tasks on to.
          */
+        @VisibleForTesting
         public Builder setHandlerThread(ServiceThread handlerThread) {
             mHandlerThread = handlerThread;
             return this;
         }
 
         /**
+         * For Testing Purposes. Set the CachedAppOptimzer used by OomAdjuster.
+         */
+        @VisibleForTesting
+        public Builder setCachedAppOptimizer(CachedAppOptimizer cachedAppOptimizer) {
+            mCachedAppOptimizer = cachedAppOptimizer;
+            return this;
+        }
+
+        /**
          * For Testing Purposes. Set an injector for OomAdjuster.
          */
+        @VisibleForTesting
         public Builder setOomAdjusterInjector(OomAdjuster.Injector injector) {
             mOomAdjInjector = injector;
             return this;
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 8395685..711b163 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -253,3 +253,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "use_cpu_time_capability"
+    namespace: "backstage_power"
+    description: "Use PROCESS_CAPABILITY_CPU_TIME to control unfreeze state."
+    bug: "370817323"
+}
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index e8fa417..afdc0c0 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -119,9 +119,16 @@
                 == BiometricManager.Authenticators.IDENTITY_CHECK;
         boolean isMandatoryBiometricsAuthentication = false;
 
+        final int effectiveUserId;
+        if (Flags.effectiveUserBp()) {
+            effectiveUserId = userManager.getCredentialOwnerProfile(userId);
+        } else {
+            effectiveUserId = userId;
+        }
+
         if (dropCredentialFallback(promptInfo.getAuthenticators(),
                 settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
-                        userId), trustManager)) {
+                        effectiveUserId), trustManager)) {
             isMandatoryBiometricsAuthentication = true;
             promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
             if (promptInfo.getNegativeButtonText() == null) {
@@ -145,13 +152,6 @@
         final List<BiometricSensor> eligibleSensors = new ArrayList<>();
         final List<Pair<BiometricSensor, Integer>> ineligibleSensors = new ArrayList<>();
 
-        final int effectiveUserId;
-        if (Flags.effectiveUserBp()) {
-            effectiveUserId = userManager.getCredentialOwnerProfile(userId);
-        } else {
-            effectiveUserId = userId;
-        }
-
         if (biometricRequested) {
             for (BiometricSensor sensor : sensors) {
 
diff --git a/services/core/java/com/android/server/integrity/model/BitInputStream.java b/services/core/java/com/android/server/integrity/model/BitInputStream.java
deleted file mode 100644
index e7cc81e..0000000
--- a/services/core/java/com/android/server/integrity/model/BitInputStream.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.model;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/** A wrapper class for reading a stream of bits.
- *
- * <p>Note: this class reads from underlying stream byte-by-byte. It is advised to apply buffering
- * to underlying streams.
- */
-public class BitInputStream {
-
-    private long mBitsRead;
-
-    private InputStream mInputStream;
-
-    private byte mCurrentByte;
-
-    public BitInputStream(InputStream inputStream) {
-        mInputStream = inputStream;
-    }
-
-    /**
-     * Read the next number of bits from the stream.
-     *
-     * @param numOfBits The number of bits to read.
-     * @return The value read from the stream.
-     */
-    public int getNext(int numOfBits) throws IOException {
-        int component = 0;
-        int count = 0;
-
-        while (count++ < numOfBits) {
-            if (mBitsRead % 8 == 0) {
-                mCurrentByte = getNextByte();
-            }
-            int offset = 7 - (int) (mBitsRead % 8);
-
-            component <<= 1;
-            component |= (mCurrentByte >>> offset) & 1;
-
-            mBitsRead++;
-        }
-
-        return component;
-    }
-
-    /** Check if there are bits left in the stream. */
-    public boolean hasNext() throws IOException {
-        return mInputStream.available() > 0;
-    }
-
-    private byte getNextByte() throws IOException {
-        return (byte) mInputStream.read();
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
deleted file mode 100644
index 14b35fd..0000000
--- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.model;
-
-import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-/** A wrapper class for writing a stream of bits. */
-public class BitOutputStream {
-
-    private static final int BUFFER_SIZE = 4 * 1024;
-
-    private int mNextBitIndex;
-
-    private final OutputStream mOutputStream;
-    private final byte[] mBuffer;
-
-    public BitOutputStream(OutputStream outputStream) {
-        mBuffer = new byte[BUFFER_SIZE];
-        mNextBitIndex = 0;
-        mOutputStream = outputStream;
-    }
-
-    /**
-     * Set the next number of bits in the stream to value.
-     *
-     * @param numOfBits The number of bits used to represent the value.
-     * @param value The value to convert to bits.
-     */
-    public void setNext(int numOfBits, int value) throws IOException {
-        if (numOfBits <= 0) {
-            return;
-        }
-
-        // optional: we can do some clever size checking to "OR" an entire segment of bits instead
-        // of setting bits one by one, but it is probably not worth it.
-        int nextBitMask = 1 << (numOfBits - 1);
-        while (numOfBits-- > 0) {
-            setNext((value & nextBitMask) != 0);
-            nextBitMask >>>= 1;
-        }
-    }
-
-    /**
-     * Set the next bit in the stream to value.
-     *
-     * @param value The value to set the bit to
-     */
-    public void setNext(boolean value) throws IOException {
-        int byteToWrite = mNextBitIndex / BYTE_BITS;
-        if (byteToWrite == BUFFER_SIZE) {
-            mOutputStream.write(mBuffer);
-            reset();
-            byteToWrite = 0;
-        }
-        if (value) {
-            mBuffer[byteToWrite] |= 1 << (BYTE_BITS - 1 - (mNextBitIndex % BYTE_BITS));
-        }
-        mNextBitIndex++;
-    }
-
-    /** Set the next bit in the stream to true. */
-    public void setNext() throws IOException {
-        setNext(/* value= */ true);
-    }
-
-    /**
-     * Flush the data written to the underlying {@link java.io.OutputStream}. Any unfinished bytes
-     * will be padded with 0.
-     */
-    public void flush() throws IOException {
-        int endByte = mNextBitIndex / BYTE_BITS;
-        if (mNextBitIndex % BYTE_BITS != 0) {
-            // If next bit is not the first bit of a byte, then mNextBitIndex / BYTE_BITS would be
-            // the byte that includes already written bits. We need to increment it so this byte
-            // gets written.
-            endByte++;
-        }
-        mOutputStream.write(mBuffer, 0, endByte);
-        reset();
-    }
-
-    /** Reset this output stream to start state. */
-    private void reset() {
-        mNextBitIndex = 0;
-        Arrays.fill(mBuffer, (byte) 0);
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
deleted file mode 100644
index ceed054..0000000
--- a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.model;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An output stream that tracks the total number written bytes since construction and allows
- * querying this value any time during the execution.
- *
- * <p>This class is used for constructing the rule indexing.
- */
-public class ByteTrackedOutputStream extends OutputStream {
-
-    private int mWrittenBytesCount;
-    private final OutputStream mOutputStream;
-
-    public ByteTrackedOutputStream(OutputStream outputStream) {
-        mWrittenBytesCount = 0;
-        mOutputStream = outputStream;
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        mWrittenBytesCount++;
-        mOutputStream.write(b);
-    }
-
-    /**
-     * Writes the given bytes into the output stream provided in constructor and updates the total
-     * number of written bytes.
-     */
-    @Override
-    public void write(byte[] bytes) throws IOException {
-        write(bytes, 0, bytes.length);
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        mWrittenBytesCount += len;
-        mOutputStream.write(b, off, len);
-    }
-
-    /** Returns the total number of bytes written into the output stream at the requested time. */
-    public int getWrittenBytesCount() {
-        return mWrittenBytesCount;
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
deleted file mode 100644
index 94e6708..0000000
--- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.model;
-
-import android.content.integrity.Rule;
-
-/**
- * A helper class containing information about the binary representation of different {@link Rule}
- * components.
- */
-public final class ComponentBitSize {
-    public static final int FORMAT_VERSION_BITS = 8;
-
-    public static final int EFFECT_BITS = 3;
-    public static final int KEY_BITS = 4;
-    public static final int OPERATOR_BITS = 3;
-    public static final int CONNECTOR_BITS = 2;
-    public static final int SEPARATOR_BITS = 3;
-    public static final int VALUE_SIZE_BITS = 8;
-    public static final int IS_HASHED_BITS = 1;
-
-    public static final int ATOMIC_FORMULA_START = 0;
-    public static final int COMPOUND_FORMULA_START = 1;
-    public static final int COMPOUND_FORMULA_END = 2;
-    public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3;
-
-    public static final int DEFAULT_FORMAT_VERSION = 1;
-    public static final int SIGNAL_BIT = 1;
-
-    public static final int BYTE_BITS = 8;
-}
diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
deleted file mode 100644
index 0c4052a..0000000
--- a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.model;
-
-/**  A helper class containing special indexing file constants. */
-public final class IndexingFileConstants {
-    // We empirically experimented with different block sizes and identified that 50 is in the
-    // optimal range of efficient computation.
-    public static final int INDEXING_BLOCK_SIZE = 50;
-
-    public static final String START_INDEXING_KEY = "START_KEY";
-    public static final String END_INDEXING_KEY = "END_KEY";
-}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java b/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java
new file mode 100644
index 0000000..04f6216
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java
@@ -0,0 +1,64 @@
+/*
+ * 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.media.quality;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.media.quality.MediaQualityContract.BaseParameters;
+
+public class MediaQualityDbHelper extends SQLiteOpenHelper {
+
+    private static final String TAG = "MediaQualityDbHelper";
+
+    static final int DATABASE_VERSION_1 = 1;
+    private static final String DATABASE_NAME = "media_quality.db";
+    public static final String PICTURE_QUALITY_TABLE_NAME = "picture_quality";
+    public static final String SOUND_QUALITY_TABLE_NAME = "sound_quality";
+    public static final String SETTINGS = "settings";
+
+    MediaQualityDbHelper(Context context) {
+        super(context, DATABASE_NAME, null, getDbVersion());
+    }
+
+    private static int getDbVersion() {
+        return DATABASE_VERSION_1;
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        db.execSQL(getTableCreateStatement(PICTURE_QUALITY_TABLE_NAME));
+        db.execSQL(getTableCreateStatement(SOUND_QUALITY_TABLE_NAME));
+    }
+
+    private String getTableCreateStatement(String tableName) {
+        return
+                "CREATE TABLE " + tableName + "("
+                        + BaseParameters.PARAMETER_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+                        + BaseParameters.PARAMETER_TYPE + " INTEGER,"
+                        + BaseParameters.PARAMETER_NAME + " STRING,"
+                        + BaseParameters.PARAMETER_PACKAGE + " STRING,"
+                        + BaseParameters.PARAMETER_INPUT_ID + " STRING,"
+                        + SETTINGS + " TEXT)";
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        // to do
+    }
+
+}
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 21ae182..84413d5 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -16,20 +16,31 @@
 
 package com.android.server.media.quality;
 
+import android.content.ContentValues;
 import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.media.quality.AmbientBacklightSettings;
 import android.media.quality.IAmbientBacklightCallback;
 import android.media.quality.IMediaQualityManager;
 import android.media.quality.IPictureProfileCallback;
 import android.media.quality.ISoundProfileCallback;
+import android.media.quality.MediaQualityContract.PictureQuality;
 import android.media.quality.ParamCapability;
 import android.media.quality.PictureProfile;
 import android.media.quality.SoundProfile;
+import android.os.Bundle;
+import android.util.Log;
 
 import com.android.server.SystemService;
 
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * This service manage picture profile and sound profile for TV setting. Also communicates with the
@@ -40,10 +51,14 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "MediaQualityService";
     private final Context mContext;
+    private final MediaQualityDbHelper mMediaQualityDbHelper;
 
     public MediaQualityService(Context context) {
         super(context);
         mContext = context;
+        mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
+        mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
+        mMediaQualityDbHelper.setIdleConnectionTimeout(30);
     }
 
     @Override
@@ -53,11 +68,23 @@
 
     // TODO: Add additional APIs. b/373951081
     private final class BinderService extends IMediaQualityManager.Stub {
+
         @Override
         public PictureProfile createPictureProfile(PictureProfile pp) {
-            // TODO: implement
-            return pp;
+            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+
+            ContentValues values = new ContentValues();
+            values.put(PictureQuality.PARAMETER_TYPE, pp.getProfileType());
+            values.put(PictureQuality.PARAMETER_NAME, pp.getName());
+            values.put(PictureQuality.PARAMETER_PACKAGE, pp.getPackageName());
+            values.put(PictureQuality.PARAMETER_INPUT_ID, pp.getInputId());
+            values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
+
+            // id is auto-generated by SQLite upon successful insertion of row
+            long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values);
+            return new PictureProfile.Builder(pp).setProfileId(Long.toString(id)).build();
         }
+
         @Override
         public void updatePictureProfile(String id, PictureProfile pp) {
             // TODO: implement
@@ -66,10 +93,58 @@
         public void removePictureProfile(String id) {
             // TODO: implement
         }
+
         @Override
         public PictureProfile getPictureProfile(int type, String name) {
             return null;
         }
+
+        private String bundleToJson(Bundle bundle) {
+            JSONObject jsonObject = new JSONObject();
+            if (bundle == null) {
+                return jsonObject.toString();
+            }
+            for (String key : bundle.keySet()) {
+                try {
+                    jsonObject.put(key, bundle.getString(key));
+                } catch (JSONException e) {
+                    Log.e(TAG, "Unable to serialize ", e);
+                }
+            }
+            return jsonObject.toString();
+        }
+
+        private Bundle jsonToBundle(String jsonString) {
+            JSONObject jsonObject = null;
+            Bundle bundle = new Bundle();
+
+            try {
+                jsonObject = new JSONObject(jsonString);
+
+                Iterator<String> keys = jsonObject.keys();
+                while (keys.hasNext()) {
+                    String key = keys.next();
+                    Object value = jsonObject.get(key);
+
+                    if (value instanceof String) {
+                        bundle.putString(key, (String) value);
+                    } else if (value instanceof Integer) {
+                        bundle.putInt(key, (Integer) value);
+                    } else if (value instanceof Boolean) {
+                        bundle.putBoolean(key, (Boolean) value);
+                    } else if (value instanceof Double) {
+                        bundle.putDouble(key, (Double) value);
+                    } else if (value instanceof Long) {
+                        bundle.putLong(key, (Long) value);
+                    }
+                }
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+
+            return bundle;
+        }
+
         @Override
         public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
             return new ArrayList<>();
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index fa2c716..e8eae4f 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -70,15 +70,8 @@
         mAppCompatAspectRatioState.reset();
     }
 
-    float getDesiredAspectRatio(@NonNull Configuration newParentConfig,
+    private float getDesiredAspectRatio(@NonNull Configuration newParentConfig,
             @NonNull Rect parentBounds) {
-        // If in camera compat mode, aspect ratio from the camera compat policy has priority over
-        // default letterbox aspect ratio.
-        if (AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
-                mActivityRecord)) {
-            return AppCompatCameraPolicy.getCameraCompatAspectRatio(mActivityRecord);
-        }
-
         final float letterboxAspectRatioOverride =
                 mAppCompatOverrides.getAppCompatAspectRatioOverrides()
                         .getFixedOrientationLetterboxAspectRatio(newParentConfig);
@@ -120,7 +113,16 @@
         if (mTransparentPolicy.isRunning()) {
             return mTransparentPolicy.getInheritedMinAspectRatio();
         }
+
         final ActivityInfo info = mActivityRecord.info;
+
+        // If in camera compat mode, aspect ratio from the camera compat policy has priority over
+        // the default aspect ratio.
+        if (AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(mActivityRecord)) {
+            return Math.max(AppCompatCameraPolicy.getCameraCompatMinAspectRatio(mActivityRecord),
+                    info.getMinAspectRatio());
+        }
+
         final AppCompatAspectRatioOverrides aspectRatioOverrides =
                 mAppCompatOverrides.getAppCompatAspectRatioOverrides();
         if (aspectRatioOverrides.shouldApplyUserMinAspectRatioOverride()) {
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 8c5689c1..8be66cc 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -234,7 +234,7 @@
     }
 
     // TODO(b/369070416): have policies implement the same interface.
-    static float getCameraCompatAspectRatio(@NonNull ActivityRecord activity) {
+    static float getCameraCompatMinAspectRatio(@NonNull ActivityRecord activity) {
         final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity);
         if (cameraPolicy == null) {
             return 1.0f;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 9841058..c19c58e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -99,10 +99,11 @@
 final class DevicePolicyEngine {
     static final String TAG = "DevicePolicyEngine";
 
-    // TODO(b/281701062): reference role name from role manager once its exposed.
     static final String DEVICE_LOCK_CONTROLLER_ROLE =
             "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
 
+    static final String SYSTEM_SUPERVISION_ROLE = "android.app.role.SYSTEM_SUPERVISION";
+
     private static final String CELLULAR_2G_USER_RESTRICTION_ID =
             DevicePolicyIdentifiers.getIdentifierForUserRestriction(
                     UserManager.DISALLOW_CELLULAR_2G);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c653038..d221e8c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9068,35 +9068,14 @@
             return;
         }
 
-        CallerIdentity caller;
-        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who);
 
-        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
-            // The effect of this policy is device-wide.
-            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    who,
-                    SET_TIME_ZONE,
-                    caller.getPackageName(),
-                    UserHandle.USER_ALL
-            );
-            mDevicePolicyEngine.setGlobalPolicy(
-                    PolicyDefinition.AUTO_TIMEZONE,
-                    // TODO(b/260573124): add correct enforcing admin when permission changes are
-                    //  merged.
-                    enforcingAdmin,
-                    new BooleanPolicyValue(enabled));
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
-                    || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
-                    caller));
-            mInjector.binderWithCleanCallingIdentity(() ->
-                    mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
-        }
+        Objects.requireNonNull(who, "ComponentName is null");
+        Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+                || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+                caller));
+        mInjector.binderWithCleanCallingIdentity(() ->
+                mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE)
@@ -9114,26 +9093,70 @@
             return false;
         }
 
-        CallerIdentity caller;
-        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
-
-        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
-            // The effect of this policy is device-wide.
-            enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
-                    || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
-                    caller));
-        }
-
+        CallerIdentity caller = getCallerIdentity(who);
+        Objects.requireNonNull(who, "ComponentName is null");
+        Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+                || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+                caller));
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
     }
 
+    /**
+     * Set auto time zone state.
+     */
+    public void setAutoTimeZonePolicy(String callerPackageName, int policy) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        CallerIdentity caller = getCallerIdentity(callerPackageName);
+        // The effect of this policy is device-wide.
+        EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                /* who */ null,
+                SET_TIME_ZONE,
+                caller.getPackageName(),
+                UserHandle.USER_ALL
+        );
+
+        if (policy != DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY) {
+            mDevicePolicyEngine.setGlobalPolicy(
+                    PolicyDefinition.AUTO_TIME_ZONE,
+                    enforcingAdmin,
+                    new IntegerPolicyValue(policy));
+
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE)
+                    .setAdmin(caller.getPackageName())
+                    .setBoolean(policy == DevicePolicyManager.AUTO_TIME_ZONE_ENABLED)
+                    .write();
+        } else {
+            mDevicePolicyEngine.removeGlobalPolicy(
+                    PolicyDefinition.AUTO_TIME_ZONE,
+                    enforcingAdmin);
+        }
+    }
+
+    /**
+     * Returns whether auto time zone is used on the device or not.
+     */
+    @Override
+    public int getAutoTimeZonePolicy(String callerPackageName) {
+        if (!mHasFeature) {
+            return DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY;
+        }
+        CallerIdentity caller = getCallerIdentity(callerPackageName);
+        // The effect of this policy is device-wide.
+        EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                /* who */ null,
+                SET_TIME_ZONE,
+                caller.getPackageName(),
+                UserHandle.USER_ALL
+        );
+        Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+                PolicyDefinition.AUTO_TIME_ZONE, enforcingAdmin);
+        return state != null ? state : DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY;
+    }
+
     // TODO (b/137101239): remove this method in follow-up CL
     // since it's only used for split system user.
     @Override
@@ -21030,6 +21053,27 @@
     }
 
     @Override
+    public boolean removeManagedProfile(int userId) {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        if (!isManagedProfile(userId)){
+            throw new IllegalArgumentException("Cannot remove user as it is not a managed profile");
+        }
+
+        boolean success = false;
+        final long identity = Binder.clearCallingIdentity();
+        try{
+            success = mUserManager.removeUserEvenWhenDisallowed(userId);
+        } catch (Exception e) {
+            Slogf.e(LOG_TAG, "Remove managed profile failed due to: ", e);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return success;
+    }
+
+    @Override
     public UserHandle createAndProvisionManagedProfile(
             @NonNull ManagedProfileProvisioningParams provisioningParams,
             @NonNull String callerPackage) {
@@ -23765,9 +23809,6 @@
                     Slogf.i(LOG_TAG,
                             "Started device policies migration to the device policy engine.");
                     // TODO(b/359188869): Move this to the current migration method.
-                    if (Flags.setAutoTimeZoneEnabledCoexistence()) {
-                        migrateAutoTimezonePolicy();
-                    }
                     if (Flags.setPermissionGrantStateCoexistence()) {
                         migratePermissionGrantStatePolicies();
                     }
@@ -23816,11 +23857,6 @@
         // Additional migration steps should repeat the pattern above with a new backupId.
     }
 
-    private void migrateAutoTimezonePolicy() {
-        Slogf.i(LOG_TAG, "Skipping Migration of AUTO_TIMEZONE policy to device policy engine,"
-                + "as no way to identify if the value was set by the admin or the user.");
-    }
-
     private void migratePermissionGrantStatePolicies() {
         Slogf.i(LOG_TAG, "Migrating PERMISSION_GRANT policy to device policy engine.");
         for (UserInfo userInfo : mUserManager.getUsers()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index f1711f5..a5aeaac 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import static com.android.server.devicepolicy.DevicePolicyEngine.DEVICE_LOCK_CONTROLLER_ROLE;
+import static com.android.server.devicepolicy.DevicePolicyEngine.SYSTEM_SUPERVISION_ROLE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -94,14 +95,18 @@
     private static final MostRestrictive<Boolean> TRUE_MORE_RESTRICTIVE = new MostRestrictive<>(
             List.of(new BooleanPolicyValue(true), new BooleanPolicyValue(false)));
 
-    static PolicyDefinition<Boolean> AUTO_TIMEZONE = new PolicyDefinition<>(
+    static PolicyDefinition<Integer> AUTO_TIME_ZONE = new PolicyDefinition<>(
             new NoArgsPolicyKey(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY),
-            // auto timezone is disabled by default, hence enabling it is more restrictive.
-            TRUE_MORE_RESTRICTIVE,
+            // Auto time zone is enabled by default. Enabled state has higher priority given it
+            // means the time will be more precise and other applications can rely on that for
+            // their purposes.
+            new TopPriority<>(List.of(
+                    EnforcingAdmin.getRoleAuthorityOf(SYSTEM_SUPERVISION_ROLE),
+                    EnforcingAdmin.getRoleAuthorityOf(DEVICE_LOCK_CONTROLLER_ROLE),
+                    EnforcingAdmin.DPC_AUTHORITY)),
             POLICY_FLAG_GLOBAL_ONLY_POLICY,
-            (Boolean value, Context context, Integer userId, PolicyKey policyKey) ->
-                    PolicyEnforcerCallbacks.setAutoTimezoneEnabled(value, context),
-            new BooleanPolicySerializer());
+            PolicyEnforcerCallbacks::setAutoTimeZonePolicy,
+            new IntegerPolicySerializer());
 
     static final PolicyDefinition<Integer> GENERIC_PERMISSION_GRANT =
             new PolicyDefinition<>(
@@ -349,7 +354,7 @@
 
     // TODO(b/277218360): Revisit policies that should be marked as global-only.
     static {
-        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIME_ZONE);
         POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
                 GENERIC_PERMISSION_GRANT);
         POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.SECURITY_LOGGING_POLICY,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index fdc0ec1..40d8dae 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -81,20 +81,24 @@
         return AndroidFuture.completedFuture(true);
     }
 
-    static CompletableFuture<Boolean> setAutoTimezoneEnabled(@Nullable Boolean enabled,
-            @NonNull Context context) {
+    static CompletableFuture<Boolean> setAutoTimeZonePolicy(
+            @Nullable Integer policy, @NonNull Context context, int userId,
+            @NonNull PolicyKey policyKey) {
         if (!Flags.setAutoTimeZoneEnabledCoexistence()) {
-            Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off.");
+            Slogf.w(LOG_TAG, "Trying to enforce setAutoTimeZonePolicy while flag is off.");
             return AndroidFuture.completedFuture(true);
         }
         return Binder.withCleanCallingIdentity(() -> {
             Objects.requireNonNull(context);
-
-            int value = enabled != null && enabled ? 1 : 0;
-            return AndroidFuture.completedFuture(
-                    Settings.Global.putInt(
-                            context.getContentResolver(), Settings.Global.AUTO_TIME_ZONE,
-                    value));
+            if (policy != null &&
+                    policy == DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY) {
+                return AndroidFuture.completedFuture(false);
+            }
+            int enabled = policy != null &&
+                    policy == DevicePolicyManager.AUTO_TIME_ZONE_ENABLED ? 1 : 0;
+            return AndroidFuture.completedFuture(Settings.Global.putInt(
+                    context.getContentResolver(), Settings.Global.AUTO_TIME_ZONE,
+                    enabled));
         });
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 4a131558..f82a860 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -109,6 +109,7 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.android.server.LocalServices;
@@ -175,6 +176,7 @@
     private ActiveUids mActiveUids;
     private PackageManagerInternal mPackageManagerInternal;
     private ActivityManagerService mService;
+    private TestCachedAppOptimizer mTestCachedAppOptimizer;
     private OomAdjusterInjector mInjector = new OomAdjusterInjector();
 
     private int mUiTierSize;
@@ -242,9 +244,11 @@
         doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(),
                 anyBoolean());
         mActiveUids = new ActiveUids(mService, false);
+        mTestCachedAppOptimizer = new TestCachedAppOptimizer(mService);
         mProcessStateController = new ProcessStateController.Builder(mService,
                 mService.mProcessList, mActiveUids)
                 .useModernOomAdjuster(mService.mConstants.ENABLE_NEW_OOMADJ)
+                .setCachedAppOptimizer(mTestCachedAppOptimizer)
                 .setOomAdjusterInjector(mInjector)
                 .build();
         mService.mProcessStateController = mProcessStateController;
@@ -3110,13 +3114,13 @@
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
 
         assertEquals(true, app.getUidRecord().isSetAllowListed());
-        assertEquals(true, app.mOptRecord.shouldNotFreeze());
-        assertEquals(true, app2.mOptRecord.shouldNotFreeze());
+        assertFreezeState(app, false);
+        assertFreezeState(app2, false);
 
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
-        assertEquals(false, app.mOptRecord.shouldNotFreeze());
-        assertEquals(false, app2.mOptRecord.shouldNotFreeze());
+        assertFreezeState(app, true);
+        assertFreezeState(app2, true);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -3138,25 +3142,25 @@
 
         assertEquals(true, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app2.getUidRecord().isSetAllowListed());
-        assertEquals(true, app.mOptRecord.shouldNotFreeze());
-        assertEquals(true, app2.mOptRecord.shouldNotFreeze());
-        assertEquals(true, app3.mOptRecord.shouldNotFreeze());
+        assertFreezeState(app, false);
+        assertFreezeState(app2, false);
+        assertFreezeState(app3, false);
 
         // Remove app1 from allowlist.
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app2.getUidRecord().isSetAllowListed());
-        assertEquals(false, app.mOptRecord.shouldNotFreeze());
-        assertEquals(true, app2.mOptRecord.shouldNotFreeze());
-        assertEquals(true, app3.mOptRecord.shouldNotFreeze());
+        assertFreezeState(app, true);
+        assertFreezeState(app2, false);
+        assertFreezeState(app3, false);
 
         // Now remove app2 from allowlist.
         mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(false, app2.getUidRecord().isSetAllowListed());
-        assertEquals(false, app.mOptRecord.shouldNotFreeze());
-        assertEquals(false, app2.mOptRecord.shouldNotFreeze());
-        assertEquals(false, app3.mOptRecord.shouldNotFreeze());
+        assertFreezeState(app, true);
+        assertFreezeState(app2, true);
+        assertFreezeState(app3, true);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -3370,6 +3374,14 @@
         assertEquals(expectedCached, state.isCached());
     }
 
+    @SuppressWarnings("GuardedBy")
+    private void assertFreezeState(ProcessRecord app, boolean expectedFreezeState) {
+        boolean actualFreezeState = mTestCachedAppOptimizer.mLastSetFreezeState.get(app.getPid(),
+                false);
+        assertEquals("Unexcepted freeze state for " + app.processName, expectedFreezeState,
+                actualFreezeState);
+    }
+
     private class ProcessRecordBuilder {
         @SuppressWarnings("UnusedVariable")
         int mPid;
@@ -3513,6 +3525,39 @@
             return app;
         }
     }
+    private static final class TestProcessDependencies
+            implements CachedAppOptimizer.ProcessDependencies {
+        @Override
+        public long[] getRss(int pid) {
+            return new long[]{/*totalRSS*/ 0, /*fileRSS*/ 0, /*anonRSS*/ 0, /*swap*/ 0};
+        }
+
+        @Override
+        public void performCompaction(CachedAppOptimizer.CompactProfile action, int pid) {}
+    }
+
+    private static class TestCachedAppOptimizer extends CachedAppOptimizer {
+        private SparseBooleanArray mLastSetFreezeState = new SparseBooleanArray();
+
+        TestCachedAppOptimizer(ActivityManagerService ams) {
+            super(ams, null, new TestProcessDependencies());
+        }
+
+        @Override
+        public boolean useFreezer() {
+            return true;
+        }
+
+        @Override
+        public void freezeAppAsyncLSP(ProcessRecord app) {
+            mLastSetFreezeState.put(app.getPid(), true);
+        }
+
+        @Override
+        public void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
+            mLastSetFreezeState.put(app.getPid(), false);
+        }
+    }
 
     static class OomAdjusterInjector extends OomAdjuster.Injector {
         // Jump ahead in time by this offset amount.
@@ -3524,7 +3569,6 @@
             mLastSetOomAdj.clear();
         }
 
-
         void jumpUptimeAheadTo(long uptimeMillis) {
             final long jumpMs = uptimeMillis - getUptimeMillis();
             if (jumpMs <= 0) return;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index c418151..96c6cbc 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -123,7 +123,7 @@
             Assert.assertEquals("mic mute reporting wrong value",
                     muted, mAudioService.isMicrophoneMuted());
             // verify the intent for mic mute changed is supposed to be fired
-            Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+            mTestLooper.dispatchAll();
             verify(mSpySystemServer, times(1))
                     .sendMicrophoneMuteChangedIntent();
             reset(mSpySystemServer);
@@ -148,7 +148,7 @@
             Assert.assertEquals("mic mute reporting wrong value",
                     !muted, mAudioService.isMicrophoneMuted());
             // verify the intent for mic mute changed is supposed to be fired
-            Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+            mTestLooper.dispatchAll();
             verify(mSpySystemServer, times(1))
                     .sendMicrophoneMuteChangedIntent();
             reset(mSpySystemServer);
@@ -159,8 +159,7 @@
     public void testRingNotifAlias() throws Exception {
         Log.i(TAG, "running testRingNotifAlias");
         Assert.assertNotNull(mAudioService);
-        // TODO add initialization message that can be caught here instead of sleeping
-        Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
+        mTestLooper.dispatchAll(); // wait for full AudioService initialization
 
         // test with aliasing RING and NOTIFICATION
         mAudioService.setNotifAliasRingForTest(true);
@@ -171,7 +170,7 @@
         mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION,
                 ringVol, 0, "bla");
         mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringMaxVol, 0, "bla");
-        Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+        mTestLooper.dispatchAll();
         Assert.assertEquals(ringMaxVol,
                 mAudioService.getStreamVolume(AudioSystem.STREAM_NOTIFICATION));
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index cf628ad..b81bf3c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -332,6 +332,28 @@
         assertThat(preAuthInfo.callingUserId).isEqualTo(USER_ID);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_EFFECTIVE_USER_BP)
+    public void testCredentialOwnerIdAsUserId_forMandatoryBiometrics() throws Exception {
+        when(mUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(OWNER_ID);
+        when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+                OWNER_ID)).thenReturn(true);
+        when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+                USER_ID)).thenReturn(false);
+        when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+
+        final BiometricSensor sensor = getFaceSensor();
+        final PromptInfo promptInfo = new PromptInfo();
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK);
+        promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
+        final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+                mSettingObserver, List.of(sensor), USER_ID , promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
+
+        assertThat(preAuthInfo.getIsMandatoryBiometricsAuthentication()).isTrue();
+    }
+
     private BiometricSensor getFingerprintSensor() {
         BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
                 TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
deleted file mode 100644
index 57274bf..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.model;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayOutputStream;
-
-@RunWith(JUnit4.class)
-public class ByteTrackedOutputStreamTest {
-
-    @Test
-    public void testConstructorStartsWithZeroBytesWritten() {
-        ByteTrackedOutputStream byteTrackedOutputStream =
-                new ByteTrackedOutputStream(new ByteArrayOutputStream());
-
-        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void testSuccessfulWriteAndValidateWrittenBytesCount_directFromByteArray()
-            throws Exception {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
-
-        byte[] outputContent = "This is going to be outputed for tests.".getBytes();
-        byteTrackedOutputStream.write(outputContent);
-
-        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(outputContent.length);
-        assertThat(outputStream.toByteArray().length).isEqualTo(outputContent.length);
-    }
-
-    @Test
-    public void testSuccessfulWriteAndValidateWrittenBytesCount_fromBitStream() throws Exception {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
-
-        BitOutputStream bitOutputStream = new BitOutputStream(byteTrackedOutputStream);
-        bitOutputStream.setNext(/* numOfBits= */5, /* value= */1);
-        bitOutputStream.flush();
-
-        // Even though we wrote 5 bits, this will complete to 1 byte.
-        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1);
-
-        // Add a bit less than 2 bytes (10 bits).
-        bitOutputStream.setNext(/* numOfBits= */10, /* value= */1);
-        bitOutputStream.flush();
-        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(3);
-
-        assertThat(outputStream.toByteArray().length).isEqualTo(3);
-    }
-}
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 62a4711..41f1e23 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -87,6 +87,7 @@
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.times;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -4782,6 +4783,114 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    public void testCameraCompatAspectRatioAppliedForFixedOrientationCameraActivities() {
+        // Needed to create camera compat policy in DisplayContent.
+        allowDesktopMode();
+        // Create display that has all stable insets and does not rotate.
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+                .setSystemDecorations(true).setCanRotate(false).build();
+
+        final float cameraCompatAspectRatio = 4.0f;
+        setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+        // Create task on test display.
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+        // Create fixed portrait activity.
+        final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
+                .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+        final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        assertEquals(cameraCompatAspectRatio, computeAspectRatio(fixedOrientationAppBounds),
+                DELTA_ASPECT_RATIO_TOLERANCE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    public void testCameraCompatAspectRatioForFixedOrientationCameraActivitiesPortraitWindow() {
+        // Needed to create camera compat policy in DisplayContent.
+        allowDesktopMode();
+        // Create portrait display that has all stable insets and does not rotate.
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 1600)
+                .setSystemDecorations(true).setCanRotate(false).build();
+
+        final float cameraCompatAspectRatio = 4.0f;
+        setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+        // Create task on test display.
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+        // Create fixed portrait activity.
+        final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
+                .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+        final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        assertEquals(cameraCompatAspectRatio, computeAspectRatio(fixedOrientationAppBounds),
+                DELTA_ASPECT_RATIO_TOLERANCE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    public void testCameraCompatAspectRatioAppliedInsteadOfDefaultAspectRatio() {
+        // Needed to create camera compat policy in DisplayContent.
+        allowDesktopMode();
+        // Create display that has all stable insets and does not rotate.
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+                .setSystemDecorations(true).setCanRotate(false).build();
+
+        final float cameraCompatAspectRatio = 5.0f;
+        setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+        // Create task on test display.
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+        // App's target min aspect ratio - this should not be used, as camera controls aspect ratio.
+        final float targetMinAspectRatio = 4.0f;
+
+        // Create fixed portrait activity with min aspect ratio greater than parent aspect ratio.
+        final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm)
+                .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setMinAspectRatio(targetMinAspectRatio).build();
+        final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        assertEquals(cameraCompatAspectRatio, computeAspectRatio(minAspectRatioAppBounds),
+                DELTA_ASPECT_RATIO_TOLERANCE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    public void testCameraCompatAspectRatio_defualtAspectRatioAppliedWhenGreater() {
+        // Needed to create camera compat policy in DisplayContent.
+        allowDesktopMode();
+        // Create display that has all stable insets and does not rotate.
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+                .setSystemDecorations(true).setCanRotate(false).build();
+
+        final float cameraCompatAspectRatio = 5.0f;
+        setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+        // Create task on test display.
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+        // App's target min aspect ratio bigger than camera compat aspect ratio - use that instead.
+        final float targetMinAspectRatio = 6.0f;
+
+        // Create fixed portrait activity with min aspect ratio greater than parent aspect ratio.
+        final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm)
+                .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setMinAspectRatio(targetMinAspectRatio).build();
+        final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        assertEquals(targetMinAspectRatio, computeAspectRatio(minAspectRatioAppBounds),
+                DELTA_ASPECT_RATIO_TOLERANCE);
+    }
+
+    @Test
     public void testUniversalResizeable() {
         mWm.mConstants.mIgnoreActivityOrientationRequest = true;
         setUpApp(mDisplayContent);
@@ -4868,6 +4977,25 @@
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
     }
 
+    /**
+     * {@code canEnterDesktopMode} is called when {@link CameraCompatFreeformPolicy} is created in
+     * {@link AppCompatCameraPolicy}.
+     *
+     * <p>{@link #allowDesktopMode()} needs to be called before {@link DisplayContent} is created.
+     */
+    private void allowDesktopMode() {
+        doReturn(true).when(() -> DesktopModeHelper.canEnterDesktopMode(any()));
+    }
+
+    private void setupCameraCompatAspectRatio(float cameraCompatAspectRatio,
+            @NonNull DisplayContent display) {
+        CameraCompatFreeformPolicy cameraPolicy = display.mAppCompatCameraPolicy
+                .mCameraCompatFreeformPolicy;
+        spyOn(cameraPolicy);
+        doReturn(true).when(cameraPolicy).shouldCameraCompatControlAspectRatio(any());
+        doReturn(cameraCompatAspectRatio).when(cameraPolicy).getCameraCompatAspectRatio(any());
+    }
+
     private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) {
         final AppCompatReachabilityOverrides reachabilityOverrides =
                 mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7e3d99a..887b798 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -3511,6 +3511,40 @@
         }
     }
 
+    /**
+     * Inform whether application supports NTN SMS in satellite mode.
+     *
+     * This method is used by default messaging application to inform framework whether it supports
+     * NTN SMS or not.
+     *
+     * Invoking this API will internally result in triggering
+     * {@link android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener
+     * #onCarrierRoamingNtnAvailableServicesChanged(List)} and
+     * {@link android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener
+     * #onCarrierRoamingNtnEligibleStateChanged(boolean)} callbacks.
+     *
+     * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(allOf = {Manifest.permission.SATELLITE_COMMUNICATION,
+            Manifest.permission.SEND_SMS})
+    public void setNtnSmsSupported(boolean ntnSmsSupported) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setNtnSmsSupported(ntnSmsSupported);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("setNtnSmsSupported() RemoteException:" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
     @Nullable
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 210200b..a584273 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3497,4 +3497,15 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
     void deprovisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
+
+   /**
+    * Inform whether application supports NTN SMS in satellite mode.
+    *
+    * This method is used by default messaging application to inform framework whether it supports
+    * NTN SMS or not.
+    *
+    * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}.
+    * @hide
+    */
+    void setNtnSmsSupported(boolean ntnSmsSupported);
 }