Merge "Add conflate to the sudio tream flow so it doesn't get overwhelmed by the old changes" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 8591a9c..6ecd38f 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@
         // !!! KEEP THIS LIST ALPHABETICAL !!!
         "aconfig_mediacodec_flags_java_lib",
         "android.adaptiveauth.flags-aconfig-java",
+        "android.app.contextualsearch.flags-aconfig-java",
         "android.app.flags-aconfig-java",
         "android.app.ondeviceintelligence-aconfig-java",
         "android.app.smartspace.flags-aconfig-java",
@@ -974,6 +975,19 @@
     ],
 }
 
+// Contextual Search
+aconfig_declarations {
+    name: "android.app.contextualsearch.flags-aconfig",
+    package: "android.app.contextualsearch.flags",
+    srcs: ["core/java/android/app/contextualsearch/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.app.contextualsearch.flags-aconfig-java",
+    aconfig_declarations: "android.app.contextualsearch.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Smartspace
 aconfig_declarations {
     name: "android.app.smartspace.flags-aconfig",
diff --git a/core/api/current.txt b/core/api/current.txt
index 1478377..925cf4c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -293,7 +293,7 @@
     field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
     field public static final String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
     field public static final String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
-    field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_LOGO = "android.permission.SET_BIOMETRIC_DIALOG_LOGO";
+    field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_ADVANCED = "android.permission.SET_BIOMETRIC_DIALOG_ADVANCED";
     field public static final String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
     field @Deprecated public static final String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
     field public static final String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
@@ -12487,33 +12487,33 @@
   }
 
   public class LauncherApps {
-    method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
-    method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
+    method @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
     method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.IntentSender getAppMarketActivityIntent(@Nullable String, @NonNull android.os.UserHandle);
-    method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public final android.content.pm.LauncherUserInfo getLauncherUserInfo(@NonNull android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method @FlaggedApi("android.os.allow_private_profile") @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<java.lang.String> getPreInstalledSystemPackages(@NonNull android.os.UserHandle);
-    method public java.util.List<android.os.UserHandle> getProfiles();
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.os.UserHandle> getProfiles();
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
     method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo);
     method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String, @NonNull android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(@NonNull android.content.pm.ShortcutInfo, int);
     method @Nullable public android.app.PendingIntent getShortcutIntent(@NonNull String, @NonNull String, @Nullable android.os.Bundle, @NonNull android.os.UserHandle);
     method @Nullable public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(@NonNull android.content.pm.LauncherApps.ShortcutQuery, @NonNull android.os.UserHandle);
-    method @Nullable public android.os.Bundle getSuspendedPackageLauncherExtras(String, android.os.UserHandle);
+    method @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.os.Bundle getSuspendedPackageLauncherExtras(String, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
-    method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
-    method public boolean isPackageEnabled(String, android.os.UserHandle);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public boolean isPackageEnabled(String, android.os.UserHandle);
     method public void pinShortcuts(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull android.os.UserHandle);
-    method public void registerCallback(android.content.pm.LauncherApps.Callback);
-    method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void registerCallback(android.content.pm.LauncherApps.Callback);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public void registerPackageInstallerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.content.pm.PackageInstaller.SessionCallback);
-    method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method @FlaggedApi("android.content.pm.archiving") public void setArchiveCompatibility(@NonNull android.content.pm.LauncherApps.ArchiveCompatibilityParams);
-    method public boolean shouldHideFromSuggestions(@NonNull String, @NonNull android.os.UserHandle);
-    method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
-    method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public boolean shouldHideFromSuggestions(@NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startPackageInstallerSessionDetailsActivity(@NonNull android.content.pm.PackageInstaller.SessionInfo, @Nullable android.graphics.Rect, @Nullable android.os.Bundle);
     method public void startShortcut(@NonNull String, @NonNull String, @Nullable android.graphics.Rect, @Nullable android.os.Bundle, @NonNull android.os.UserHandle);
     method public void startShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.graphics.Rect, @Nullable android.os.Bundle);
@@ -18904,9 +18904,9 @@
     method @Nullable public int getAllowedAuthenticators();
     method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
     method @Nullable public CharSequence getDescription();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.graphics.Bitmap getLogoBitmap();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public String getLogoDescription();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public int getLogoRes();
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap getLogoBitmap();
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getLogoDescription();
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
     method @Nullable public CharSequence getNegativeButtonText();
     method @Nullable public CharSequence getSubtitle();
     method @NonNull public CharSequence getTitle();
@@ -18956,9 +18956,9 @@
     method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
     method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -18971,6 +18971,7 @@
     ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
     ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
     ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement);
+    ctor @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public BiometricPrompt.CryptoObject(long);
     method @Nullable public javax.crypto.Cipher getCipher();
     method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
@@ -19000,6 +19001,21 @@
   @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentView {
   }
 
+  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentViewWithMoreOptionsButton implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
+    method public int describeContents();
+    method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getDescription();
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.content.DialogInterface.OnClickListener getMoreOptionsButtonListener();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentViewWithMoreOptionsButton> CREATOR;
+  }
+
+  public static final class PromptContentViewWithMoreOptionsButton.Builder {
+    ctor public PromptContentViewWithMoreOptionsButton.Builder();
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton build();
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder setDescription(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder setMoreOptionsButtonListener(@NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
+  }
+
   @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
     method public int describeContents();
     method @Nullable public String getDescription();
@@ -20121,9 +20137,14 @@
     ctor public OutputConfiguration(@NonNull android.view.Surface);
     ctor public OutputConfiguration(int, @NonNull android.view.Surface);
     ctor public <T> OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
+    ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, @NonNull android.util.Size);
+    ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, int, @NonNull android.util.Size);
+    ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, @NonNull android.util.Size, long);
+    ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, int, @NonNull android.util.Size, long);
     method public void addSensorPixelModeUsed(int);
     method public void addSurface(@NonNull android.view.Surface);
     method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
+    method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") @NonNull public static java.util.List<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int);
     method public int describeContents();
     method public void enableSurfaceSharing();
     method public long getDynamicRangeProfile();
@@ -20142,6 +20163,7 @@
     method public void setPhysicalCameraId(@Nullable String);
     method public void setReadoutTimestampEnabled(boolean);
     method public void setStreamUseCase(long);
+    method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public static void setSurfacesForMultiResolutionOutput(@NonNull java.util.Collection<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.MultiResolutionImageReader);
     method public void setTimestampBase(int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
@@ -20203,6 +20225,7 @@
 
   public final class SessionConfiguration implements android.os.Parcelable {
     ctor public SessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback);
+    ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public SessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>);
     method public void clearColorSpace();
     method public int describeContents();
     method @Nullable public android.graphics.ColorSpace getColorSpace();
@@ -20212,6 +20235,7 @@
     method public android.hardware.camera2.CaptureRequest getSessionParameters();
     method public int getSessionType();
     method public android.hardware.camera2.CameraCaptureSession.StateCallback getStateCallback();
+    method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback);
     method public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
     method public void setInputConfiguration(@NonNull android.hardware.camera2.params.InputConfiguration);
     method public void setSessionParameters(android.hardware.camera2.CaptureRequest);
@@ -33960,6 +33984,7 @@
     method public static boolean supportsMultipleUsers();
     field public static final String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
     field @Deprecated public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
+    field @FlaggedApi("android.os.allow_private_profile") public static final String DISALLOW_ADD_PRIVATE_PROFILE = "no_add_private_profile";
     field public static final String DISALLOW_ADD_USER = "no_add_user";
     field public static final String DISALLOW_ADD_WIFI_CONFIG = "no_add_wifi_config";
     field public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
@@ -52193,7 +52218,7 @@
     method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction addTransactionCompletedListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.SurfaceControl.TransactionStats>);
     method public void apply();
     method @NonNull public android.view.SurfaceControl.Transaction clearFrameRate(@NonNull android.view.SurfaceControl);
-    method @NonNull public android.view.SurfaceControl.Transaction clearTrustedPresentationCallback(@NonNull android.view.SurfaceControl);
+    method @Deprecated @NonNull public android.view.SurfaceControl.Transaction clearTrustedPresentationCallback(@NonNull android.view.SurfaceControl);
     method public void close();
     method public int describeContents();
     method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
@@ -52218,7 +52243,7 @@
     method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
     method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
     method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
-    method @NonNull public android.view.SurfaceControl.Transaction setTrustedPresentationCallback(@NonNull android.view.SurfaceControl, @NonNull android.view.SurfaceControl.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setTrustedPresentationCallback(@NonNull android.view.SurfaceControl, @NonNull android.view.SurfaceControl.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
@@ -52233,8 +52258,8 @@
     method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.hardware.SyncFence getPresentFence();
   }
 
-  public static final class SurfaceControl.TrustedPresentationThresholds {
-    ctor public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
+  @Deprecated public static final class SurfaceControl.TrustedPresentationThresholds {
+    ctor @Deprecated public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
   }
 
   @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public interface SurfaceControlInputReceiver {
@@ -52249,7 +52274,7 @@
     method public void relayout(int, int);
     method public void release();
     method public void setView(@NonNull android.view.View, int, int);
-    method public boolean transferTouchGestureToHost();
+    method @Deprecated public boolean transferTouchGestureToHost();
   }
 
   public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
@@ -52308,7 +52333,7 @@
     ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
     method public void applyTransactionToFrame(@NonNull android.view.SurfaceControl.Transaction);
     method public android.view.SurfaceHolder getHolder();
-    method @Nullable public android.os.IBinder getHostToken();
+    method @Deprecated @Nullable public android.os.IBinder getHostToken();
     method public android.view.SurfaceControl getSurfaceControl();
     method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
     method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 78ac774..9039bf1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7,6 +7,7 @@
     field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
     field public static final String ACCESS_BROADCAST_RESPONSE_STATS = "android.permission.ACCESS_BROADCAST_RESPONSE_STATS";
     field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
+    field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String ACCESS_CONTEXTUAL_SEARCH = "android.permission.ACCESS_CONTEXTUAL_SEARCH";
     field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
     field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
     field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
@@ -198,7 +199,6 @@
     field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS";
     field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
-    field @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") public static final String MANAGE_DEVICE_POLICY_THEFT_DETECTION = "android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION";
     field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
     field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
@@ -282,6 +282,7 @@
     field @FlaggedApi("android.content.pm.quarantined_enabled") public static final String QUARANTINE_APPS = "android.permission.QUARANTINE_APPS";
     field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
     field public static final String QUERY_CLONED_APPS = "android.permission.QUERY_CLONED_APPS";
+    field @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") public static final String QUERY_DEVICE_STOLEN_STATE = "android.permission.QUERY_DEVICE_STOLEN_STATE";
     field @Deprecated public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
     field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
     field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
@@ -1328,12 +1329,12 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
     method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
     method public boolean isDeviceManaged();
+    method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE) public boolean isDevicePotentiallyStolen();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean isDpcDownloaded();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk();
     method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
-    method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION) public boolean isTheftDetectionTriggered();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk();
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
@@ -2176,6 +2177,39 @@
 
 }
 
+package android.app.contextualsearch {
+
+  @FlaggedApi("android.app.contextualsearch.flags.enable_service") public class ContextualSearchManager {
+    method public void getContextualSearchState(@NonNull android.os.IBinder, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.contextualsearch.ContextualSearchState,java.lang.Throwable>);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH) public void startContextualSearch(int);
+    field public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH = "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH";
+    field public static final int ENTRYPOINT_LONG_PRESS_HOME = 2; // 0x2
+    field public static final int ENTRYPOINT_LONG_PRESS_META = 10; // 0xa
+    field public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1; // 0x1
+    field public static final int ENTRYPOINT_LONG_PRESS_OVERVIEW = 3; // 0x3
+    field public static final int ENTRYPOINT_OVERVIEW_ACTION = 4; // 0x4
+    field public static final int ENTRYPOINT_OVERVIEW_MENU = 5; // 0x5
+    field public static final int ENTRYPOINT_SYSTEM_ACTION = 9; // 0x9
+    field public static final String EXTRA_ENTRYPOINT = "android.app.contextualsearch.extra.ENTRYPOINT";
+    field public static final String EXTRA_FLAG_SECURE_FOUND = "android.app.contextualsearch.extra.FLAG_SECURE_FOUND";
+    field public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE = "android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE";
+    field public static final String EXTRA_SCREENSHOT = "android.app.contextualsearch.extra.SCREENSHOT";
+    field public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
+    field public static final String EXTRA_VISIBLE_PACKAGE_NAMES = "android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES";
+  }
+
+  @FlaggedApi("android.app.contextualsearch.flags.enable_service") public final class ContextualSearchState implements android.os.Parcelable {
+    ctor public ContextualSearchState(@Nullable android.app.assist.AssistStructure, @Nullable android.app.assist.AssistContent, @NonNull android.os.Bundle);
+    method public int describeContents();
+    method @Nullable public android.app.assist.AssistContent getContent();
+    method @NonNull public android.os.Bundle getExtras();
+    method @Nullable public android.app.assist.AssistStructure getStructure();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.contextualsearch.ContextualSearchState> CREATOR;
+  }
+
+}
+
 package android.app.job {
 
   public abstract class JobScheduler {
@@ -3747,6 +3781,7 @@
     field public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
+    field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String CONTEXTUAL_SEARCH_SERVICE = "contextual_search";
     field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
@@ -5006,7 +5041,7 @@
   }
 
   public static interface DeviceStateManager.DeviceStateCallback {
-    method public default void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
+    method public void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
     method public default void onSupportedStatesChanged(@NonNull java.util.List<android.hardware.devicestate.DeviceState>);
   }
 
@@ -12979,11 +13014,11 @@
     method public long getMaximumDataBlockSize();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName();
-    method public byte[] read();
+    method @Nullable public byte[] read();
     method @FlaggedApi("android.security.frp_enforcement") public boolean setFactoryResetProtectionSecret(@NonNull byte[]);
     method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
     method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
-    method public int write(byte[]);
+    method public int write(@Nullable byte[]);
     field public static final int FLASH_LOCK_LOCKED = 1; // 0x1
     field public static final int FLASH_LOCK_UNKNOWN = -1; // 0xffffffff
     field public static final int FLASH_LOCK_UNLOCKED = 0; // 0x0
@@ -13936,9 +13971,8 @@
   }
 
   @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class DisconnectCause.Builder {
-    ctor public DisconnectCause.Builder();
+    ctor public DisconnectCause.Builder(int);
     method @NonNull public android.telecom.DisconnectCause build();
-    method @NonNull public android.telecom.DisconnectCause.Builder setCode(int);
     method @NonNull public android.telecom.DisconnectCause.Builder setDescription(@Nullable CharSequence);
     method @NonNull public android.telecom.DisconnectCause.Builder setImsReasonInfo(@Nullable android.telephony.ims.ImsReasonInfo);
     method @NonNull public android.telecom.DisconnectCause.Builder setLabel(@Nullable CharSequence);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bc45a76..c1af719 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1625,25 +1625,37 @@
 package android.hardware.devicestate {
 
   @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+    ctor public DeviceState(@NonNull android.hardware.devicestate.DeviceState.Configuration);
     field public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8; // 0x8
   }
 
+  public static final class DeviceState.Configuration implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getIdentifier();
+    method @NonNull public String getName();
+    method @NonNull public java.util.Set<java.lang.Integer> getPhysicalProperties();
+    method @NonNull public java.util.Set<java.lang.Integer> getSystemProperties();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.devicestate.DeviceState.Configuration> CREATOR;
+  }
+
+  public static final class DeviceState.Configuration.Builder {
+    ctor public DeviceState.Configuration.Builder(int, @NonNull String);
+    method @NonNull public android.hardware.devicestate.DeviceState.Configuration build();
+    method @NonNull public android.hardware.devicestate.DeviceState.Configuration.Builder setPhysicalProperties(@NonNull java.util.Set<java.lang.Integer>);
+    method @NonNull public android.hardware.devicestate.DeviceState.Configuration.Builder setSystemProperties(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
   @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
     method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
-    method @Deprecated @NonNull public int[] getSupportedStates();
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
     method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+    field public static final int INVALID_DEVICE_STATE_IDENTIFIER = -1; // 0xffffffff
     field public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000; // 0x2710
     field public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0; // 0x0
   }
 
-  public static interface DeviceStateManager.DeviceStateCallback {
-    method @Deprecated public default void onBaseStateChanged(int);
-    method @Deprecated public void onStateChanged(int);
-    method @Deprecated public default void onSupportedStatesChanged(@NonNull int[]);
-  }
-
   public final class DeviceStateRequest {
     method public int getFlags();
     method public int getState();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index df566db..6f6e091 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -272,10 +272,15 @@
 
     @UnsupportedAppUsage
     private Context mOuterContext;
+
+    private final Object mThemeLock = new Object();
     @UnsupportedAppUsage
+    @GuardedBy("mThemeLock")
     private int mThemeResource = 0;
     @UnsupportedAppUsage
+    @GuardedBy("mThemeLock")
     private Resources.Theme mTheme = null;
+
     @UnsupportedAppUsage
     private PackageManager mPackageManager;
     private Context mReceiverRestrictedContext = null;
@@ -288,7 +293,6 @@
 
     private ContentCaptureOptions mContentCaptureOptions = null;
 
-    private final Object mSync = new Object();
     /**
      * Indicates this {@link Context} can not handle UI components properly and is not associated
      * with a {@link Display} instance.
@@ -340,20 +344,21 @@
      */
     private boolean mOwnsToken = false;
 
-    @GuardedBy("mSync")
+    private final Object mDirsLock = new Object();
+    @GuardedBy("mDirsLock")
     private File mDatabasesDir;
-    @GuardedBy("mSync")
+    @GuardedBy("mDirsLock")
     @UnsupportedAppUsage
     private File mPreferencesDir;
-    @GuardedBy("mSync")
+    @GuardedBy("mDirsLock")
     private File mFilesDir;
-    @GuardedBy("mSync")
+    @GuardedBy("mDirsLock")
     private File mCratesDir;
-    @GuardedBy("mSync")
+    @GuardedBy("mDirsLock")
     private File mNoBackupFilesDir;
-    @GuardedBy("mSync")
+    @GuardedBy("mDirsLock")
     private File mCacheDir;
-    @GuardedBy("mSync")
+    @GuardedBy("mDirsLock")
     private File mCodeCacheDir;
 
     // The system service cache for the system services that are cached per-ContextImpl.
@@ -458,7 +463,7 @@
 
     @Override
     public void setTheme(int resId) {
-        synchronized (mSync) {
+        synchronized (mThemeLock) {
             if (mThemeResource != resId) {
                 mThemeResource = resId;
                 initializeTheme();
@@ -468,14 +473,14 @@
 
     @Override
     public int getThemeResId() {
-        synchronized (mSync) {
+        synchronized (mThemeLock) {
             return mThemeResource;
         }
     }
 
     @Override
     public Resources.Theme getTheme() {
-        synchronized (mSync) {
+        synchronized (mThemeLock) {
             if (mTheme != null) {
                 return mTheme;
             }
@@ -737,7 +742,7 @@
 
     @UnsupportedAppUsage
     private File getPreferencesDir() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mPreferencesDir == null) {
                 mPreferencesDir = new File(getDataDir(), "shared_prefs");
             }
@@ -826,7 +831,7 @@
 
     @Override
     public File getFilesDir() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mFilesDir == null) {
                 mFilesDir = new File(getDataDir(), "files");
             }
@@ -841,7 +846,7 @@
         final Path absoluteNormalizedCratePath = cratesRootPath.resolve(crateId)
                 .toAbsolutePath().normalize();
 
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mCratesDir == null) {
                 mCratesDir = cratesRootPath.toFile();
             }
@@ -854,7 +859,7 @@
 
     @Override
     public File getNoBackupFilesDir() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mNoBackupFilesDir == null) {
                 mNoBackupFilesDir = new File(getDataDir(), "no_backup");
             }
@@ -871,7 +876,7 @@
 
     @Override
     public File[] getExternalFilesDirs(String type) {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
             if (type != null) {
                 dirs = Environment.buildPaths(dirs, type);
@@ -889,7 +894,7 @@
 
     @Override
     public File[] getObbDirs() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
             return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
         }
@@ -897,7 +902,7 @@
 
     @Override
     public File getCacheDir() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mCacheDir == null) {
                 mCacheDir = new File(getDataDir(), "cache");
             }
@@ -907,7 +912,7 @@
 
     @Override
     public File getCodeCacheDir() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mCodeCacheDir == null) {
                 mCodeCacheDir = getCodeCacheDirBeforeBind(getDataDir());
             }
@@ -933,7 +938,7 @@
 
     @Override
     public File[] getExternalCacheDirs() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
             // We don't try to create cache directories in-process, because they need special
             // setup for accurate quota tracking. This ensures the cache dirs are always
@@ -944,7 +949,7 @@
 
     @Override
     public File[] getExternalMediaDirs() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
             return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
         }
@@ -1046,7 +1051,7 @@
     }
 
     private File getDatabasesDir() {
-        synchronized (mSync) {
+        synchronized (mDirsLock) {
             if (mDatabasesDir == null) {
                 if ("android".equals(getPackageName())) {
                     mDatabasesDir = new File("/data/system");
diff --git a/core/java/android/app/ForegroundServiceDelegationOptions.java b/core/java/android/app/ForegroundServiceDelegationOptions.java
index 875e01f..d6b6a58 100644
--- a/core/java/android/app/ForegroundServiceDelegationOptions.java
+++ b/core/java/android/app/ForegroundServiceDelegationOptions.java
@@ -92,6 +92,16 @@
      */
     public final @DelegationService int mDelegationService;
 
+    /**
+     * The optional notification Id of the foreground service delegation.
+     */
+    public final int mClientNotificationId;
+
+    /**
+     * The optional notification of the foreground service delegation.
+     */
+    public final @Nullable Notification mClientNotification;
+
     public ForegroundServiceDelegationOptions(int clientPid,
             int clientUid,
             @NonNull String clientPackageName,
@@ -100,6 +110,21 @@
             @NonNull String clientInstanceName,
             int foregroundServiceTypes,
             @DelegationService int delegationService) {
+        this(clientPid, clientUid, clientPackageName, clientAppThread, isSticky,
+                clientInstanceName, foregroundServiceTypes, delegationService,
+                0 /* notificationId */, null /* notification */);
+    }
+
+    public ForegroundServiceDelegationOptions(int clientPid,
+            int clientUid,
+            @NonNull String clientPackageName,
+            @NonNull IApplicationThread clientAppThread,
+            boolean isSticky,
+            @NonNull String clientInstanceName,
+            int foregroundServiceTypes,
+            @DelegationService int delegationService,
+            int clientNotificationId,
+            @Nullable Notification clientNotification) {
         mClientPid = clientPid;
         mClientUid = clientUid;
         mClientPackageName = clientPackageName;
@@ -108,6 +133,8 @@
         mClientInstanceName = clientInstanceName;
         mForegroundServiceTypes = foregroundServiceTypes;
         mDelegationService = delegationService;
+        mClientNotificationId = clientNotificationId;
+        mClientNotification = clientNotification;
     }
 
     /**
@@ -201,7 +228,8 @@
         int mClientPid; // The actual app PID
         int mClientUid; // The actual app UID
         String mClientPackageName; // The actual app's package name
-        int mClientNotificationId; // The actual app's notification
+        int mClientNotificationId; // The actual app's notification id
+        Notification mClientNotification; // The actual app's notification
         IApplicationThread mClientAppThread; // The actual app's app thread
         boolean mSticky; // Is it a sticky service
         String mClientInstanceName; // The delegation service instance name
@@ -233,10 +261,12 @@
         }
 
         /**
-         * Set the notification ID from the client app.
+         * Set the notification from the client app.
          */
-        public Builder setClientNotificationId(int clientNotificationId) {
+        public Builder setClientNotification(int clientNotificationId,
+                @Nullable Notification clientNotification) {
             mClientNotificationId = clientNotificationId;
+            mClientNotification = clientNotification;
             return this;
         }
 
@@ -291,7 +321,9 @@
                 mSticky,
                 mClientInstanceName,
                 mForegroundServiceTypes,
-                mDelegationService
+                mDelegationService,
+                mClientNotificationId,
+                mClientNotification
             );
         }
     }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 9f2e473..7c803eb 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -776,7 +776,8 @@
 
     /**
      * Whether or not notifications posted to this channel can bypass the Do Not Disturb
-     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
+     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode when the active policy allows
+     * priority channels to bypass notification filtering.
      */
     public boolean canBypassDnd() {
         return mBypassDnd;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fa4a400..66269a5 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -32,6 +32,7 @@
 import android.app.blob.BlobStoreManagerFrameworkInitializer;
 import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IContentSuggestionsManager;
+import android.app.contextualsearch.ContextualSearchManager;
 import android.app.ecm.EnhancedConfirmationFrameworkInitializer;
 import android.app.job.JobSchedulerFrameworkInitializer;
 import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
@@ -1282,6 +1283,16 @@
                 }
             });
 
+        registerService(Context.CONTEXTUAL_SEARCH_SERVICE, ContextualSearchManager.class,
+                new CachedServiceFetcher<>() {
+                    @Override
+                    public ContextualSearchManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getService(Context.CONTEXTUAL_SEARCH_SERVICE);
+                        return b == null ? null : new ContextualSearchManager();
+                    }
+                });
+
         registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
                 new CachedServiceFetcher<AppPredictionManager>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cb4ed058..5b28f50 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -45,11 +45,11 @@
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STATUS_BAR;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
 import static android.Manifest.permission.QUERY_ADMIN_POLICY;
+import static android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE;
 import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
 import static android.Manifest.permission.SET_TIME;
 import static android.Manifest.permission.SET_TIME_ZONE;
@@ -5952,7 +5952,8 @@
      * <p>
      * This method can be called on the {@link DevicePolicyManager} instance returned by
      * {@link #getParentProfileInstance(ComponentName)} in order to set a value on the parent
-     * profile.
+     * profile. This allows a profile wipe after too many incorrect device-unlock password have
+     * been entered on the parent profile even if each profile has a separate challenge.
      * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
      * password is always empty and this method has no effect - i.e. the policy is not set.
      *
@@ -9979,17 +9980,22 @@
     /**
      * Called by a profile owner or device owner or holder of the permission
      * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}. to set a default activity
-     * that the system selects to handle intents that match the given {@link IntentFilter}.
+     * that the system selects to handle intents that match the given {@link IntentFilter} instead
+     * of showing the default disambiguation mechanism.
      * This activity will remain the default intent handler even if the set of potential event
      * handlers for the intent filter changes and if the intent preferences are reset.
      * <p>
-     * Note that the caller should still declare the activity in the manifest, the API just sets
-     * the activity to be the default one to handle the given intent filter.
+     * Note that the target application should still declare the activity in the manifest, the API
+     * just sets the activity to be the default one to handle the given intent filter.
      * <p>
      * The default disambiguation mechanism takes over if the activity is not installed (anymore).
      * When the activity is (re)installed, it is automatically reset as default intent handler for
      * the filter.
      * <p>
+     * Note that calling this API to set a default intent handler, only allow to avoid the default
+     * disambiguation mechanism. Implicit intents that do not trigger this mechanism (like invoking
+     * the browser) cannot be configured as they are controlled by other configurations.
+     * <p>
      * The calling device admin must be a profile owner or device owner. If it is not, a security
      * exception will be thrown.
      * <p>
@@ -17152,15 +17158,15 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(value = MANAGE_DEVICE_POLICY_THEFT_DETECTION)
+    @RequiresPermission(value = QUERY_DEVICE_STOLEN_STATE)
     @FlaggedApi(FLAG_DEVICE_THEFT_API_ENABLED)
-    public boolean isTheftDetectionTriggered() {
-        throwIfParentInstance("isTheftDetectionTriggered");
+    public boolean isDevicePotentiallyStolen() {
+        throwIfParentInstance("isDevicePotentiallyStolen");
         if (mService == null) {
             return false;
         }
         try {
-            return mService.isTheftDetectionTriggered(mContext.getPackageName());
+            return mService.isDevicePotentiallyStolen(mContext.getPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 03d0b0f..aea0246 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -581,7 +581,7 @@
     void setWifiSsidPolicy(String callerPackageName, in WifiSsidPolicy policy);
     WifiSsidPolicy getWifiSsidPolicy(String callerPackageName);
 
-    boolean isTheftDetectionTriggered(String callerPackageName);
+    boolean isDevicePotentiallyStolen(String callerPackageName);
 
     List<UserHandle> listForegroundAffiliatedUsers();
     void setDrawables(in List<DevicePolicyDrawableResource> drawables);
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
new file mode 100644
index 0000000..693de21
--- /dev/null
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.contextualsearch;
+
+import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.contextualsearch.flags.Flags;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.ParcelableException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link ContextualSearchManager} is a system service to facilitate contextual search experience on
+ * configured Android devices.
+ * <p>
+ * This class lets
+ * <ul>
+ *   <li> a caller start contextual search by calling {@link #startContextualSearch} method.
+ *   <li> a handler request {@link ContextualSearchState} by calling the
+ *   {@link #getContextualSearchState} method.
+ * </ul>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_SERVICE)
+public class ContextualSearchManager {
+
+    /**
+     * Key to get the entrypoint from the extras of the activity launched by contextual search.
+     * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+     */
+    public static final String EXTRA_ENTRYPOINT =
+            "android.app.contextualsearch.extra.ENTRYPOINT";
+    /**
+     * Key to get the flag_secure value from the extras of the activity launched by contextual
+     * search. The value will be true if flag_secure is found in any of the visible activities.
+     * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+     */
+    public static final String EXTRA_FLAG_SECURE_FOUND =
+            "android.app.contextualsearch.extra.FLAG_SECURE_FOUND";
+    /**
+     * Key to get the screenshot from the extras of the activity launched by contextual search.
+     * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+     */
+    public static final String EXTRA_SCREENSHOT =
+            "android.app.contextualsearch.extra.SCREENSHOT";
+    /**
+     * Key to check whether managed profile is visible from the extras of the activity launched by
+     * contextual search. The value will be true if any one of the visible apps is managed.
+     * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+     */
+    public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE =
+            "android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE";
+    /**
+     * Key to get the list of visible packages from the extras of the activity launched by
+     * contextual search.
+     * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+     */
+    public static final String EXTRA_VISIBLE_PACKAGE_NAMES =
+            "android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES";
+
+    /**
+     * Key to get the binder token from the extras of the activity launched by contextual search.
+     * This token is needed to invoke {@link #getContextualSearchState} method.
+     * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+     */
+    public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
+    /**
+     * Intent action for contextual search invocation. The app providing the contextual search
+     * experience must add this intent filter action to the activity it wants to be launched.
+     * <br>
+     * <b>Note</b> This activity must not be exported.
+     */
+    public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH =
+            "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH";
+
+    /** Entrypoint to be used when a user long presses on the nav handle. */
+    public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1;
+    /** Entrypoint to be used when a user long presses on the home button. */
+    public static final int ENTRYPOINT_LONG_PRESS_HOME = 2;
+    /** Entrypoint to be used when a user long presses on the overview button. */
+    public static final int ENTRYPOINT_LONG_PRESS_OVERVIEW = 3;
+    /** Entrypoint to be used when a user presses the action button in overview. */
+    public static final int ENTRYPOINT_OVERVIEW_ACTION = 4;
+    /** Entrypoint to be used when a user presses the context menu button in overview. */
+    public static final int ENTRYPOINT_OVERVIEW_MENU = 5;
+    /** Entrypoint to be used by system actions like TalkBack, Accessibility etc. */
+    public static final int ENTRYPOINT_SYSTEM_ACTION = 9;
+    /** Entrypoint to be used when a user long presses on the meta key. */
+    public static final int ENTRYPOINT_LONG_PRESS_META = 10;
+    /**
+     * The {@link Entrypoint} annotation is used to standardize the entrypoints supported by
+     * {@link #startContextualSearch} method.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"ENTRYPOINT_"}, value = {
+            ENTRYPOINT_LONG_PRESS_NAV_HANDLE,
+            ENTRYPOINT_LONG_PRESS_HOME,
+            ENTRYPOINT_LONG_PRESS_OVERVIEW,
+            ENTRYPOINT_OVERVIEW_ACTION,
+            ENTRYPOINT_OVERVIEW_MENU,
+            ENTRYPOINT_SYSTEM_ACTION,
+            ENTRYPOINT_LONG_PRESS_META
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Entrypoint {
+    }
+    private static final String TAG = ContextualSearchManager.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private final IContextualSearchManager mService;
+
+    /** @hide */
+    public ContextualSearchManager() {
+        if (DEBUG) Log.d(TAG, "ContextualSearchManager created");
+        IBinder b = ServiceManager.getService(Context.CONTEXTUAL_SEARCH_SERVICE);
+        mService = IContextualSearchManager.Stub.asInterface(b);
+    }
+
+    /**
+     * Used to start contextual search.
+     * <p>
+     *     When {@link #startContextualSearch} is called, the system server does the following:
+     *     <ul>
+     *         <li>Resolves the activity using the package name and intent filter. The package name
+     *             is fetched from the config specified in ContextualSearchManagerService.
+     *             The activity must have ACTION_LAUNCH_CONTEXTUAL_SEARCH specified in its manifest.
+     *         <li>Puts the required extras in the launch intent.
+     *         <li>Launches the activity.
+     *     </ul>
+     * </p>
+     *
+     * @param entrypoint the invocation entrypoint
+     */
+    @RequiresPermission(ACCESS_CONTEXTUAL_SEARCH)
+    public void startContextualSearch(@Entrypoint int entrypoint) {
+        if (DEBUG) Log.d(TAG, "startContextualSearch for entrypoint: " + entrypoint);
+        try {
+            mService.startContextualSearch(entrypoint);
+        } catch (RemoteException e) {
+            if (DEBUG) Log.d(TAG, "Failed to startContextualSearch", e);
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the {@link ContextualSearchState} to the handler via the provided callback.
+     *
+     * @param token The caller is expected to get the token from the launch extras of the handling
+     *              activity using {@link Bundle#getIBinder} with {@link #EXTRA_TOKEN} key.
+     *              <br>
+     *              <b>Note</b> This token is for one time use only. Subsequent uses will invoke
+     *              callback's {@link OutcomeReceiver#onError}.
+     * @param executor The executor which will be used to invoke the callback.
+     * @param callback The callback which will be used to return {@link ContextualSearchState}
+     *                 if/when it is available via {@link OutcomeReceiver#onResult}. It will also be
+     *                 used to return errors via {@link OutcomeReceiver#onError}.
+     */
+    public void getContextualSearchState(@NonNull IBinder token,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) {
+        if (DEBUG) Log.d(TAG, "getContextualSearchState for token:" + token);
+        try {
+            final CallbackWrapper wrapper = new CallbackWrapper(executor, callback);
+            mService.getContextualSearchState(token, wrapper);
+        } catch (RemoteException e) {
+            if (DEBUG) Log.d(TAG, "Failed to getContextualSearchState", e);
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    private static class CallbackWrapper extends IContextualSearchCallback.Stub {
+        private final OutcomeReceiver<ContextualSearchState, Throwable> mCallback;
+        private final Executor mExecutor;
+
+        CallbackWrapper(@NonNull Executor callbackExecutor,
+                        @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) {
+            mCallback = callback;
+            mExecutor = callbackExecutor;
+        }
+
+        @Override
+        public void onResult(ContextualSearchState state) {
+            Binder.withCleanCallingIdentity(() -> {
+                if (DEBUG) Log.d(TAG, "onResult state:" + state);
+                mExecutor.execute(() -> mCallback.onResult(state));
+            });
+        }
+
+        @Override
+        public void onError(ParcelableException error) {
+            Binder.withCleanCallingIdentity(() -> {
+                if (DEBUG) Log.w(TAG, "onError", error);
+                mExecutor.execute(() -> mCallback.onError(error));
+            });
+        }
+    }
+}
diff --git a/core/java/android/app/contextualsearch/ContextualSearchState.aidl b/core/java/android/app/contextualsearch/ContextualSearchState.aidl
new file mode 100644
index 0000000..7f64484
--- /dev/null
+++ b/core/java/android/app/contextualsearch/ContextualSearchState.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.app.contextualsearch;
+
+parcelable ContextualSearchState;
\ No newline at end of file
diff --git a/core/java/android/app/contextualsearch/ContextualSearchState.java b/core/java/android/app/contextualsearch/ContextualSearchState.java
new file mode 100644
index 0000000..5c04bc89
--- /dev/null
+++ b/core/java/android/app/contextualsearch/ContextualSearchState.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.contextualsearch;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.contextualsearch.flags.Flags;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/**
+ * {@link ContextualSearchState} contains additional data a contextual search handler can request
+ * via {@link ContextualSearchManager#getContextualSearchState} method.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_SERVICE)
+@SystemApi
+public final class ContextualSearchState implements Parcelable {
+    private final @NonNull Bundle mExtras;
+    private final @Nullable AssistStructure mStructure;
+    private final @Nullable AssistContent mContent;
+
+    public ContextualSearchState(@Nullable AssistStructure structure,
+            @Nullable AssistContent content, @NonNull Bundle extras) {
+        mStructure = structure;
+        mContent = content;
+        mExtras = extras;
+    }
+
+    private ContextualSearchState(Parcel source) {
+        this.mStructure = source.readTypedObject(AssistStructure.CREATOR);
+        this.mContent = source.readTypedObject(AssistContent.CREATOR);
+        Bundle extras = source.readBundle(getClass().getClassLoader());
+        this.mExtras = extras != null ? extras : Bundle.EMPTY;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(this.mStructure, flags);
+        dest.writeTypedObject(this.mContent, flags);
+        dest.writeBundle(this.mExtras);
+    }
+
+    /** Gets an instance of {@link AssistContent}. */
+    @Nullable
+    public AssistContent getContent() {
+        return mContent;
+    }
+
+    /** Gets an instance of {@link AssistStructure}. */
+    @Nullable
+    public AssistStructure getStructure() {
+        return mStructure;
+    }
+
+    /**
+     * Gets an instance of {@link Bundle} containing the extras added by the system server.
+     * The contents of this bundle vary by usecase. When Contextual is invoked via Launcher, this
+     * bundle is empty.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @NonNull
+    public static final Creator<ContextualSearchState> CREATOR = new Creator<>() {
+        @Override
+        public ContextualSearchState createFromParcel(Parcel source) {
+            return new ContextualSearchState(source);
+        }
+
+        @Override
+        public ContextualSearchState[] newArray(int size) {
+            return new ContextualSearchState[size];
+        }
+    };
+}
diff --git a/core/java/android/app/contextualsearch/IContextualSearchCallback.aidl b/core/java/android/app/contextualsearch/IContextualSearchCallback.aidl
new file mode 100644
index 0000000..5fe5fd2
--- /dev/null
+++ b/core/java/android/app/contextualsearch/IContextualSearchCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.app.contextualsearch;
+
+import android.os.ParcelableException;
+import android.app.contextualsearch.ContextualSearchState;
+/**
+ * @hide
+ */
+oneway interface IContextualSearchCallback {
+    void onResult(in ContextualSearchState state);
+    void onError(in ParcelableException error);
+}
diff --git a/core/java/android/app/contextualsearch/IContextualSearchManager.aidl b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl
new file mode 100644
index 0000000..1735a71
--- /dev/null
+++ b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl
@@ -0,0 +1,11 @@
+package android.app.contextualsearch;
+
+
+import android.app.contextualsearch.IContextualSearchCallback;
+/**
+ * @hide
+ */
+oneway interface IContextualSearchManager {
+  void startContextualSearch(int entrypoint);
+  void getContextualSearchState(in IBinder token, in IContextualSearchCallback callback);
+}
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
new file mode 100644
index 0000000..5ab0762
--- /dev/null
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.app.contextualsearch.flags"
+
+flag {
+  name: "enable_service"
+  namespace: "machine_learning"
+  description: "Flag to enable the service"
+  bug: "309689654"
+}
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
index 3e543d2..c275cc7 100644
--- a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
+++ b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
@@ -75,7 +75,11 @@
 
 
     /**
-     * Sends a custom signal with the provided parameters. It also signals the remote callback
+     * Sends a custom signal with the provided parameters. If there are multiple concurrent
+     * requests to this method, the actionParams are queued in a blocking fashion, in the order they
+     * are received.
+     *
+     * It also signals the remote callback
      * with the same params if already configured, if not the action is queued to be sent when a
      * remote is configured. Similarly, on the receiver side, the callback will be invoked if
      * already set, if not all actions are queued to be sent to callback when it is set.
@@ -159,9 +163,9 @@
      * Sets the remote transport.
      *
      * If there are actions queued from {@link ProcessingSignal#sendSignal}, they are also
-     * sequentially sent to the remote.
+     * sequentially sent to the configured remote.
      *
-     * This method is guaranteed that the remote transport will not be called after it
+     * This method guarantees that the remote transport will not be called after it
      * has been removed.
      *
      * @param remote The remote transport, or null to remove.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7f2ec53..1653bf5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5396,6 +5396,19 @@
     public static final String SMARTSPACE_SERVICE = "smartspace";
 
     /**
+     * Used for getting the contextual search service.
+     *
+     * <p><b>NOTE: </b> this service is optional; callers of
+     * {@code Context.getSystemServiceName(CONTEXTUAL_SEARCH_SERVICE)} must check for {@code null}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @FlaggedApi(android.app.contextualsearch.flags.Flags.FLAG_ENABLE_SERVICE)
+    @SystemApi
+    public static final String CONTEXTUAL_SEARCH_SERVICE = "contextual_search";
+
+    /**
      * Used for getting the cloudsearch service.
      *
      * <p><b>NOTE: </b> this service is optional; callers of
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 443aadd..8bc5e8c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6129,10 +6129,14 @@
      * the selection changes made by the user.
      * Applications may implement this method to change any of the following Chooser arguments by
      * returning new values in the result bundle:
-     * {@link #EXTRA_CHOOSER_TARGETS}, {@link #EXTRA_ALTERNATE_INTENTS},
+     * {@link #EXTRA_CHOOSER_TARGETS},
+     * {@link #EXTRA_ALTERNATE_INTENTS},
      * {@link #EXTRA_CHOOSER_CUSTOM_ACTIONS},
      * {@link #EXTRA_CHOOSER_MODIFY_SHARE_ACTION},
-     * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p>
+     * {@link #EXTRA_METADATA_TEXT},
+     * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER},
+     * {@link #EXTRA_CHOOSER_RESULT_INTENT_SENDER}.
+     * </p>
      */
     @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING)
     public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI =
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 535cebb..bd04634 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1280,26 +1280,6 @@
             264301586L; // buganizer id
 
     /**
-     * Excludes the packages the override is applied to from the camera compatibility treatment
-     * in free-form windowing mode for fixed-orientation apps.
-     *
-     * <p>In free-form windowing mode, the compatibility treatment emulates running on a portrait
-     * device by letterboxing the app window and changing the camera characteristics to what apps
-     * commonly expect in a portrait device: 90 and 270 degree sensor rotation for back and front
-     * cameras, respectively, and setting display rotation to 0.
-     *
-     * <p>Use this flag to disable the compatibility treatment for apps that do not respond well to
-     * the treatment.
-     *
-     * @hide
-     */
-    @ChangeId
-    @Overridable
-    @Disabled
-    public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT =
-            314961188L;
-
-    /**
      * This change id forces the packages it is applied to sandbox {@link android.view.View} API to
      * an activity bounds for:
      *
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 3a5383d..39b9149 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -693,7 +693,12 @@
      *
      * <p>If the caller is running on a managed profile, it'll return only the current profile.
      * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
+     *
+     * <p> To get hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public List<UserHandle> getProfiles() {
         if (mUserManager.isManagedProfile()
                 || (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()
@@ -751,11 +756,17 @@
      * list.</li>
      * </ul>
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param packageName The specific package to query. If null, it checks all installed packages
      *            in the profile.
      * @param user The UserHandle of the profile.
      * @return List of launchable activities. Can be an empty list but will not be null.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
@@ -925,10 +936,16 @@
      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
      * returns null.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param intent The intent to find a match for.
      * @param user The profile to look in for a match.
      * @return An activity info object if there is a match.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
@@ -978,11 +995,17 @@
     /**
      * Starts a Main activity in the specified profile.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param component The ComponentName of the activity to launch
      * @param user The UserHandle of the profile
      * @param sourceBounds The Rect containing the source bounds of the clicked icon
      * @param opts Options to pass to startActivity
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
             Bundle opts) {
         logErrorForInvalidProfileAccess(user);
@@ -1020,11 +1043,17 @@
      * Starts the settings activity to show the application details for a
      * package in the specified profile.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param component The ComponentName of the package to launch settings for.
      * @param user The UserHandle of the profile
      * @param sourceBounds The Rect containing the source bounds of the clicked icon
      * @param opts Options to pass to startActivity
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public void startAppDetailsActivity(ComponentName component, UserHandle user,
             Rect sourceBounds, Bundle opts) {
         logErrorForInvalidProfileAccess(user);
@@ -1135,11 +1164,17 @@
     /**
      * Checks if the package is installed and enabled for a profile.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param packageName The package to check.
      * @param user The UserHandle of the profile.
      *
      * @return true if the package exists and is enabled.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public boolean isPackageEnabled(String packageName, UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
@@ -1157,6 +1192,10 @@
      * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
      * app and the launcher.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * <p>Note: This just returns whatever extras were provided to the system, <em>which might
      * even be {@code null}.</em>
      *
@@ -1168,6 +1207,8 @@
      * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
      * @see PackageManager#isPackageSuspended()
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
@@ -1182,10 +1223,16 @@
      * could be done because the package was marked as distracting to the user via
      * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param packageName The package for which to check.
      * @param user the {@link UserHandle} of the profile.
      * @return
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public boolean shouldHideFromSuggestions(@NonNull String packageName,
             @NonNull UserHandle user) {
         Objects.requireNonNull(packageName, "packageName");
@@ -1200,6 +1247,10 @@
     /**
      * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param packageName The package name of the application
      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
      * @param user The UserHandle of the profile.
@@ -1208,6 +1259,8 @@
      *         {@code null} if the package isn't installed for the given profile, or the profile
      *         isn't enabled.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public ApplicationInfo getApplicationInfo(@NonNull String packageName,
             @ApplicationInfoFlagsBits int flags, @NonNull UserHandle user)
             throws PackageManager.NameNotFoundException {
@@ -1257,11 +1310,17 @@
      * <p>The activity may still not be exported, in which case {@link #startMainActivity} will
      * throw a {@link SecurityException} unless the caller has the same UID as the target app's.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @param component The activity to check.
      * @param user The UserHandle of the profile.
      *
      * @return true if the activity exists and is enabled.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public boolean isActivityEnabled(ComponentName component, UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
@@ -1816,8 +1875,14 @@
     /**
      * Registers a callback for changes to packages in this user and managed profiles.
      *
+     * <p>To receive callbacks for hidden profile{@link UserManager.USER_TYPE_PROFILE_PRIVATE},
+     * caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+     * permissions required.
+     *
      * @param callback The callback to register.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public void registerCallback(Callback callback) {
         registerCallback(callback, null);
     }
@@ -1825,9 +1890,15 @@
     /**
      * Registers a callback for changes to packages in this user and managed profiles.
      *
+     * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
+     * caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+     * permissions required.
+     *
      * @param callback The callback to register.
      * @param handler that should be used to post callbacks on, may be null.
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public void registerCallback(Callback callback, Handler handler) {
         synchronized (this) {
             if (callback != null && findCallbackLocked(callback) < 0) {
@@ -2275,8 +2346,14 @@
      * package name in the app's manifest, have the android.permission.QUERY_ALL_PACKAGES, or be
      * the session owner to retrieve these details.
      *
+     * <p>If the user in question is a hidden profile
+     * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+     * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+     *
      * @see PackageInstaller#getAllSessions()
      */
+    @RequiresPermission(conditional = true,
+            anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
     public @NonNull List<SessionInfo> getAllPackageInstallerSessions() {
         try {
             return mService.getAllSessions(mContext.getPackageName()).getList();
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 749e0f8..2d32aed 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -193,3 +193,10 @@
     bug: "290333800"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "delete_private_space_from_reset"
+    namespace: "profile_experiences"
+    description: "Add entrypoint in Settings Reset options for deleting private space when lock is forgotten"
+    bug: "329601751"
+}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index dfb77c0..faa2c70 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -34,22 +34,17 @@
 import android.util.LruCache;
 import android.util.Pair;
 import android.util.Printer;
-import com.android.internal.util.RingBuffer;
 import dalvik.system.BlockGuard;
 import dalvik.system.CloseGuard;
-
 import java.io.File;
 import java.io.IOException;
 import java.lang.ref.Reference;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.Locale;
 import java.util.Map;
 import java.util.function.BinaryOperator;
 import java.util.function.UnaryOperator;
@@ -190,7 +185,7 @@
             SQLiteDatabaseConfiguration configuration,
             int connectionId, boolean primaryConnection) {
         mPool = pool;
-        mRecentOperations = new OperationLog();
+        mRecentOperations = new OperationLog(mPool);
         mConfiguration = new SQLiteDatabaseConfiguration(configuration);
         mConnectionId = connectionId;
         mIsPrimaryConnection = primaryConnection;
@@ -312,16 +307,6 @@
         }
     }
 
-    /** Record the start of a transaction for logging and debugging. */
-    void recordBeginTransaction(String mode) {
-        mRecentOperations.beginTransaction(mode);
-    }
-
-    /** Record the end of a transaction for logging and debugging. */
-    void recordEndTransaction(boolean successful) {
-        mRecentOperations.endTransaction(successful);
-    }
-
     private void setPageSize() {
         if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
             final long newValue = SQLiteGlobal.getDefaultPageSize();
@@ -1352,7 +1337,6 @@
         }
         printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
         printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
-        printer.println("  totalLongOperations: " + mRecentOperations.getTotalLongOperations());
 
         mRecentOperations.dump(printer);
 
@@ -1611,39 +1595,51 @@
         }
     }
 
-    private final class OperationLog {
+    private static final class OperationLog {
         private static final int MAX_RECENT_OPERATIONS = 20;
         private static final int COOKIE_GENERATION_SHIFT = 8;
         private static final int COOKIE_INDEX_MASK = 0xff;
 
-        // Operations over 2s are long.  Save the last ten.
-        private static final long LONG_OPERATION_THRESHOLD_MS = 2_000;
-        private static final int MAX_LONG_OPERATIONS = 10;
-
         private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS];
-        private int mIndex = -1;
-        private int mGeneration = 0;
-        private final Operation mTransaction = new Operation();
+        private int mIndex;
+        private int mGeneration;
+        private final SQLiteConnectionPool mPool;
         private long mResultLong = Long.MIN_VALUE;
         private String mResultString;
 
-        private final RingBuffer<Operation> mLongOperations =
-                new RingBuffer<>(()->{return new Operation();},
-                        (n) ->{return new Operation[n];},
-                        MAX_LONG_OPERATIONS);
-        private int mTotalLongOperations = 0;
+        OperationLog(SQLiteConnectionPool pool) {
+            mPool = pool;
+        }
 
         public int beginOperation(String kind, String sql, Object[] bindArgs) {
             mResultLong = Long.MIN_VALUE;
             mResultString = null;
 
             synchronized (mOperations) {
-                Operation operation = newOperationLocked();
+                final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
+                Operation operation = mOperations[index];
+                if (operation == null) {
+                    operation = new Operation();
+                    mOperations[index] = operation;
+                } else {
+                    operation.mFinished = false;
+                    operation.mException = null;
+                    if (operation.mBindArgs != null) {
+                        operation.mBindArgs.clear();
+                    }
+                }
+                operation.mStartWallTime = System.currentTimeMillis();
+                operation.mStartTime = SystemClock.uptimeMillis();
                 operation.mKind = kind;
                 operation.mSql = sql;
+                operation.mPath = mPool.getPath();
+                operation.mResultLong = Long.MIN_VALUE;
+                operation.mResultString = null;
                 if (bindArgs != null) {
                     if (operation.mBindArgs == null) {
                         operation.mBindArgs = new ArrayList<Object>();
+                    } else {
+                        operation.mBindArgs.clear();
                     }
                     for (int i = 0; i < bindArgs.length; i++) {
                         final Object arg = bindArgs[i];
@@ -1655,44 +1651,16 @@
                         }
                     }
                 }
+                operation.mCookie = newOperationCookieLocked(index);
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
                     Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
                             operation.mCookie);
                 }
+                mIndex = index;
                 return operation.mCookie;
             }
         }
 
-        public void beginTransaction(String kind) {
-            synchronized (mOperations) {
-                Operation operation = newOperationLocked();
-                operation.mKind = kind;
-                mTransaction.copyFrom(operation);
-
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
-                    Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
-                            operation.mCookie);
-                }
-            }
-        }
-
-        /**
-         * Fetch a new operation from the ring buffer.  The operation is properly initialized.
-         * This advances mIndex to point to the next element.
-         */
-        private Operation newOperationLocked() {
-            final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
-            Operation operation = mOperations[index];
-            if (operation == null) {
-                mOperations[index] = new Operation();
-                operation = mOperations[index];
-            }
-            operation.start();
-            operation.mCookie = newOperationCookieLocked(index);
-            mIndex = index;
-            return operation;
-        }
-
         public void failOperation(int cookie, Exception ex) {
             synchronized (mOperations) {
                 final Operation operation = getOperationLocked(cookie);
@@ -1716,20 +1684,6 @@
             }
         }
 
-        public boolean endTransaction(boolean success) {
-            synchronized (mOperations) {
-                mTransaction.mResultLong = success ? 1 : 0;
-                final long execTime = finishOperationLocked(mTransaction);
-                final Operation operation = getOperationLocked(mTransaction.mCookie);
-                if (operation != null) {
-                    operation.copyFrom(mTransaction);
-                }
-                mTransaction.setEmpty();
-                return NoPreloadHolder.DEBUG_LOG_SLOW_QUERIES
-                        && SQLiteDebug.shouldLogSlowQuery(execTime);
-            }
-        }
-
         public void logOperation(int cookie, String detail) {
             synchronized (mOperations) {
                 logOperationLocked(cookie, detail);
@@ -1751,7 +1705,9 @@
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
                             operation.mCookie);
                 }
-                final long execTime = finishOperationLocked(operation);
+                operation.mEndTime = SystemClock.uptimeMillis();
+                operation.mFinished = true;
+                final long execTime = operation.mEndTime - operation.mStartTime;
                 mPool.onStatementExecuted(execTime);
                 return NoPreloadHolder.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
                         execTime);
@@ -1776,22 +1732,10 @@
             return generation << COOKIE_GENERATION_SHIFT | index;
         }
 
-        /** Close out the operation and return the elapsed time. */
-        private long finishOperationLocked(Operation operation) {
-            operation.mEndTime = SystemClock.uptimeMillis();
-            operation.mFinished = true;
-            final long elapsed = operation.mEndTime - operation.mStartTime;
-            if (elapsed > LONG_OPERATION_THRESHOLD_MS) {
-                mLongOperations.getNextSlot().copyFrom(operation);
-                mTotalLongOperations++;
-            }
-            return elapsed;
-        }
-
         private Operation getOperationLocked(int cookie) {
             final int index = cookie & COOKIE_INDEX_MASK;
             final Operation operation = mOperations[index];
-            return (operation != null && operation.mCookie == cookie) ? operation : null;
+            return operation.mCookie == cookie ? operation : null;
         }
 
         public String describeCurrentOperation() {
@@ -1806,87 +1750,48 @@
             }
         }
 
-        /**
-         * Dump an Operation if it is not in the recent operations list.  Return 1 if the
-         * operation was dumped and 0 if not.
-         */
-        private int dumpIfNotRecentLocked(Printer pw, Operation op, int counter) {
-            if (op == null || op.isEmpty() || getOperationLocked(op.mCookie) != null) {
-                return 0;
-            }
-            pw.println(op.describe(counter));
-            return 1;
-        }
-
-        private void dumpRecentLocked(Printer printer) {
+        public void dump(Printer printer) {
             synchronized (mOperations) {
                 printer.println("  Most recently executed operations:");
                 int index = mIndex;
-                if (index == 0) {
-                    printer.println("    <none>");
-                    return;
-                }
-
-                // Operations are dumped in order of most recent first.
-                int counter = 0;
-                int n = 0;
                 Operation operation = mOperations[index];
-                do {
-                    printer.println(operation.describe(counter));
+                if (operation != null) {
+                    // Note: SimpleDateFormat is not thread-safe, cannot be compile-time created,
+                    // and is relatively expensive to create during preloading. This method is only
+                    // used when dumping a connection, which is a rare (mainly error) case.
+                    SimpleDateFormat opDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+                    int n = 0;
+                    do {
+                        StringBuilder msg = new StringBuilder();
+                        msg.append("    ").append(n).append(": [");
+                        String formattedStartTime = opDF.format(new Date(operation.mStartWallTime));
+                        msg.append(formattedStartTime);
+                        msg.append("] ");
+                        operation.describe(msg, false); // Never dump bingargs in a bugreport
+                        printer.println(msg.toString());
 
-                    if (index > 0) {
-                        index -= 1;
-                    } else {
-                        index = MAX_RECENT_OPERATIONS - 1;
-                    }
-                    n++;
-                    counter++;
-                    operation = mOperations[index];
-                } while (operation != null && n < MAX_RECENT_OPERATIONS);
-                counter += dumpIfNotRecentLocked(printer, mTransaction, counter);
-            }
-        }
-
-        private void dumpLongLocked(Printer printer) {
-            printer.println("  Operations exceeding " + LONG_OPERATION_THRESHOLD_MS + "ms:");
-            if (mLongOperations.isEmpty()) {
-                printer.println("    <none>");
-                return;
-            }
-            Operation[] longOps = mLongOperations.toArray();
-            for (int i = 0; i < longOps.length; i++) {
-                if (longOps[i] != null) {
-                    printer.println(longOps[i].describe(i));
+                        if (index > 0) {
+                            index -= 1;
+                        } else {
+                            index = MAX_RECENT_OPERATIONS - 1;
+                        }
+                        n += 1;
+                        operation = mOperations[index];
+                    } while (operation != null && n < MAX_RECENT_OPERATIONS);
+                } else {
+                    printer.println("    <none>");
                 }
             }
         }
-
-        public long getTotalLongOperations() {
-            return mTotalLongOperations;
-        }
-
-        public void dump(Printer printer) {
-            synchronized (mOperations) {
-                dumpRecentLocked(printer);
-                dumpLongLocked(printer);
-            }
-        }
     }
 
-    private final class Operation {
+    private static final class Operation {
         // Trim all SQL statements to 256 characters inside the trace marker.
         // This limit gives plenty of context while leaving space for other
         // entries in the trace buffer (and ensures atrace doesn't truncate the
         // marker for us, potentially losing metadata in the process).
         private static final int MAX_TRACE_METHOD_NAME_LEN = 256;
 
-        // The reserved start time that indicates the Operation is empty.
-        private static final long EMPTY_OPERATION = -1;
-
-        // The formatter for the timestamp.
-        private static final DateTimeFormatter sDateTime =
-                DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS", Locale.US);
-
         public long mStartWallTime; // in System.currentTimeMillis()
         public long mStartTime; // in SystemClock.uptimeMillis();
         public long mEndTime; // in SystemClock.uptimeMillis();
@@ -1896,58 +1801,16 @@
         public boolean mFinished;
         public Exception mException;
         public int mCookie;
+        public String mPath;
         public long mResultLong; // MIN_VALUE means "value not set".
         public String mResultString;
 
-        /** Reset the object to begin a new operation. */
-        void start() {
-            mStartWallTime = System.currentTimeMillis();
-            mStartTime = SystemClock.uptimeMillis();
-            mEndTime = Long.MIN_VALUE;
-            mKind = null;
-            mSql = null;
-            if (mBindArgs != null) mBindArgs.clear();
-            mFinished = false;
-            mException = null;
-            mCookie = -1;
-            mResultLong = Long.MIN_VALUE;
-            mResultString = null;
-        }
-
-        /**
-         * Initialize from the source object.  This is meant to clone the object for use in a
-         * transaction operation.  To that end, the local bind args are set to null.
-         */
-        void copyFrom(Operation r) {
-            mStartWallTime = r.mStartWallTime;
-            mStartTime = r.mStartTime;
-            mEndTime = r.mEndTime;
-            mKind = r.mKind;
-            mSql = r.mSql;
-            mBindArgs = null;
-            mFinished = r.mFinished;
-            mException = r.mException;
-            mCookie = r.mCookie;
-            mResultLong = r.mResultLong;
-            mResultString = r.mResultString;
-        }
-
-        /** Mark the operation empty. */
-        void setEmpty() {
-            mStartWallTime = EMPTY_OPERATION;
-        }
-
-        /** Return true if the operation is empty. */
-        boolean isEmpty() {
-            return mStartWallTime == EMPTY_OPERATION;
-        }
-
         public void describe(StringBuilder msg, boolean allowDetailedLog) {
             msg.append(mKind);
             if (mFinished) {
                 msg.append(" took ").append(mEndTime - mStartTime).append("ms");
             } else {
-                msg.append(" started ").append(SystemClock.uptimeMillis() - mStartTime)
+                msg.append(" started ").append(System.currentTimeMillis() - mStartWallTime)
                         .append("ms ago");
             }
             msg.append(" - ").append(getStatus());
@@ -1976,7 +1839,7 @@
                 }
                 msg.append("]");
             }
-            msg.append(", path=").append(mPool.getPath());
+            msg.append(", path=").append(mPath);
             if (mException != null) {
                 msg.append(", exception=\"").append(mException.getMessage()).append("\"");
             }
@@ -1988,21 +1851,6 @@
             }
         }
 
-        /**
-         * Convert a wall-clock time in milliseconds to logcat format.
-         */
-        private String timeString(long millis) {
-            return sDateTime.withZone(ZoneId.systemDefault()).format(Instant.ofEpochMilli(millis));
-        }
-
-        public String describe(int n) {
-            final StringBuilder msg = new StringBuilder();
-            final String start = timeString(mStartWallTime);
-            msg.append("    ").append(n).append(": [").append(start).append("] ");
-            describe(msg, false); // Never dump bingargs in a bugreport
-            return msg.toString();
-        }
-
         private String getStatus() {
             if (!mFinished) {
                 return "running";
@@ -2016,6 +1864,7 @@
                 return methodName.substring(0, MAX_TRACE_METHOD_NAME_LEN);
             return methodName;
         }
+
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 15d7d66..ad335b6 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1175,7 +1175,7 @@
                     + ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled
                     + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.resolveJournalMode())
                     + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.resolveSyncMode()));
-            printer.println("  IsReadOnlyDatabase: " + mConfiguration.isReadOnlyDatabase());
+            printer.println("  IsReadOnlyDatabase=" + mConfiguration.isReadOnlyDatabase());
 
             if (isCompatibilityWalEnabled) {
                 printer.println("  Compatibility WAL enabled: wal_syncmode="
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 3b14d9d..7d9f02d 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -312,15 +312,6 @@
                 cancellationSignal);
     }
 
-    private String modeString(int transactionMode) {
-        switch (transactionMode) {
-            case TRANSACTION_MODE_IMMEDIATE: return "TRANSACTION-IMMEDIATE";
-            case TRANSACTION_MODE_EXCLUSIVE: return "TRANSACTION-EXCLUSIVE";
-            case TRANSACTION_MODE_DEFERRED:  return "TRANSACTION-DEFERRED";
-            default: return "TRANSACTION";
-        }
-    }
-
     private void beginTransactionUnchecked(int transactionMode,
             SQLiteTransactionListener transactionListener, int connectionFlags,
             CancellationSignal cancellationSignal) {
@@ -330,7 +321,6 @@
 
         if (mTransactionStack == null) {
             acquireConnection(null, connectionFlags, cancellationSignal); // might throw
-            mConnection.recordBeginTransaction(modeString(transactionMode));
         }
         try {
             // Set up the transaction such that we can back out safely
@@ -475,7 +465,6 @@
                     mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                 }
             } finally {
-                mConnection.recordEndTransaction(successful);
                 releaseConnection(); // might throw
             }
         }
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index be9f0a0..a8d54ed 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -16,7 +16,7 @@
 
 package android.hardware.biometrics;
 
-import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
 import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -135,7 +135,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface DismissedReason {}
 
-    private static class ButtonInfo {
+    static class ButtonInfo {
         Executor executor;
         DialogInterface.OnClickListener listener;
         ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
@@ -150,6 +150,7 @@
     public static class Builder {
         private PromptInfo mPromptInfo;
         private ButtonInfo mNegativeButtonInfo;
+        private ButtonInfo mContentViewMoreOptionsButtonInfo;
         private Context mContext;
         private IAuthService mService;
 
@@ -175,7 +176,7 @@
          * @return This builder.
          */
         @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-        @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
             mPromptInfo.setLogoRes(logoRes);
@@ -194,7 +195,7 @@
          * @return This builder.
          */
         @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-        @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
             mPromptInfo.setLogoBitmap(logoBitmap);
@@ -213,7 +214,7 @@
          * @return This builder.
          */
         @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-        @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
             mPromptInfo.setLogoDescription(logoDescription);
@@ -301,6 +302,12 @@
         @NonNull
         public BiometricPrompt.Builder setContentView(@NonNull PromptContentView view) {
             mPromptInfo.setContentView(view);
+
+            if (mPromptInfo.isContentViewMoreOptionsButtonUsed()) {
+                mContentViewMoreOptionsButtonInfo =
+                        ((PromptContentViewWithMoreOptionsButton) view).getButtonInfo();
+            }
+
             return this;
         }
 
@@ -619,7 +626,8 @@
             }
             mService = (mService == null) ? IAuthService.Stub.asInterface(
                     ServiceManager.getService(Context.AUTH_SERVICE)) : mService;
-            return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, mService);
+            return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo,
+                    mContentViewMoreOptionsButtonInfo, mService);
         }
     }
 
@@ -646,6 +654,9 @@
     private final IAuthService mService;
     private final PromptInfo mPromptInfo;
     private final ButtonInfo mNegativeButtonInfo;
+    // TODO(b/328843028): add callback onContentViewMoreOptionsButtonClicked() in
+    //  IBiometricServiceReceiver.
+    private final ButtonInfo mContentViewMoreOptionsButtonInfo;
 
     private CryptoObject mCryptoObject;
     private Executor mExecutor;
@@ -751,10 +762,11 @@
     private boolean mIsPromptShowing;
 
     private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo,
-            IAuthService service) {
+            ButtonInfo contentViewMoreOptionsButtonInfo, IAuthService service) {
         mContext = context;
         mPromptInfo = promptInfo;
         mNegativeButtonInfo = negativeButtonInfo;
+        mContentViewMoreOptionsButtonInfo = contentViewMoreOptionsButtonInfo;
         mService = service;
         mIsPromptShowing = false;
     }
@@ -766,7 +778,7 @@
      * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
      */
     @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-    @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+    @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
     @DrawableRes
     public int getLogoRes() {
         return mPromptInfo.getLogoRes();
@@ -779,7 +791,7 @@
      * @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
      */
     @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-    @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+    @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
     @Nullable
     public Bitmap getLogoBitmap() {
         return mPromptInfo.getLogoBitmap();
@@ -794,7 +806,7 @@
      * set.
      */
     @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-    @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+    @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
     @Nullable
     public String getLogoDescription() {
         return mPromptInfo.getLogoDescription();
@@ -932,14 +944,29 @@
      * @see Builder#setAllowedAuthenticators(int)
      */
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+        /**
+         * Create from a {@link Signature} object.
+         *
+         * @param signature a {@link Signature} object.
+         */
         public CryptoObject(@NonNull Signature signature) {
             super(signature);
         }
 
+        /**
+         * Create from a {@link Cipher} object.
+         *
+         * @param cipher a {@link Cipher} object.
+         */
         public CryptoObject(@NonNull Cipher cipher) {
             super(cipher);
         }
 
+        /**
+         * Create from a {@link Mac} object.
+         *
+         * @param mac a {@link Mac} object.
+         */
         public CryptoObject(@NonNull Mac mac) {
             super(mac);
         }
@@ -955,16 +982,37 @@
             super(credential);
         }
 
+        /**
+         * Create from a {@link PresentationSession} object.
+         *
+         * @param session a {@link PresentationSession} object.
+         */
         public CryptoObject(@NonNull PresentationSession session) {
             super(session);
         }
 
+        /**
+         * Create from a {@link KeyAgreement} object.
+         *
+         * @param keyAgreement a {@link KeyAgreement} object.
+         */
         @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
         public CryptoObject(@NonNull KeyAgreement keyAgreement) {
             super(keyAgreement);
         }
 
         /**
+         * Create from an operation handle.
+         * @see CryptoObject#getOperationHandle()
+         *
+         * @param operationHandle the operation handle associated with this object.
+         */
+        @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
+        public CryptoObject(long operationHandle) {
+            super(operationHandle);
+        }
+
+        /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
          */
@@ -1016,7 +1064,20 @@
         }
 
         /**
-         * Get the operation handle associated with this object or 0 if none.
+         * Returns the {@code operationHandle} associated with this object or 0 if none.
+         * The {@code operationHandle} is the underlying identifier associated with
+         * the {@code CryptoObject}.
+         *
+         * <p> The {@code operationHandle} can be used to reconstruct a {@code CryptoObject}
+         * instance. This is useful for any cross-process communication as the {@code CryptoObject}
+         * class is not {@link android.os.Parcelable}. Hence, if the {@code CryptoObject} is
+         * constructed in one process, and needs to be propagated to another process,
+         * before calling the
+         * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
+         * AuthenticationCallback)} API in the second process, the recommendation is to retrieve the
+         * {@code operationHandle} using this API, and then reconstruct the
+         * {@code CryptoObject}using the constructor that takes in an {@code operationHandle}, and
+         * pass that in to the {@code authenticate} API mentioned above.
          */
         @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
         public long getOperationHandle() {
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 8d3ea3f..81b4c21 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -97,6 +97,10 @@
         mCrypto = keyAgreement;
     }
 
+    public CryptoObject(long operationHandle) {
+        mCrypto = operationHandle;
+    }
+
     /**
      * Get {@link Signature} object.
      * @return {@link Signature} object or null if this doesn't contain one.
@@ -157,6 +161,8 @@
     public long getOpId() {
         if (mCrypto == null) {
             return 0;
+        } else if (mCrypto instanceof Long) {
+            return (long) mCrypto;
         } else if (mCrypto instanceof IdentityCredential) {
             return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
         } else if (mCrypto instanceof PresentationSession) {
diff --git a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
index 43b965b..b5982d4 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
@@ -26,5 +26,5 @@
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 sealed interface PromptContentViewParcelable extends PromptContentView, Parcelable
-        permits PromptVerticalListContentView {
+        permits PromptVerticalListContentView, PromptContentViewWithMoreOptionsButton {
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
new file mode 100644
index 0000000..9ebfa8f
--- /dev/null
+++ b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
+import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.DialogInterface;
+import android.hardware.biometrics.BiometricPrompt.ButtonInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Contains the information of the template of content view with a more options button for Biometric
+ * Prompt.
+ * This button should be used to provide more options for sign in or other purposes, such as when a
+ * user needs to select between multiple app-specific accounts or profiles that are available for
+ * sign in. This is not common and apps should avoid using it if there is only one choice available
+ * or if the user has already selected the appropriate account to use before invoking
+ * BiometricPrompt because it will create additional steps that the user must navigate through.
+ * Clicking the more options button will dismiss the prompt, provide the app an opportunity to ask
+ * the user for the correct account, &finally allow the app to decide how to proceed once selected.
+ * <p>
+ * Here's how you'd set a <code>PromptContentViewWithMoreOptionsButton</code> on a Biometric Prompt:
+ * <pre class="prettyprint">
+ * BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(...)
+ *     .setTitle(...)
+ *     .setSubTitle(...)
+ *     .setContentView(new PromptContentViewWithMoreOptionsButton.Builder()
+ *         .setDescription("test description")
+ *         .setMoreOptionsButtonListener(executor, listener)
+ *         .build())
+ *     .build();
+ * </pre>
+ */
+@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable {
+
+    private final String mDescription;
+    private DialogInterface.OnClickListener mListener;
+    private ButtonInfo mButtonInfo;
+
+    private PromptContentViewWithMoreOptionsButton(
+            @NonNull String description, @NonNull @CallbackExecutor Executor executor,
+            @NonNull DialogInterface.OnClickListener listener) {
+        mDescription = description;
+        mListener = listener;
+        mButtonInfo = new ButtonInfo(executor, listener);
+    }
+
+    private PromptContentViewWithMoreOptionsButton(Parcel in) {
+        mDescription = in.readString();
+    }
+
+    /**
+     * Gets the description for the content view, as set by
+     * {@link PromptContentViewWithMoreOptionsButton.Builder#setDescription(String)}.
+     *
+     * @return The description for the content view, or null if the content view has no description.
+     */
+    @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+    @Nullable
+    public String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Gets the click listener for the more options button on the content view, as set by
+     * {@link PromptContentViewWithMoreOptionsButton.Builder#setMoreOptionsButtonListener(Executor,
+     * DialogInterface.OnClickListener)}.
+     *
+     * @return The click listener for the more options button on the content view.
+     */
+    @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+    @NonNull
+    public DialogInterface.OnClickListener getMoreOptionsButtonListener() {
+        return mListener;
+    }
+
+    ButtonInfo getButtonInfo() {
+        return mButtonInfo;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mDescription);
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<PromptContentViewWithMoreOptionsButton> CREATOR = new Creator<>() {
+        @Override
+        public PromptContentViewWithMoreOptionsButton createFromParcel(Parcel in) {
+            return new PromptContentViewWithMoreOptionsButton(in);
+        }
+
+        @Override
+        public PromptContentViewWithMoreOptionsButton[] newArray(int size) {
+            return new PromptContentViewWithMoreOptionsButton[size];
+        }
+    };
+
+    public static final class Builder {
+        private String mDescription;
+        private Executor mExecutor;
+        private DialogInterface.OnClickListener mListener;
+
+        /**
+         * Optional: Sets a description that will be shown on the content view.  Note that there are
+         * limits on the number of characters allowed for description.
+         *
+         * @param description The description to display.
+         * @return This builder.
+         */
+        @NonNull
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        public Builder setDescription(@NonNull String description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Required: Sets the executor and click listener for the more options button on the
+         * prompt content.
+         * This button should be used to provide more options for sign in or other purposes, such
+         * as when a user needs to select between multiple app-specific accounts or profiles that
+         * are available for sign in. This is not common and apps should avoid using it if there
+         * is only one choice available or if the user has already selected the appropriate
+         * account to use before invoking BiometricPrompt because it will create additional steps
+         * that the user must navigate through. Clicking the more options button will dismiss the
+         * prompt, provide the app an opportunity to ask the user for the correct account, &finally
+         * allow the app to decide how to proceed once selected.
+         *
+         * @param executor Executor that will be used to run the on click callback.
+         * @param listener Listener containing a callback to be run when the button is pressed.
+         * @return This builder.
+         */
+        @NonNull
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        public Builder setMoreOptionsButtonListener(@NonNull @CallbackExecutor Executor executor,
+                @NonNull DialogInterface.OnClickListener listener) {
+            if (executor == null) {
+                throw new IllegalArgumentException("Executor must not be null");
+            }
+            if (listener == null) {
+                throw new IllegalArgumentException("Listener must not be null");
+            }
+            mExecutor = executor;
+            mListener = listener;
+            return this;
+        }
+
+
+        /**
+         * Creates a {@link PromptContentViewWithMoreOptionsButton}.
+         *
+         * @return An instance of {@link PromptContentViewWithMoreOptionsButton}.
+         */
+        @NonNull
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        public PromptContentViewWithMoreOptionsButton build() {
+            if (mListener == null) {
+                throw new IllegalArgumentException(
+                        "The listener of more options button on prompt content must be set if "
+                                + "PromptContentViewWithMoreOptionsButton is used.");
+            }
+            return new PromptContentViewWithMoreOptionsButton(mDescription, mExecutor, mListener);
+        }
+    }
+}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 8bb9585..8143213 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -172,7 +172,7 @@
     }
 
     /**
-     * Returns whether SET_BIOMETRIC_DIALOG_LOGO is contained.
+     * Returns whether SET_BIOMETRIC_DIALOG_ADVANCED is contained.
      */
     public boolean containsSetLogoApiConfigurations() {
         if (mLogoRes != -1) {
@@ -191,6 +191,15 @@
     public boolean shouldUseParentProfileForDeviceCredential() {
         return mUseParentProfileForDeviceCredential;
     }
+
+    /**
+     * Returns if the PromptContentViewWithMoreOptionsButton is set.
+     */
+    public boolean isContentViewMoreOptionsButtonUsed() {
+        return Flags.customBiometricPrompt() && mContentView != null
+                && mContentView instanceof PromptContentViewWithMoreOptionsButton;
+    }
+
     // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
 
     // Setters
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 991bade..ec9b013 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -585,6 +585,11 @@
      * <p>Configuring a session with an empty or null list will close the current session, if
      * any. This can be used to release the current session's target surfaces for another use.</p>
      *
+     * <p>This function throws an {@code IllegalArgumentException} if called with a
+     * SessionConfiguration lacking state callbacks or valid output surfaces. The only exceptions
+     * are deferred SurfaceView or SurfaceTexture outputs. See {@link
+     * OutputConfiguration#OutputConfiguration(Size, Class)} for details.</p>
+     *
      * <h3>Regular capture</h3>
      *
      * <p>While any of the sizes from {@link StreamConfigurationMap#getOutputSizes} can be used when
@@ -1675,19 +1680,32 @@
          *
          * <p><b>IMPORTANT:</b></p>
          * <ul>
-         * <li>If a feature support can be queried via
+         * <li>If feature support can be queried via
          * {@link CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS} or
          * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}, applications should
-         * directly use it rather than calling this function as: (1) using
-         * {@code CameraCharacteristics} is more efficient, and (2) calling this function with on
-         * non-supported devices will throw a {@link UnsupportedOperationException}.
+         * directly use that route rather than calling this function as: (1) using
+         * {@code CameraCharacteristics} is more efficient, and (2) calling this function with
+         * certain non-supported features will throw a {@link IllegalArgumentException}.</li>
          *
-         * <li>To minimize latency of {@link SessionConfiguration} creation, applications can
-         * use deferred surfaces for SurfaceView and SurfaceTexture to avoid waiting for UI
-         * creation before setting up the camera. For {@link android.media.MediaRecorder} and
-         * {@link android.media.MediaCodec} uses, applications can use {@code ImageReader} with
-         * {@link android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE}. The lightweight nature of
-         * {@code ImageReader} helps minimize the latency cost.
+         * <li>To minimize {@link SessionConfiguration} creation latency due to its dependency on
+         * output surfaces, the application can call this method before acquiring valid
+         * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture},
+         * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec}, or {@link
+         * android.media.ImageReader} surfaces. For {@link android.view.SurfaceView},
+         * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, and
+         * {@link android.media.MediaCodec}, the application can call
+         * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. For {@link
+         * android.media.ImageReader}, the application can call {@link
+         * OutputConfiguration#OutputConfiguration(int, Size)}, {@link
+         * OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
+         * OutputConfiguration#OutputConfiguration(int, Size, long)}, or {@link
+         * OutputConfiguration#OutputConfiguration(int, int, Size, long)}. The {@link
+         * SessionConfiguration} can then be created using the OutputConfiguration objects and
+         * be used to query whether it's supported by the camera device. To create the
+         * CameraCaptureSession, the application still needs to make sure all output surfaces
+         * are added via {@link OutputConfiguration#addSurfaces} with the exception of deferred
+         * surfaces for {@link android.view.SurfaceView} and
+         * {@link android.graphics.SurfaceTexture}.</li>
          * </ul>
          *
          * @return {@code true} if the given session configuration is supported by the camera
@@ -1706,8 +1724,8 @@
                 @NonNull SessionConfiguration config) throws CameraAccessException;
 
         /**
-         * <p>Get camera characteristics for a particular session configuration for this camera
-         * device</p>
+         * Get camera characteristics for a particular session configuration for this camera
+         * device.
          *
          * <p>The camera characteristics returned by this method are different from those returned
          * from {@link CameraManager#getCameraCharacteristics}. The characteristics returned here
@@ -1718,6 +1736,24 @@
          * <p>Other than that, the characteristics returned here can be used in the same way as
          * those returned from {@link CameraManager#getCameraCharacteristics}.</p>
          *
+         * <p>To optimize latency, the application can call this method before acquiring valid
+         * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture},
+         * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec}, or {@link
+         * android.media.ImageReader} surfaces. For {@link android.view.SurfaceView},
+         * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, and
+         * {@link android.media.MediaCodec}, the application can call
+         * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. For {@link
+         * android.media.ImageReader}, the application can call {@link
+         * OutputConfiguration#OutputConfiguration(int, Size)}, {@link
+         * OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
+         * OutputConfiguration#OutputConfiguration(int, Size, long)}, or {@link
+         * OutputConfiguration#OutputConfiguration(int, int, Size, long)}. The {@link
+         * SessionConfiguration} can then be created using the OutputConfiguration objects and
+         * be used for this function. To create the CameraCaptureSession, the application still
+         * needs to make sure all output surfaces are added via {@link
+         * OutputConfiguration#addSurfaces} with the exception of deferred surfaces for {@link
+         * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}.</p>
+         *
          * @param sessionConfig The session configuration for which characteristics are fetched.
          * @return CameraCharacteristics specific to a given session configuration.
          *
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 6962811..eb644e8 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -229,9 +229,10 @@
                                                      StreamConfigurationMap streamMap) {
         ArrayList<Size> ret = getSupportedSizes(sizesList, format);
 
-        if (format == ImageFormat.JPEG || format == ImageFormat.YUV_420_888) {
+        if (format == ImageFormat.JPEG || format == ImageFormat.YUV_420_888 ||
+                format == ImageFormat.PRIVATE) {
             // Per API contract it is assumed that the extension is able to support all
-            // camera advertised sizes for JPEG and YUV_420_888 in case it doesn't return
+            // camera advertised sizes for JPEG, YUV_420_888 and PRIVATE in case it doesn't return
             // a valid non-empty size list.
             Size[] supportedSizes = streamMap.getOutputSizes(format);
             if ((ret.isEmpty()) && (supportedSizes != null)) {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index d2e4a61..b43a900 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -47,6 +47,7 @@
 import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
 import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
 import android.hardware.camera2.utils.ExceptionUtils;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManager;
 import android.os.Binder;
@@ -204,14 +205,12 @@
             mDeviceStateListeners.add(new WeakReference<>(listener));
         }
 
+        @SuppressWarnings("FlaggedApi")
         @Override
-        public final void onBaseStateChanged(int state) {
-            handleStateChange(state);
-        }
-
-        @Override
-        public final void onStateChanged(int state) {
-            handleStateChange(state);
+        public void onDeviceStateChanged(DeviceState state) {
+            // Suppressing the FlaggedAPI warning as this specific API isn't new, just moved to
+            // system API which requires it to be flagged.
+            handleStateChange(state.getIdentifier());
         }
     }
 
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
index 8a18a0d..116928b 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -16,6 +16,8 @@
 
 package android.hardware.camera2;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
@@ -26,20 +28,15 @@
 import android.graphics.ImageFormat.Format;
 import android.hardware.HardwareBuffer;
 import android.hardware.HardwareBuffer.Usage;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.media.Image;
 import android.media.ImageReader;
-import android.hardware.camera2.params.MultiResolutionStreamInfo;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
+import android.util.Size;
 import android.view.Surface;
 
 import com.android.internal.camera.flags.Flags;
 
-import java.nio.NioUtils;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -351,6 +348,52 @@
     }
 
     /**
+     * Get the internal ImageReader surface based on configured size and physical camera Id.
+     *
+     * <p>The {@code configuredSize} and {@code physicalCameraId} parameters must match one of the
+     * MultiResolutionStreamInfo used to create this {@link MultiResolutionImageReader}.</p>
+     *
+     * <p>The Surface returned from this function isn't meant to be used directly as part of a
+     * {@link CaptureRequest}. It should instead be used for creating an OutputConfiguration
+     * before session creation. See {@link OutputConfiguration#setSurfacesForMultiResolutionOutput}
+     * for details. For {@link CaptureRequest}, use {@link #getSurface()} instead.</p>
+     *
+     * <p>Please note that holding on to the Surface objects returned by this method is not enough
+     * to keep their parent MultiResolutionImageReaders from being reclaimed. In that sense, a
+     * Surface acts like a {@link java.lang.ref.WeakReference weak reference} to the
+     * MultiResolutionImageReader that provides it.</p>
+     *
+     * @param configuredSize The configured size corresponding to one of the internal ImageReader.
+     * @param physicalCameraId The physical camera Id the internal ImageReader targets for. If
+     *                         the ImageReader is not targeting a physical camera of a logical
+     *                         multi-camera, this parameter is set to "".
+     *
+     * @return The {@link Surface} of the internal ImageReader corresponding to the provided
+     *         configured size and physical camera Id.
+     *
+     * @throws IllegalArgumentException If {@code configuredSize} is {@code null}, or the ({@code
+     *                                  configuredSize} and {@code physicalCameraId}) combo is not
+     *                                  part of this {@code MultiResolutionImageReader}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public @NonNull Surface getSurface(@NonNull Size configuredSize,
+            @NonNull String physicalCameraId) {
+        checkNotNull(configuredSize, "configuredSize must not be null");
+        checkNotNull(physicalCameraId, "physicalCameraId must not be null");
+
+        for (int i = 0; i < mStreamInfo.length; i++) {
+            if (mStreamInfo[i].getWidth() == configuredSize.getWidth()
+                    && mStreamInfo[i].getHeight() == configuredSize.getHeight()
+                    && physicalCameraId.equals(mStreamInfo[i].getPhysicalCameraId())) {
+                return mReaders[i].getSurface();
+            }
+        }
+        throw new IllegalArgumentException("configuredSize and physicalCameraId don't match with "
+                + "this MultiResolutionImageReader");
+    }
+
+    /**
      * Get the surface that is used as a target for {@link CaptureRequest}
      *
      * <p>The application must use the surface returned by this function as a target for
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
index 0f199b1..9e01438 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
@@ -90,7 +90,6 @@
     @Override
     public boolean isSessionConfigurationSupported(@NonNull SessionConfiguration config)
             throws CameraAccessException {
-        // TODO(b/298033056): restructure the OutputConfiguration API for better usability
         synchronized (mInterfaceLock) {
             if (mCameraManager.isCameraServiceDisabled()) {
                 throw new IllegalArgumentException("No cameras available on device");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 8aacd5e..dda52dd 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,6 +19,7 @@
 
 import static com.android.internal.util.Preconditions.*;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +28,9 @@
 import android.annotation.TestApi;
 import android.graphics.ColorSpace;
 import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -43,6 +47,8 @@
 import android.util.Size;
 import android.view.Surface;
 
+import com.android.internal.camera.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -50,6 +56,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * A class for describing camera output, which contains a {@link Surface} and its specific
@@ -361,6 +368,21 @@
     private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
 
     /**
+     * The surface is obtained from {@link android.media.MediaRecorder}.
+     */
+    private static final int SURFACE_TYPE_MEDIA_RECORDER = 2;
+
+    /**
+     * The surface is obtained from {@link android.media.MediaCodec}.
+     */
+    private static final int SURFACE_TYPE_MEDIA_CODEC = 3;
+
+    /**
+     * The surface is obtained from {@link android.media.ImageReader}.
+     */
+    private static final int SURFACE_TYPE_IMAGE_READER = 4;
+
+    /**
      * Maximum number of surfaces supported by one {@link OutputConfiguration}.
      *
      * <p>The combined number of surfaces added by the constructor and
@@ -576,6 +598,7 @@
         mMirrorMode = MIRROR_MODE_AUTO;
         mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
+        mUsage = 0;
     }
 
     /**
@@ -592,13 +615,7 @@
             @NonNull MultiResolutionImageReader multiResolutionImageReader)  {
         checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null");
 
-        int groupId = MULTI_RESOLUTION_GROUP_ID_COUNTER;
-        MULTI_RESOLUTION_GROUP_ID_COUNTER++;
-        // Skip in case the group id counter overflows to -1, the invalid value.
-        if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) {
-            MULTI_RESOLUTION_GROUP_ID_COUNTER++;
-        }
-
+        int groupId = getAndIncreaseMultiResolutionGroupId();
         ImageReader[] imageReaders = multiResolutionImageReader.getReaders();
         ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
         for (int i = 0; i < imageReaders.length; i++) {
@@ -620,6 +637,115 @@
     }
 
     /**
+     * Create a list of {@link OutputConfiguration} instances for a
+     * {@link android.hardware.camera2.params.MultiResolutionImageReader}.
+     *
+     * <p>This method can be used to create query OutputConfigurations for a
+     * MultiResolutionImageReader that can be included in a SessionConfiguration passed into
+     * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up
+     * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput}
+     * can be used to link to the actual MultiResolutionImageReader.</p>
+     *
+     * <p>This constructor takes same arguments used to create a {@link
+     * MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo}
+     * objects and the format.</p>
+     *
+     * @param streams The group of multi-resolution stream info objects, which are used to create a
+     *                multi-resolution image reader containing a number of ImageReaders.
+     * @param format The format of the MultiResolutionImageReader. This must be one of the {@link
+     *               android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants
+     *               supported by the camera device. Note that not all formats are supported, like
+     *               {@link ImageFormat.NV21}. The supported multi-resolution reader format can be
+     *               queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}.
+     *
+     * @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader.
+     *
+     * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain
+     *                                 at least 2 items, or if {@code format} isn't a valid camera
+     *                                 format.
+     *
+     * @see MultiResolutionImageReader
+     * @see MultiResolutionStreamInfo
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public static @NonNull List<OutputConfiguration> createInstancesForMultiResolutionOutput(
+            @NonNull Collection<MultiResolutionStreamInfo> streams,
+            @Format int format)  {
+        if (streams == null || streams.size() <= 1) {
+            throw new IllegalArgumentException(
+                "The streams list must contain at least 2 entries");
+        }
+        if (format == ImageFormat.NV21) {
+            throw new IllegalArgumentException(
+                    "NV21 format is not supported");
+        }
+
+        int groupId = getAndIncreaseMultiResolutionGroupId();
+        ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
+        for (MultiResolutionStreamInfo stream : streams) {
+            Size surfaceSize = new Size(stream.getWidth(), stream.getHeight());
+            OutputConfiguration config = new OutputConfiguration(
+                    groupId, format, surfaceSize);
+            config.setPhysicalCameraId(stream.getPhysicalCameraId());
+            config.setMultiResolutionOutput();
+            configs.add(config);
+
+            // No need to call addSensorPixelModeUsed for ultra high resolution sensor camera,
+            // because regular and max resolution output configurations are used for DEFAULT mode
+            // and MAX_RESOLUTION mode respectively by default.
+        }
+
+        return configs;
+    }
+
+    /**
+     * Set the OutputConfiguration surfaces corresponding to the {@link MultiResolutionImageReader}.
+     *
+     * <p>This function should be used together with {@link
+     * #createInstancesForMultiResolutionOutput}. The application calls {@link
+     * #createInstancesForMultiResolutionOutput} first to create a list of
+     * OutputConfiguration objects without the actual MultiResolutionImageReader.
+     * Once the MultiResolutionImageReader is created later during full camera setup, the
+     * application then calls this function to assign the surfaces to the OutputConfiguration
+     * instances.</p>
+     *
+     * @param outputConfigurations The OutputConfiguration objects created by {@link
+     *                             #createInstancesFromMultiResolutionOutput}
+     * @param multiResolutionImageReader The MultiResolutionImageReader object created from the same
+     *                                   MultiResolutionStreamInfo parameters as
+     *                                   {@code outputConfigurations}.
+     * @throws IllegalArgumentException If {@code outputConfigurations} or {@code
+     *                                  multiResolutionImageReader} is {@code null}, the {@code
+     *                                  outputConfigurations} and {@code multiResolutionImageReader}
+     *                                  sizes don't match, or if the
+     *                                  {@code multiResolutionImageReader}'s surfaces don't match
+     *                                  with the {@code outputConfigurations}.
+     * @throws IllegalStateException If {@code outputConfigurations} already contains valid output
+     *                               surfaces.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public static void setSurfacesForMultiResolutionOutput(
+            @NonNull Collection<OutputConfiguration> outputConfigurations,
+            @NonNull MultiResolutionImageReader multiResolutionImageReader) {
+        checkNotNull(outputConfigurations, "outputConfigurations must not be null");
+        checkNotNull(multiResolutionImageReader, "multiResolutionImageReader must not be null");
+        if (outputConfigurations.size() != multiResolutionImageReader.getReaders().length) {
+            throw new IllegalArgumentException(
+                    "outputConfigurations and multiResolutionImageReader sizes must match");
+        }
+
+        for (OutputConfiguration config : outputConfigurations) {
+            String physicalCameraId = config.getPhysicalCameraId();
+            if (physicalCameraId == null) {
+                physicalCameraId = "";
+            }
+            Surface surface = multiResolutionImageReader.getSurface(config.getConfiguredSize(),
+                    physicalCameraId);
+            config.addSurface(surface);
+        }
+    }
+
+    /**
      * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
      * source class.
      * <p>
@@ -628,30 +754,64 @@
      * with a deferred Surface. The application can use this output configuration to create a
      * session.
      * </p>
-     * <p>
-     * However, the actual output Surface must be set via {@link #addSurface} and the deferred
-     * Surface configuration must be finalized via {@link
-     * CameraCaptureSession#finalizeOutputConfigurations} before submitting a request with this
-     * Surface target. The deferred Surface can only be obtained either from {@link
-     * android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface}, or from
+     *
+     * <p>Starting from {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V},
+     * the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView}
+     * by calling {@link android.view.SurfaceHolder#getSurface}, (2) from
      * {@link android.graphics.SurfaceTexture} via
-     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
-     * </p>
+     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link
+     * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link
+     * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link
+     * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link
+     * android.media.MediaCodec#createPersistentInputSource}.</p>
+     *
+     * <ul>
+     * <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}
+     * can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the
+     * output Surface must be set via {@link #addSurface}, and the Surface configuration must be
+     * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting
+     * a request with the Surface target.</li>
+     * <li>For all other target types, the output Surface must be set by {@link #addSurface},
+     * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the
+     * OutputConfiguration used to create the session will contain the actual Surface.</li>
+     * </ul>
+     *
+     * <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link
+     * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both
+     * kind of outputs can be deferred until after {@link
+     * CameraDevice#createCaptureSessionByOutputConfiguration}.</p>
+     *
+     * <p>An OutputConfiguration object created by this constructor can be used for {@link
+     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
      *
      * @param surfaceSize Size for the deferred surface.
      * @param klass a non-{@code null} {@link Class} object reference that indicates the source of
-     *            this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and
-     *            {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
+     *            this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class},
+     *            {@link android.graphics.SurfaceTexture SurfaceTexture.class}, {@link
+     *            android.media.MediaRecorder MediaRecorder.class}, and
+     *            {@link android.media.MediaCodec MediaCodec.class} are supported.
+     *            Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only
+     *            {@link android.view.SurfaceHolder SurfaceHolder.class} and {@link
+     *            android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
      * @throws IllegalArgumentException if the Surface source class is not supported, or Surface
      *         size is zero.
      */
     public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
-        checkNotNull(klass, "surfaceSize must not be null");
+        checkNotNull(surfaceSize, "surfaceSize must not be null");
         checkNotNull(klass, "klass must not be null");
         if (klass == android.view.SurfaceHolder.class) {
             mSurfaceType = SURFACE_TYPE_SURFACE_VIEW;
+            mIsDeferredConfig = true;
         } else if (klass == android.graphics.SurfaceTexture.class) {
             mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
+            mIsDeferredConfig = true;
+        } else if (klass == android.media.MediaRecorder.class) {
+            mSurfaceType = SURFACE_TYPE_MEDIA_RECORDER;
+            mIsDeferredConfig = false;
+        } else if (klass == android.media.MediaCodec.class) {
+            mSurfaceType = SURFACE_TYPE_MEDIA_CODEC;
+            mIsDeferredConfig = false;
         } else {
             mSurfaceType = SURFACE_TYPE_UNKNOWN;
             throw new IllegalArgumentException("Unknown surface source class type");
@@ -668,7 +828,6 @@
         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
         mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
         mConfiguredGenerationId = 0;
-        mIsDeferredConfig = true;
         mIsShared = false;
         mPhysicalCameraId = null;
         mIsMultiResolution = false;
@@ -678,6 +837,131 @@
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
         mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
+        mUsage = 0;
+    }
+
+    /**
+     * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+     * format and size.
+     *
+     * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+     * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+     * before creating the capture session.</p>
+     *
+     * <p>An OutputConfiguration object created by this constructor can be used for {@link
+     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     *
+     * @param format The format of the ImageReader output. This must be one of the
+     *               {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+     *               constants. Note that not all formats are supported by the camera device.
+     * @param surfaceSize Size for the ImageReader surface.
+     * @throws IllegalArgumentException if the Surface size is null or zero.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public OutputConfiguration(@Format int format, @NonNull Size surfaceSize) {
+        this(format, surfaceSize,
+                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+    }
+
+    /**
+     * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+     * surfaceGroupId, format, and size.
+     *
+     * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+     * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+     * before creating the capture session.</p>
+     *
+     * <p>An OutputConfiguration object created by this constructor can be used for {@link
+     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     *
+     * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
+     *                       outputs.
+     * @param format The format of the ImageReader output. This must be one of the
+     *               {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+     *               constants. Note that not all formats are supported by the camera device.
+     * @param surfaceSize Size for the ImageReader surface.
+     * @throws IllegalArgumentException if the Surface size is null or zero.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public OutputConfiguration(int surfaceGroupId, @Format int format, @NonNull Size surfaceSize) {
+        this(surfaceGroupId, format, surfaceSize,
+                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+    }
+
+    /**
+     * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+     * format, size, and usage flags.
+     *
+     * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+     * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+     * before creating the capture session.</p>
+     *
+     * <p>An OutputConfiguration object created by this constructor can be used for {@link
+     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     *
+     * @param format The format of the ImageReader output. This must be one of the
+     *               {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+     *               constants. Note that not all formats are supported by the camera device.
+     * @param surfaceSize Size for the ImageReader surface.
+     * @param usage The usage flags of the ImageReader output surface.
+     * @throws IllegalArgumentException if the Surface size is null or zero.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public OutputConfiguration(@Format int format, @NonNull Size surfaceSize, @Usage long usage) {
+        this(SURFACE_GROUP_ID_NONE, format, surfaceSize, usage);
+    }
+
+    /**
+     * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+     * surface group id, format, size, and usage flags.
+     *
+     * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+     * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+     * before creating the capture session.</p>
+     *
+     * <p>An OutputConfiguration object created by this constructor can be used for {@link
+     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     *
+     * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
+     *                       outputs.
+     * @param format The format of the ImageReader output. This must be one of the
+     *               {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+     *               constants. Note that not all formats are supported by the camera device.
+     * @param surfaceSize Size for the ImageReader surface.
+     * @param usage The usage flags of the ImageReader output surface.
+     * @throws IllegalArgumentException if the Surface size is null or zero.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public OutputConfiguration(int surfaceGroupId, @Format int format,
+            @NonNull Size surfaceSize, @Usage long usage) {
+        checkNotNull(surfaceSize, "surfaceSize must not be null");
+        if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
+            throw new IllegalArgumentException("Surface size needs to be non-zero");
+        }
+
+        mSurfaceType = SURFACE_TYPE_IMAGE_READER;
+        mSurfaceGroupId = surfaceGroupId;
+        mSurfaces = new ArrayList<Surface>();
+        mRotation = ROTATION_0;
+        mConfiguredSize = surfaceSize;
+        mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(format);
+        mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(format);
+        mConfiguredGenerationId = 0;
+        mIsDeferredConfig = false;
+        mIsShared = false;
+        mPhysicalCameraId = null;
+        mIsMultiResolution = false;
+        mSensorPixelModesUsed = new ArrayList<Integer>();
+        mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+        mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
+        mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+        mReadoutTimestampEnabled = false;
+        mIsReadoutSensorTimestampBase = false;
+        mUsage = usage;
     }
 
     /**
@@ -852,7 +1136,8 @@
     /**
      * Check if this configuration has deferred configuration.
      *
-     * <p>This will return true if the output configuration was constructed with surface deferred by
+     * <p>This will return true if the output configuration was constructed with {@link
+     * android.view.SurfaceView} or {@link android.graphics.SurfaceTexture} deferred by
      * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. It will return true even after
      * the deferred surface is added later by {@link OutputConfiguration#addSurface}.</p>
      *
@@ -872,13 +1157,28 @@
      * {@link CameraCaptureSession#finalizeOutputConfigurations}. It is possible to call this method
      * after the output configurations have been finalized only in cases of enabled surface sharing
      * see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
-     * {@link CameraCaptureSession#updateOutputConfiguration}.</p>
+     * {@link CameraCaptureSession#updateOutputConfiguration}. If this function is called before
+     * session creation, {@link CameraCaptureSession#finalizeOutputConfigurations} doesn't need to
+     * be called.</p>
      *
-     * <p> If the OutputConfiguration was constructed with a deferred surface by {@link
-     * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
-     * from {@link android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface},
-     * or from {@link android.graphics.SurfaceTexture} via
-     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).</p>
+     * <p> If the OutputConfiguration was constructed by {@link
+     * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained:
+     * <ul>
+     * <li>from {@link android.view.SurfaceView} by calling
+     * {@link android.view.SurfaceHolder#getSurface}</li>
+     * <li>from {@link android.graphics.SurfaceTexture} by calling
+     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}</li>
+     * <li>from {@link android.media.MediaRecorder} by calling
+     * {@link android.media.MediaRecorder#getSurface} or {@link
+     * android.media.MediaCodec#createPersistentInputSurface}</li>
+     * <li>from {@link android.media.MediaCodce} by calling
+     * {@link android.media.MediaCodec#createInputSurface} or {@link
+     * android.media.MediaCodec#createPersistentInputSource}</li>
+     * </ul>
+     *
+     * <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)}
+     * or its variants, the added surface must be obtained from {@link android.media.ImageReader}
+     * by calling {@link android.media.ImageReader#getSurface}.</p>
      *
      * <p> If the OutputConfiguration was constructed by other constructors, the added
      * surface must be compatible with the existing surface. See {@link #enableSurfaceSharing} for
@@ -934,9 +1234,13 @@
      *
      * <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
      *  OutputConfiguration. The only notable exception is the surface associated with
-     *  the OutputConfiguration see {@link #getSurface} which was passed as part of the constructor
-     *  or was added first in the deferred case
-     *  {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
+     *  the OutputConfiguration (see {@link #getSurface}) which was passed as part of the
+     *  constructor or was added first in the case of
+     *  {@link OutputConfiguration#OutputConfiguration(Size, Class)}, {@link
+     *  OutputConfiguration#OutputConfiguration(int, Size)}, {@link
+     *  OutputConfiguration#OutputConfiguration(int, Size, long)}, {@link
+     *  OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
+     *  OutputConfiguration#OutputConfiguration(int, int, Size, long)}.</p>
      *
      * @param surface The surface to be removed.
      *
@@ -945,6 +1249,7 @@
      *                                  with {@link #addSurface}.
      */
     public void removeSurface(@NonNull Surface surface) {
+        checkNotNull(surface, "Surface must not be null");
         if (getSurface() == surface) {
             throw new IllegalArgumentException(
                     "Cannot remove surface associated with this output configuration");
@@ -1175,6 +1480,7 @@
         this.mTimestampBase = other.mTimestampBase;
         this.mMirrorMode = other.mMirrorMode;
         this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
+        this.mUsage = other.mUsage;
     }
 
     /**
@@ -1203,6 +1509,9 @@
         int timestampBase = source.readInt();
         int mirrorMode = source.readInt();
         boolean readoutTimestampEnabled = source.readInt() == 1;
+        int format = source.readInt();
+        int dataSpace = source.readInt();
+        long usage = source.readLong();
 
         mSurfaceGroupId = surfaceSetId;
         mRotation = rotation;
@@ -1211,6 +1520,7 @@
         mIsDeferredConfig = isDeferred;
         mIsShared = isShared;
         mSurfaces = surfaces;
+        mUsage = 0;
         if (mSurfaces.size() > 0) {
             mSurfaceType = SURFACE_TYPE_UNKNOWN;
             mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces.get(0));
@@ -1218,9 +1528,16 @@
             mConfiguredGenerationId = mSurfaces.get(0).getGenerationId();
         } else {
             mSurfaceType = surfaceType;
-            mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
-            mConfiguredDataspace =
-                    StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
+            if (mSurfaceType != SURFACE_TYPE_IMAGE_READER) {
+                mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(
+                        ImageFormat.PRIVATE);
+                mConfiguredDataspace =
+                        StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
+            } else {
+                mConfiguredFormat = format;
+                mConfiguredDataspace = dataSpace;
+                mUsage = usage;
+            }
             mConfiguredGenerationId = 0;
         }
         mPhysicalCameraId = physicalCameraId;
@@ -1293,6 +1610,31 @@
         return mSurfaceGroupId;
     }
 
+    /**
+     * Get the configured size associated with this {@link OutputConfiguration}.
+     *
+     * @return The configured size associated with this {@link OutputConfiguration}.
+     *
+     * @hide
+     */
+    public Size getConfiguredSize() {
+        return mConfiguredSize;
+    }
+
+    /**
+     * Get the physical camera ID associated with this {@link OutputConfiguration}.
+     *
+     * <p>If this OutputConfiguration isn't targeting a physical camera of a logical
+     * multi-camera, this function returns {@code null}.</p>
+     *
+     * @return The physical camera Id associated with this {@link OutputConfiguration}.
+     *
+     * @hide
+     */
+    public @Nullable String getPhysicalCameraId() {
+        return mPhysicalCameraId;
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<OutputConfiguration> CREATOR =
             new Parcelable.Creator<OutputConfiguration>() {
         @Override
@@ -1353,6 +1695,9 @@
         dest.writeInt(mTimestampBase);
         dest.writeInt(mMirrorMode);
         dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
+        dest.writeInt(mConfiguredFormat);
+        dest.writeInt(mConfiguredDataspace);
+        dest.writeLong(mUsage);
     }
 
     /**
@@ -1372,22 +1717,24 @@
             return true;
         } else if (obj instanceof OutputConfiguration) {
             final OutputConfiguration other = (OutputConfiguration) obj;
-            if (mRotation != other.mRotation ||
-                    !mConfiguredSize.equals(other.mConfiguredSize) ||
-                    mConfiguredFormat != other.mConfiguredFormat ||
-                    mSurfaceGroupId != other.mSurfaceGroupId ||
-                    mSurfaceType != other.mSurfaceType ||
-                    mIsDeferredConfig != other.mIsDeferredConfig ||
-                    mIsShared != other.mIsShared ||
-                    mConfiguredDataspace != other.mConfiguredDataspace ||
-                    mConfiguredGenerationId != other.mConfiguredGenerationId ||
-                    !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
-                    mIsMultiResolution != other.mIsMultiResolution ||
-                    mStreamUseCase != other.mStreamUseCase ||
-                    mTimestampBase != other.mTimestampBase ||
-                    mMirrorMode != other.mMirrorMode ||
-                    mReadoutTimestampEnabled != other.mReadoutTimestampEnabled)
+            if (mRotation != other.mRotation
+                    || !mConfiguredSize.equals(other.mConfiguredSize)
+                    || mConfiguredFormat != other.mConfiguredFormat
+                    || mSurfaceGroupId != other.mSurfaceGroupId
+                    || mSurfaceType != other.mSurfaceType
+                    || mIsDeferredConfig != other.mIsDeferredConfig
+                    || mIsShared != other.mIsShared
+                    || mConfiguredDataspace != other.mConfiguredDataspace
+                    || mConfiguredGenerationId != other.mConfiguredGenerationId
+                    || !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId)
+                    || mIsMultiResolution != other.mIsMultiResolution
+                    || mStreamUseCase != other.mStreamUseCase
+                    || mTimestampBase != other.mTimestampBase
+                    || mMirrorMode != other.mMirrorMode
+                    || mReadoutTimestampEnabled != other.mReadoutTimestampEnabled
+                    || mUsage != other.mUsage) {
                 return false;
+            }
             if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
                 return false;
             }
@@ -1416,6 +1763,16 @@
     }
 
     /**
+     * Get and increase the next MultiResolution group id.
+     *
+     * If the ID reaches -1, skip it.
+     */
+    private static int getAndIncreaseMultiResolutionGroupId() {
+        return sNextMultiResolutionGroupId.getAndUpdate(i ->
+                i + 1 == SURFACE_GROUP_ID_NONE ? i + 2 : i + 1);
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -1430,7 +1787,8 @@
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                     mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                     mDynamicRangeProfile, mColorSpace, mStreamUseCase,
-                    mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
+                    mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
+                    Long.hashCode(mUsage));
         }
 
         return HashCodeHelpers.hashCode(
@@ -1440,14 +1798,14 @@
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                 mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                 mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
-                mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
+                mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
     }
 
     private static final String TAG = "OutputConfiguration";
 
     // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
     // incremented every time {@link createInstancesForMultiResolutionOutput} is called.
-    private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
+    private static AtomicInteger sNextMultiResolutionGroupId = new AtomicInteger(0);
 
     private ArrayList<Surface> mSurfaces;
     private final int mRotation;
@@ -1486,4 +1844,6 @@
     private boolean mReadoutTimestampEnabled;
     // Whether the timestamp base is set to READOUT_SENSOR
     private boolean mIsReadoutSensorTimestampBase;
+    // The usage flags. Only set for instances created for ImageReader without specifying surface.
+    private long mUsage;
 }
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 991f545..b0f354f 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -20,6 +20,7 @@
 import static com.android.internal.util.Preconditions.*;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +29,7 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CameraDeviceSetup;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.params.InputConfiguration;
@@ -125,6 +127,34 @@
     }
 
     /**
+     * Create a new {@link SessionConfiguration} with sessionType and output configurations.
+     *
+     * <p>The SessionConfiguration objects created by this constructor can be used by
+     * {@link CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+     * CameraDeviceSetup.getSessionCharacteristics} to query a camera device's feature
+     * combination support and session specific characteristics. For the SessionConfiguration
+     * object to be used to create a capture session, {@link #setCallback} must be called to
+     * specify the state callback function, and any incomplete OutputConfigurations must be
+     * completed via {@link OutputConfiguration#addSurface} or
+     * {@link OutputConfiguration#setSurfacesForMultiResolutionOutput} as appropriate.</p>
+     *
+     * @param sessionType The session type.
+     * @param outputs A list of output configurations for the capture session.
+     *
+     * @see #SESSION_REGULAR
+     * @see #SESSION_HIGH_SPEED
+     * @see CameraDevice#createCaptureSession(SessionConfiguration)
+     * @see CameraDeviceSetup#isSessionConfigurationSupported
+     * @see CameraDeviceSetup#getSessionCharacteristics
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public SessionConfiguration(@SessionMode int sessionType,
+            @NonNull List<OutputConfiguration> outputs) {
+        mSessionType = sessionType;
+        mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
+    }
+
+    /**
      * Create a SessionConfiguration from Parcel.
      * No support for parcelable 'mStateCallback' and 'mExecutor' yet.
      */
@@ -376,4 +406,23 @@
             return null;
         }
     }
+
+    /**
+     * Set the state callback and executor.
+     *
+     * <p>This function must be called for the SessionConfiguration object created via {@link
+     * #SessionConfiguration(int, List) SessionConfiguration(int, List&lt;OutputConfiguration&gt;)}
+     * before it's used to create a capture session.</p>
+     *
+     * @param executor The executor which should be used to invoke the callback. In general it is
+     *                 recommended that camera operations are not done on the main (UI) thread.
+     * @param cb A state callback interface implementation.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+    public void setCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CameraCaptureSession.StateCallback cb) {
+        mStateCallback = cb;
+        mExecutor = executor;
+    }
 }
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index b85d686..b067095 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1509,6 +1509,8 @@
                 return HAL_DATASPACE_HEIF;
             case ImageFormat.JPEG_R:
                 return HAL_DATASPACE_JPEG_R;
+            case ImageFormat.YUV_420_888:
+                return HAL_DATASPACE_JFIF;
             default:
                 return HAL_DATASPACE_UNKNOWN;
         }
@@ -2025,6 +2027,10 @@
      * @hide
      */
     public static final int HAL_DATASPACE_JPEG_R = 0x1005;
+    /**
+     * @hide
+     */
+    public static final int HAL_DATASPACE_JFIF = 0x8C20000;
     private static final long DURATION_20FPS_NS = 50000000L;
     /**
      * @see #getDurations(int, int)
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index 76888f3..b214da2 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -52,78 +52,6 @@
 @FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
 public final class DeviceState {
     /**
-     * Flag that indicates override requests should be cancelled when this device state becomes the
-     * base device state.
-     * @hide
-     * @deprecated use {@link #PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS}
-     */
-    @Deprecated
-    public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
-
-    /**
-     * Flag that indicates this device state is inaccessible for applications to be placed in. This
-     * could be a device-state where the {@link Display#DEFAULT_DISPLAY} is not enabled.
-     * @hide
-     * @deprecated use {@link #PROPERTY_APP_INACCESSIBLE}
-     */
-    @Deprecated
-    public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
-
-    /**
-     * Some device states can be both entered through a physical configuration as well as emulation
-     * through {@link DeviceStateManager#requestState}, while some states can only be entered
-     * through emulation and have no physical configuration to match.
-     *
-     * This flag indicates that the corresponding state can only be entered through emulation.
-     * @hide
-     * @deprecated use {@link #PROPERTY_EMULATED_ONLY}
-     */
-    @Deprecated
-    public static final int FLAG_EMULATED_ONLY = 1 << 2;
-
-    /**
-     * This flag indicates that the corresponding state should be automatically canceled when the
-     * requesting app is no longer on top. The app is considered not on top when (1) the top
-     * activity in the system is from a different app, (2) the device is in sleep mode, or
-     * (3) the keyguard shows up.
-     * @hide
-     * @deprecated use {@link #PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP}
-     */
-    @Deprecated
-    public static final int FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 1 << 3;
-
-    /**
-     * This flag indicates that the corresponding state should be disabled when the device is
-     * overheating and reaching the critical status.
-     * @hide
-     * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL}
-     */
-    @Deprecated
-    public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
-
-    /**
-     * This flag indicates that the corresponding state should be disabled when power save mode
-     * is enabled.
-     * @hide
-     * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE}
-     */
-    @Deprecated
-    public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
-
-    /** @hide */
-    @IntDef(prefix = {"FLAG_"}, flag = true, value = {
-            FLAG_CANCEL_OVERRIDE_REQUESTS,
-            FLAG_APP_INACCESSIBLE,
-            FLAG_EMULATED_ONLY,
-            FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
-            FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
-            FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
-    })
-    @Deprecated
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DeviceStateFlags {}
-
-    /**
      * Property that indicates that a fold-in style foldable device is currently in a fully closed
      * configuration.
      */
@@ -302,42 +230,11 @@
     @NonNull
     private final DeviceState.Configuration mDeviceStateConfiguration;
 
-    @DeviceStateFlags
-    private final int mFlags;
-
     /** @hide */
+    @TestApi
     public DeviceState(@NonNull DeviceState.Configuration deviceStateConfiguration) {
         Objects.requireNonNull(deviceStateConfiguration, "Device StateConfiguration is null");
         mDeviceStateConfiguration = deviceStateConfiguration;
-        mFlags = 0;
-    }
-
-    /** @hide */
-    public DeviceState(
-            @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
-                    MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
-            @NonNull String name,
-            @NonNull Set<@DeviceStateProperties Integer> properties) {
-        mDeviceStateConfiguration = new DeviceState.Configuration(identifier, name, properties,
-                Collections.emptySet());
-        mFlags = 0;
-    }
-
-    /**
-     * @deprecated Deprecated in favor of {@link #DeviceState(int, String, Set)}
-     * @hide
-     */
-    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-    @Deprecated
-    public DeviceState(
-            @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
-                    MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
-            @NonNull String name,
-            @DeviceStateFlags int flags) {
-
-        mDeviceStateConfiguration = new DeviceState.Configuration(identifier, name,
-                Collections.emptySet(), Collections.emptySet());
-        mFlags = flags;
     }
 
     /** Returns the unique identifier for the device state. */
@@ -352,17 +249,6 @@
         return mDeviceStateConfiguration.getName();
     }
 
-    /**
-     * @hide
-     * @deprecated in favor of {@link #hasProperty(int)} method
-     */
-    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-    @Deprecated
-    @DeviceStateFlags
-    public int getFlags() {
-        return mFlags;
-    }
-
     @Override
     public String toString() {
         return "DeviceState{" + "identifier=" + mDeviceStateConfiguration.getIdentifier()
@@ -388,16 +274,6 @@
         return Objects.hash(mDeviceStateConfiguration);
     }
 
-    /** Checks if a specific flag is set
-     * @hide
-     * @deprecated in favor of {@link #hasProperty(int)}
-     */
-    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-    @Deprecated
-    public boolean hasFlag(int flagToCheckFor) {
-        return (mFlags & flagToCheckFor) == flagToCheckFor;
-    }
-
     /**
      * Checks if a specific property is set on this state
      */
@@ -438,6 +314,7 @@
      * @see DeviceStateManager
      * @hide
      */
+    @TestApi
     public static final class Configuration implements Parcelable {
         /** Unique identifier for the device state. */
         @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
@@ -563,29 +440,35 @@
         };
 
         /** @hide */
-        public static class Builder {
+        @TestApi
+        public static final class Builder {
             private final int mIdentifier;
+            @NonNull
             private final String mName;
+            @NonNull
             private Set<@SystemDeviceStateProperties Integer> mSystemProperties =
                     Collections.emptySet();
+            @NonNull
             private Set<@PhysicalDeviceStateProperties Integer> mPhysicalProperties =
                     Collections.emptySet();
 
-            public Builder(int identifier, String name) {
+            public Builder(int identifier, @NonNull String name) {
                 mIdentifier = identifier;
                 mName = name;
             }
 
             /** Sets the system properties for this {@link DeviceState.Configuration.Builder} */
+            @NonNull
             public Builder setSystemProperties(
-                    Set<@SystemDeviceStateProperties Integer> systemProperties) {
+                    @NonNull Set<@SystemDeviceStateProperties Integer> systemProperties) {
                 mSystemProperties = systemProperties;
                 return this;
             }
 
             /** Sets the system properties for this {@link DeviceState.Configuration.Builder} */
+            @NonNull
             public Builder setPhysicalProperties(
-                    Set<@PhysicalDeviceStateProperties Integer> physicalProperties) {
+                    @NonNull Set<@PhysicalDeviceStateProperties Integer> physicalProperties) {
                 mPhysicalProperties = physicalProperties;
                 return this;
             }
@@ -594,6 +477,7 @@
              * Returns a new {@link DeviceState.Configuration} whose values match the values set on
              * the builder.
              */
+            @NonNull
             public DeviceState.Configuration build() {
                 return new DeviceState.Configuration(mIdentifier, mName, mSystemProperties,
                         mPhysicalProperties);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index a4c3833..febc24c1 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -49,6 +49,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int INVALID_DEVICE_STATE_IDENTIFIER = -1;
 
     /**
@@ -96,20 +97,6 @@
     /**
      * Returns the list of device states that are supported and can be requested with
      * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
-     * @deprecated use {@link #getSupportedDeviceStates()}
-     * @hide
-     */
-    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-    @TestApi
-    @Deprecated
-    @NonNull
-    public int[] getSupportedStates() {
-        return mGlobal.getSupportedStates();
-    }
-
-    /**
-     * Returns the list of device states that are supported and can be requested with
-     * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
      */
     @NonNull
     public List<DeviceState> getSupportedDeviceStates() {
@@ -239,23 +226,6 @@
          * Guaranteed to be called once on registration of the callback with the initial value and
          * then on every subsequent change in the supported states.
          *
-         * @param supportedStates the new supported states.
-         *
-         * @see DeviceStateManager#getSupportedStates()
-         * @deprecated use {@link #onSupportedStatesChanged(List)}
-         * @hide
-         */
-        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-        @TestApi
-        @Deprecated
-        default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
-
-        /**
-         * Called in response to a change in the states supported by the device.
-         * <p>
-         * Guaranteed to be called once on registration of the callback with the initial value and
-         * then on every subsequent change in the supported states.
-         *
          * The supported device states may change due to certain states becoming unavailable
          * due to device configuration or device conditions such as if the device is too hot or
          * external monitors have been connected.
@@ -267,42 +237,6 @@
         default void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {}
 
         /**
-         * Called in response to a change in the base device state.
-         * <p>
-         * The base state is the state of the device without considering any requests made through
-         * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}
-         * from any client process. The base state is guaranteed to match the state provided with a
-         * call to {@link #onStateChanged(int)} when there are no active requests from any process.
-         * <p>
-         * Guaranteed to be called once on registration of the callback with the initial value and
-         * then on every subsequent change in the non-override state.
-         *
-         * @param state the new base device state.
-         * @deprecated use {@link #onDeviceStateChanged(DeviceState)} and query for physical
-         * properties that are relevant to your needs.
-         * @hide
-         */
-        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-        @TestApi
-        @Deprecated
-        default void onBaseStateChanged(int state) {}
-
-        /**
-         * Called in response to device state changes.
-         * <p>
-         * Guaranteed to be called once on registration of the callback with the initial value and
-         * then on every subsequent change in device state.
-         *
-         * @param state the new device state.
-         * @deprecated use {@link #onDeviceStateChanged(DeviceState)}
-         * @hide
-         */
-        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-        @TestApi
-        @Deprecated
-        void onStateChanged(int state);
-
-        /**
          * Called in response to device state changes.
          * <p>
          * Guaranteed to be called once on registration of the callback with the initial value and
@@ -310,8 +244,7 @@
          *
          * @param state the new device state.
          */
-        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
-        default void onDeviceStateChanged(@NonNull DeviceState state) {}
+        void onDeviceStateChanged(@NonNull DeviceState state);
     }
 
     /**
@@ -340,9 +273,6 @@
         }
 
         @Override
-        public final void onStateChanged(int state) {}
-
-        @Override
         public final void onDeviceStateChanged(@NonNull DeviceState deviceState) {
             final boolean folded;
             if (mFeatureFlags.deviceStatePropertyApi()) {
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index d6cc00d..0c84019 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -90,33 +90,6 @@
     }
 
     /**
-     * Returns the set of supported device states.
-     *
-     * @see DeviceStateManager#getSupportedStates()
-     */
-    // TODO(b/325124054): Remove unused methods when clients are migrated.
-    public int[] getSupportedStates() {
-        synchronized (mLock) {
-            final DeviceStateInfo currentInfo;
-            if (mLastReceivedInfo != null) {
-                // If we have mLastReceivedInfo a callback is registered for this instance and it
-                // is receiving the most recent info from the server. Use that info here.
-                currentInfo = mLastReceivedInfo;
-            } else {
-                // If mLastReceivedInfo is null there is no registered callback so we manually
-                // fetch the current info.
-                try {
-                    currentInfo = mDeviceStateManager.getDeviceStateInfo();
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-            }
-
-            return getSupportedStateIdentifiersLocked(currentInfo.supportedStates);
-        }
-    }
-
-    /**
      * Returns {@link List} of supported {@link DeviceState}s.
      *
      * @see DeviceStateManager#getSupportedDeviceStates()
@@ -264,14 +237,8 @@
             mCallbacks.add(wrapper);
 
             if (mLastReceivedInfo != null) {
-                // Copy the array to prevent the callback from modifying the internal state.
-                final int[] supportedStates = getSupportedStateIdentifiersLocked(
-                        mLastReceivedInfo.supportedStates);
-                wrapper.notifySupportedStatesChanged(supportedStates);
                 wrapper.notifySupportedDeviceStatesChanged(
                         List.copyOf(mLastReceivedInfo.supportedStates));
-                wrapper.notifyBaseStateChanged(mLastReceivedInfo.baseState.getIdentifier());
-                wrapper.notifyStateChanged(mLastReceivedInfo.currentState.getIdentifier());
                 wrapper.notifyDeviceStateChanged(mLastReceivedInfo.currentState);
             }
         }
@@ -330,15 +297,6 @@
         return -1;
     }
 
-    @GuardedBy("mLock")
-    private int[] getSupportedStateIdentifiersLocked(List<DeviceState> states) {
-        int[] identifiers = new int[states.size()];
-        for (int i = 0; i < states.size(); i++) {
-            identifiers[i] = states.get(i).getIdentifier();
-        }
-        return identifiers;
-    }
-
     @Nullable
     private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
         for (int i = 0; i < mRequests.size(); i++) {
@@ -353,12 +311,10 @@
     private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
         ArrayList<DeviceStateCallbackWrapper> callbacks;
         DeviceStateInfo oldInfo;
-        int[] supportedStateIdentifiers;
         synchronized (mLock) {
             oldInfo = mLastReceivedInfo;
             mLastReceivedInfo = info;
             callbacks = new ArrayList<>(mCallbacks);
-            supportedStateIdentifiers = getSupportedStateIdentifiersLocked(info.supportedStates);
         }
 
         final int diff = oldInfo == null ? ~0 : info.diff(oldInfo);
@@ -366,18 +322,11 @@
             for (int i = 0; i < callbacks.size(); i++) {
                 callbacks.get(i).notifySupportedDeviceStatesChanged(
                         List.copyOf(info.supportedStates));
-                callbacks.get(i).notifySupportedStatesChanged(supportedStateIdentifiers);
-            }
-        }
-        if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
-            for (int i = 0; i < callbacks.size(); i++) {
-                callbacks.get(i).notifyBaseStateChanged(info.baseState.getIdentifier());
             }
         }
         if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
             for (int i = 0; i < callbacks.size(); i++) {
                 callbacks.get(i).notifyDeviceStateChanged(info.currentState);
-                callbacks.get(i).notifyStateChanged(info.currentState.getIdentifier());
             }
         }
     }
@@ -439,26 +388,11 @@
             mExecutor = executor;
         }
 
-        void notifySupportedStatesChanged(int[] newSupportedStates) {
-            mExecutor.execute(() ->
-                    mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
-        }
-
         void notifySupportedDeviceStatesChanged(List<DeviceState> newSupportedDeviceStates) {
             mExecutor.execute(() ->
                     mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));
         }
 
-        void notifyBaseStateChanged(int newBaseState) {
-            execute("notifyBaseStateChanged",
-                    () -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
-        }
-
-        void notifyStateChanged(int newDeviceState) {
-            execute("notifyStateChanged",
-                    () -> mDeviceStateCallback.onStateChanged(newDeviceState));
-        }
-
         void notifyDeviceStateChanged(DeviceState newDeviceState) {
             execute("notifyDeviceStateChanged",
                     () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
index 893d765..7665e2f 100644
--- a/core/java/android/hardware/devicestate/DeviceStateRequest.java
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -115,8 +115,8 @@
          * requested state.
          * <p>
          * Guaranteed to be called after a call to
-         * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} with a state
-         * matching the requested state.
+         * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} with a
+         * state matching the requested state.
          */
         default void onRequestActivated(@NonNull DeviceStateRequest request) {}
 
@@ -124,7 +124,7 @@
          * Called to indicate the request has been temporarily suspended.
          * <p>
          * Guaranteed to be called before a call to
-         * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+         * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
          */
         default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
 
@@ -134,7 +134,7 @@
          * DeviceStateRequest.Callback)}.
          * <p>
          * Guaranteed to be called before a call to
-         * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+         * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
          * <p>
          * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
          * occur before this method.
diff --git a/core/java/android/hardware/devicestate/DeviceStateUtil.java b/core/java/android/hardware/devicestate/DeviceStateUtil.java
new file mode 100644
index 0000000..627e740
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateUtil.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
+
+import android.annotation.NonNull;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utilities for {@link DeviceStateManager}.
+ * @hide
+ */
+public class DeviceStateUtil {
+    private DeviceStateUtil() { }
+
+    /**
+     * Returns the state identifier of the {@link DeviceState} that matches the
+     * {@code currentState}s physical properties. This will return the identifier of the
+     * {@link DeviceState} that matches the devices physical configuration.
+     *
+     * Returns {@link INVALID_DEVICE_STATE_IDENTIFIER} if there is no {@link DeviceState} in the
+     * provided list of {@code supportedStates} that matches.
+     * @hide
+     */
+    public static int calculateBaseStateIdentifier(@NonNull DeviceState currentState,
+            @NonNull List<DeviceState> supportedStates) {
+        DeviceState.Configuration stateConfiguration = currentState.getConfiguration();
+        for (int i = 0; i < supportedStates.size(); i++) {
+            DeviceState stateToCompare = supportedStates.get(i);
+            if (stateToCompare.getConfiguration().getPhysicalProperties().isEmpty()) {
+                continue;
+            }
+            if (isDeviceStateMatchingPhysicalProperties(stateConfiguration.getPhysicalProperties(),
+                    supportedStates.get(i))) {
+                return supportedStates.get(i).getIdentifier();
+            }
+        }
+        return INVALID_DEVICE_STATE_IDENTIFIER;
+    }
+
+    /**
+     * Returns if the physical properties provided, matches the same physical properties on the
+     * provided {@link DeviceState}.
+     */
+    private static boolean isDeviceStateMatchingPhysicalProperties(
+            Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties,
+            DeviceState state) {
+        Iterator<@DeviceState.PhysicalDeviceStateProperties Integer> iterator =
+                physicalProperties.iterator();
+        while (iterator.hasNext()) {
+            if (!state.hasProperty(iterator.next())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index eb26a76..4894fb1 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1653,6 +1653,22 @@
     }
 
     /**
+     * Allows internal application to restrict display modes to specified modeIds
+     *
+     * @param displayId display that restrictions will be applied to
+     * @param modeIds allowed mode ids
+     *
+     * @hide
+     */
+    @RequiresPermission("android.permission.RESTRICT_DISPLAY_MODES")
+    public void requestDisplayModes(int displayId, @Nullable int[] modeIds) {
+        if (modeIds != null && modeIds.length == 0) {
+            throw new IllegalArgumentException("requestDisplayModes: modesIds can't be empty");
+        }
+        mGlobal.requestDisplayModes(displayId, modeIds);
+    }
+
+    /**
      * Listens for changes in available display devices.
      */
     public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 75f0ceb..3d7b714 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -38,6 +38,7 @@
 import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.media.projection.IMediaProjection;
 import android.media.projection.MediaProjection;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.IBinder;
@@ -138,6 +139,8 @@
 
     private int mWifiDisplayScanNestCount;
 
+    private final Binder mToken = new Binder();
+
     @VisibleForTesting
     public DisplayManagerGlobal(IDisplayManager dm) {
         mDm = dm;
@@ -1182,6 +1185,20 @@
         }
     }
 
+    /**
+     * Sets allowed display mode ids
+     *
+     * @hide
+     */
+    @RequiresPermission("android.permission.RESTRICT_DISPLAY_MODES")
+    public void requestDisplayModes(int displayId, @Nullable int[] modeIds) {
+        try {
+            mDm.requestDisplayModes(mToken, displayId, modeIds);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
         @Override
         public void onDisplayEvent(int displayId, @DisplayEvent int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 83de4e4..70efc6f 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -235,4 +235,8 @@
     // Disable a connected display that is enabled.
     @EnforcePermission("MANAGE_DISPLAYS")
     void disableConnectedDisplay(int displayId);
+
+    // Restricts display modes to specified modeIds.
+    @EnforcePermission("RESTRICT_DISPLAY_MODES")
+    void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 318c291..84619a0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1024,7 +1024,11 @@
     /**
      * Specifies if a user is disallowed from creating a private profile.
      * <p>The default value for an unmanaged user is <code>false</code>.
-     * For users with a device owner set, the default is <code>true</code>.
+     * For users with a device owner set, the default value is <code>true</code> and the
+     * device owner currently cannot change it to <code>false</code>.
+     * On organization-owned managed profile devices, the default value is <code>false</code> but
+     * the profile owner can change it to <code>true</code> via the parent profile to block creating
+     * of private profiles on the personal user.
      *
      * <p>Holders of the permission
      * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PROFILES}
@@ -1034,9 +1038,10 @@
      * <p>Type: Boolean
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#getParentProfileInstance(ComponentName)
      * @see #getUserRestrictions()
-     * @hide
      */
+    @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
     public static final String DISALLOW_ADD_PRIVATE_PROFILE = "no_add_private_profile";
 
     /**
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index 27e8628..6815440 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -222,7 +222,10 @@
 
     /**
      * Invoked by the {@link OnDeviceIntelligenceService} inorder to send updates to the inference
-     * service if there is a state change to be performed.
+     * service if there is a state change to be performed. State change could be config updates,
+     * performing initialization or cleanup tasks in the remote inference service.
+     * The Bundle passed in here is expected to be read-only and will be rejected if it has any
+     * writable fields as detailed under {@link InferenceParams}.
      *
      * @param processingState  the updated state to be applied.
      * @param callbackExecutor executor to the run status callback on.
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 9b9cc19..7a1c75a 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -100,7 +101,7 @@
      */
     @SystemApi
     @SuppressLint("RequiresPermission")
-    public int write(byte[] data) {
+    public int write(@Nullable byte[] data) {
         try {
             return sService.write(data);
         } catch (RemoteException e) {
@@ -115,7 +116,7 @@
      */
     @SystemApi
     @SuppressLint("RequiresPermission")
-    public byte[] read() {
+    public @Nullable byte[] read() {
         try {
             return sService.read();
         } catch (RemoteException e) {
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 821e13d..b300022 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -49,9 +49,9 @@
 
     /**
      * Tracks whether we have an outstanding request from the IME to show, but weren't able to
-     * execute it because we didn't have control yet.
+     * execute it because we didn't have control yet, or we didn't have a leash on the control yet.
      */
-    private boolean mIsRequestedVisibleAwaitingControl;
+    private boolean mIsRequestedVisibleAwaitingLeash;
 
     public ImeInsetsSourceConsumer(
             int id, InsetsState state, Supplier<Transaction> transactionSupplier,
@@ -68,7 +68,7 @@
         }
         final boolean insetsChanged = super.onAnimationStateChanged(running);
         if (!isShowRequested()) {
-            mIsRequestedVisibleAwaitingControl = false;
+            mIsRequestedVisibleAwaitingLeash = false;
             if (!running && !mHasPendingRequest) {
                 final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
                         ImeTracker.ORIGIN_CLIENT,
@@ -91,8 +91,8 @@
     public void onWindowFocusGained(boolean hasViewFocus) {
         super.onWindowFocusGained(hasViewFocus);
         getImm().registerImeConsumer(this);
-        if ((mController.getRequestedVisibleTypes() & getType()) != 0 && getControl() == null) {
-            mIsRequestedVisibleAwaitingControl = true;
+        if ((mController.getRequestedVisibleTypes() & getType()) != 0 && !hasLeash()) {
+            mIsRequestedVisibleAwaitingLeash = true;
         }
     }
 
@@ -100,7 +100,7 @@
     public void onWindowFocusLost() {
         super.onWindowFocusLost();
         getImm().unregisterImeConsumer(this);
-        mIsRequestedVisibleAwaitingControl = false;
+        mIsRequestedVisibleAwaitingLeash = false;
     }
 
     @Override
@@ -130,15 +130,15 @@
         ImeTracker.forLogging().onProgress(statsToken,
                 ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW);
 
-        if (getControl() == null) {
-            // If control is null, schedule to show IME when control is available.
-            mIsRequestedVisibleAwaitingControl = true;
+        if (!hasLeash()) {
+            // If control or leash is null, schedule to show IME when both available.
+            mIsRequestedVisibleAwaitingLeash = true;
         }
         // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
         // this code here means that we now got control, so we can start the animation immediately.
         // If client window is trying to control IME and IME is already visible, it is immediate.
         if (fromIme
-                || (mState.isSourceOrDefaultVisible(getId(), getType()) && getControl() != null)) {
+                || (mState.isSourceOrDefaultVisible(getId(), getType()) && hasLeash())) {
             return ShowResult.SHOW_IMMEDIATELY;
         }
 
@@ -148,9 +148,9 @@
 
     void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
         if (!fromIme) {
-            // Create a new token to track the hide request when we have control,
+            // Create a new token to track the hide request when we have control and leash,
             // as we use the passed in token for the insets animation already.
-            final var notifyStatsToken = getControl() != null
+            final var notifyStatsToken = hasLeash()
                     ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
                         ImeTracker.ORIGIN_CLIENT,
                         SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
@@ -176,7 +176,7 @@
                 ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);
 
         getImm().notifyImeHidden(mController.getHost().getWindowToken(), statsToken);
-        mIsRequestedVisibleAwaitingControl = false;
+        mIsRequestedVisibleAwaitingLeash = false;
         Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
     }
 
@@ -196,19 +196,28 @@
         if (!super.setControl(control, showTypes, hideTypes)) {
             return false;
         }
-        if (control == null && !mIsRequestedVisibleAwaitingControl) {
+        final boolean hasLeash = control != null && control.getLeash() != null;
+        if (!hasLeash && !mIsRequestedVisibleAwaitingLeash) {
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType());
             removeSurface();
         }
-        if (control != null) {
-            mIsRequestedVisibleAwaitingControl = false;
+        if (hasLeash) {
+            mIsRequestedVisibleAwaitingLeash = false;
         }
         return true;
     }
 
     @Override
     protected boolean isRequestedVisibleAwaitingControl() {
-        return super.isRequestedVisibleAwaitingControl() || mIsRequestedVisibleAwaitingControl;
+        return super.isRequestedVisibleAwaitingControl() || mIsRequestedVisibleAwaitingLeash;
+    }
+
+    /**
+     * Checks whether the consumer has an insets source control with a leash.
+     */
+    private boolean hasLeash() {
+        final var control = getControl();
+        return control != null && control.getLeash() != null;
     }
 
     @Override
@@ -224,7 +233,7 @@
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
-        proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
+        proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingLeash);
         proto.write(HAS_PENDING_REQUEST, mHasPendingRequest);
         proto.end(token);
     }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a098e4d..4f5b51d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2631,7 +2631,10 @@
      * Threshold values that are sent with
      * {@link Transaction#setTrustedPresentationCallback(SurfaceControl,
      * TrustedPresentationThresholds, Executor, Consumer)}
+     *
+     * @deprecated Use {@link android.window.TrustedPresentationThresholds} instead.
      */
+    @Deprecated
     public static final class TrustedPresentationThresholds {
         private final float mMinAlpha;
         private final float mMinFractionRendered;
@@ -4451,8 +4454,7 @@
          *   <tr><td>o</td><td>x</td><td>x</td><td>x</td></tr>
          * </table>
          * </blockquote>
-         *
-         *<p>
+         * <p>
          * We first start by computing fr=xscale*yscale=0.9*0.9=0.81, indicating
          * that "81%" of the pixels were rendered. This corresponds to what was 100
          * pixels being displayed in 81 pixels. This is somewhat of an abuse of
@@ -4469,6 +4471,7 @@
          * be somewhat arbitrary, and so there are some somewhat arbitrary decisions in
          * this API as well.
          * <p>
+         *
          * @param sc         The {@link SurfaceControl} to set the callback on
          * @param thresholds The {@link TrustedPresentationThresholds} that will specify when the to
          *                   invoke the callback.
@@ -4477,7 +4480,11 @@
          *                   exited the threshold.
          * @return This transaction
          * @see TrustedPresentationThresholds
+         * @deprecated Use
+         * {@link WindowManager#registerTrustedPresentationListener(IBinder,
+         * android.window.TrustedPresentationThresholds, Executor, Consumer)} instead.
          */
+        @Deprecated
         @NonNull
         public Transaction setTrustedPresentationCallback(@NonNull SurfaceControl sc,
                 @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor,
@@ -4506,7 +4513,10 @@
          *
          * @param sc The SurfaceControl that the callback should be cleared from
          * @return This transaction
+         * @deprecated Use {@link WindowManager#unregisterTrustedPresentationListener(Consumer)}
+         * instead.
          */
+        @Deprecated
         @NonNull
         public Transaction clearTrustedPresentationCallback(@NonNull SurfaceControl sc) {
             checkPreconditions(sc);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 6f757df..04ec4d0 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -583,13 +583,16 @@
      * associated host {@link InputTransferToken}.
      *
      * @return Whether the touch stream was transferred.
+     * @deprecated Use {@link WindowManager#transferTouchGesture(InputTransferToken,
+     * InputTransferToken)} instead.
      */
+    @Deprecated
     public boolean transferTouchGestureToHost() {
         if (mViewRoot == null) {
             return false;
         }
-        final WindowManager wm =
-                (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
+        final WindowManager wm = (WindowManager) mViewRoot.mContext.getSystemService(
+                Context.WINDOW_SERVICE);
         InputTransferToken embeddedToken = getInputTransferToken();
         InputTransferToken hostToken = mWm.mHostInputTransferToken;
         if (embeddedToken == null || hostToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d494e28..c95d6ff 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1946,7 +1946,9 @@
      * be passed from the host process to the client process.
      *
      * @return The token
+     * @deprecated Use {@link AttachedSurfaceControl#getInputTransferToken()} instead.
      */
+    @Deprecated
     public @Nullable IBinder getHostToken() {
         final ViewRootImpl viewRoot = getViewRootImpl();
         if (viewRoot == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0ac8936..0a75f4e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18,6 +18,7 @@
 
 import static android.content.res.Resources.ID_NULL;
 import static android.os.Trace.TRACE_TAG_APP;
+import static android.os.Trace.TRACE_TAG_VIEW;
 import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
 import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
@@ -16356,20 +16357,20 @@
         ListenerInfo li = mListenerInfo;
         if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED) {
             try {
-                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "View.onTouchListener#onTouch");
+                Trace.traceBegin(TRACE_TAG_VIEW, "View.onTouchListener#onTouch");
                 handled = li.mOnTouchListener.onTouch(this, event);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+                Trace.traceEnd(TRACE_TAG_VIEW);
             }
         }
         if (handled) {
             return true;
         }
         try {
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "View#onTouchEvent");
+            Trace.traceBegin(TRACE_TAG_VIEW, "View#onTouchEvent");
             return onTouchEvent(event);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            Trace.traceEnd(TRACE_TAG_VIEW);
         }
     }
 
@@ -23846,14 +23847,14 @@
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+            if (Trace.isTagEnabled(TRACE_TAG_VIEW)) {
+                Trace.traceBegin(TRACE_TAG_VIEW,
                         "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
             }
             try {
                 buildDrawingCacheImpl(autoScale);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+                Trace.traceEnd(TRACE_TAG_VIEW);
             }
         }
     }
@@ -28980,6 +28981,7 @@
         surface.copyFrom(surfaceControl);
         IBinder token = null;
         try {
+            Trace.traceBegin(TRACE_TAG_VIEW, "startDragAndDrop#drawDragShadow");
             final Canvas canvas = isHardwareAccelerated()
                     ? surface.lockHardwareCanvas()
                     : surface.lockCanvas(null);
@@ -28988,33 +28990,40 @@
                 shadowBuilder.onDrawShadow(canvas);
             } finally {
                 surface.unlockCanvasAndPost(canvas);
+                Trace.traceEnd(TRACE_TAG_VIEW);
             }
 
-            token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
-                    root.getLastTouchSource(), root.getLastTouchDeviceId(),
-                    root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
-                    shadowTouchPoint.x, shadowTouchPoint.y, data);
-            if (ViewDebug.DEBUG_DRAG) {
-                Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
+            Trace.traceBegin(TRACE_TAG_VIEW, "startDragAndDrop#performDrag");
+            try {
+                token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
+                        root.getLastTouchSource(), root.getLastTouchDeviceId(),
+                        root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
+                        shadowTouchPoint.x, shadowTouchPoint.y, data);
+                if (ViewDebug.DEBUG_DRAG) {
+                    Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
+                }
+                if (token != null) {
+                    if (mAttachInfo.mDragSurface != null) {
+                        mAttachInfo.mDragSurface.release();
+                    }
+                    if (mAttachInfo.mDragData != null) {
+                        // Clean up previous drag data intents
+                        View.cleanUpPendingIntents(mAttachInfo.mDragData);
+                    }
+                    mAttachInfo.mDragSurface = surface;
+                    mAttachInfo.mDragToken = token;
+                    mAttachInfo.mDragData = data;
+                    // Cache the local state object for delivery with DragEvents
+                    root.setLocalDragState(myLocalState);
+                    if (a11yEnabled) {
+                        // Set for AccessibilityEvents
+                        mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
+                    }
+                }
+                return token != null;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_VIEW);
             }
-            if (token != null) {
-                if (mAttachInfo.mDragSurface != null) {
-                    mAttachInfo.mDragSurface.release();
-                }
-                if (mAttachInfo.mDragData != null) {
-                    View.cleanUpPendingIntents(mAttachInfo.mDragData);
-                }
-                mAttachInfo.mDragSurface = surface;
-                mAttachInfo.mDragToken = token;
-                mAttachInfo.mDragData = data;
-                // Cache the local state object for delivery with DragEvents
-                root.setLocalDragState(myLocalState);
-                if (a11yEnabled) {
-                    // Set for AccessibilityEvents
-                    mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
-                }
-            }
-            return token != null;
         } catch (Exception e) {
             Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
             return false;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8c3cf5f..cae6672 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,8 +21,10 @@
 import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
 import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
 import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
+import static android.os.Trace.TRACE_TAG_VIEW;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.DragEvent.ACTION_DRAG_LOCATION;
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
@@ -6473,7 +6475,18 @@
                     DragEvent event = (DragEvent) msg.obj;
                     // only present when this app called startDrag()
                     event.mLocalState = mLocalDragState;
-                    handleDragEvent(event);
+                    final boolean traceDragEvent = event.mAction != ACTION_DRAG_LOCATION;
+                    try {
+                        if (traceDragEvent) {
+                            Trace.traceBegin(TRACE_TAG_VIEW,
+                                    "c#" + DragEvent.actionToString(event.mAction));
+                        }
+                        handleDragEvent(event);
+                    } finally {
+                        if (traceDragEvent) {
+                            Trace.traceEnd(TRACE_TAG_VIEW);
+                        }
+                    }
                 } break;
                 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
                     handleDispatchSystemUiVisibilityChanged();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c127a43..107c5f2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -5871,7 +5871,10 @@
                     && height == WindowManager.LayoutParams.MATCH_PARENT;
         }
 
-        private static String layoutInDisplayCutoutModeToString(
+        /**
+         * @hide
+         */
+        public static String layoutInDisplayCutoutModeToString(
                 @LayoutInDisplayCutoutMode int mode) {
             switch (mode) {
                 case LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 40a437f..9dec102 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -129,7 +129,6 @@
     private final int mDefaultWindowAnimationStyleResId;
 
     private final boolean mDebug;
-    private final boolean mGridLayoutRecentsEnabled;
     private final boolean mLowRamRecentsEnabled;
 
     public TransitionAnimation(Context context, boolean debug, String tag) {
@@ -166,7 +165,6 @@
         mConfigShortAnimTime = context.getResources().getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
 
-        mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
         mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic();
 
         final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
@@ -768,10 +766,8 @@
                         // We scale the width and clip to the top/left square
                         float scale =
                                 thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
-                        if (!mGridLayoutRecentsEnabled) {
-                            int unscaledThumbHeight = (int) (thumbHeight / scale);
-                            mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
-                        }
+                        int unscaledThumbHeight = (int) (thumbHeight / scale);
+                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
 
                         Animation scaleAnim = new ScaleAnimation(
                                 scaleUp ? scale : 1, scaleUp ? 1 : scale,
@@ -887,12 +883,6 @@
             toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
             pivotX = mTmpRect.width() / 2;
             pivotY = appRect.height() / 2 / scaleW;
-            if (mGridLayoutRecentsEnabled) {
-                // In the grid layout, the header is displayed above the thumbnail instead of
-                // overlapping it.
-                fromY -= thumbHeightI;
-                toY -= thumbHeightI * scaleW;
-            }
         } else {
             pivotX = 0;
             pivotY = 0;
@@ -936,10 +926,7 @@
             // This AnimationSet uses the Interpolators assigned above.
             AnimationSet set = new AnimationSet(false);
             set.addAnimation(scale);
-            if (!mGridLayoutRecentsEnabled) {
-                // In the grid layout, the header should be shown for the whole animation.
-                set.addAnimation(alpha);
-            }
+            set.addAnimation(alpha);
             set.addAnimation(translate);
             set.addAnimation(clipAnim);
             a = set;
@@ -958,10 +945,7 @@
             // This AnimationSet uses the Interpolators assigned above.
             AnimationSet set = new AnimationSet(false);
             set.addAnimation(scale);
-            if (!mGridLayoutRecentsEnabled) {
-                // In the grid layout, the header should be shown for the whole animation.
-                set.addAnimation(alpha);
-            }
+            set.addAnimation(alpha);
             set.addAnimation(translate);
             a = set;
 
@@ -1081,8 +1065,7 @@
      * @return whether the transition should show the thumbnail being scaled down.
      */
     private boolean shouldScaleDownThumbnailTransition(int orientation) {
-        return mGridLayoutRecentsEnabled
-                || orientation == Configuration.ORIENTATION_PORTRAIT;
+        return orientation == Configuration.ORIENTATION_PORTRAIT;
     }
 
     private static int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a2efbd2..a22232a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -376,6 +376,12 @@
     void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop);
 
     /**
+     * Set the split screen focus to the left / top app or the right / bottom app based on
+     * {@param leftOrTop}.
+     */
+    void setSplitscreenFocus(boolean leftOrTop);
+
+    /**
      * Shows the media output switcher dialog.
      *
      * @param packageName of the session for which the output switcher is shown.
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 79b1376..a44e92c 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -156,6 +156,7 @@
     generate_product_characteristics_rro: true,
 
     flags_packages: [
+        "android.app.contextualsearch.flags-aconfig",
         "android.content.pm.flags-aconfig",
         "android.provider.flags-aconfig",
         "camera_platform_flags",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0869af5..1b9da20 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3801,12 +3801,12 @@
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
                 android:protectionLevel="internal|role" />
 
-    <!-- Allows an application to manage policy related to theft detection.
+    <!-- Allows an application to query the device stolen state.
         @FlaggedApi("android.app.admin.flags.device_theft_api_enabled")
         @hide
         @SystemApi
     -->
-    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION"
+    <permission android:name="android.permission.QUERY_DEVICE_STOLEN_STATE"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to manage policy related to system apps.
@@ -6776,11 +6776,12 @@
     <permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG"
                 android:protectionLevel="signature" />
 
-    <!-- Allows an application to set the BiometricDialog (SystemUI) logo .
+    <!-- Allows an application to set the advanced features on BiometricDialog (SystemUI), including
+         logo, logo description.
          <p>Not for use by third-party applications.
          @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
     -->
-    <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO"
+    <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED"
                 android:protectionLevel="signature|privileged" />
 
     <!-- Allows an application to control keyguard.  Only allowed for system processes.
@@ -7208,6 +7209,13 @@
     <permission android:name="android.permission.ACCESS_SMARTSPACE"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- @SystemApi Allows an application to start a contextual search.
+         @FlaggedApi("android.app.contextualsearch.flags.enable_service")
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.ACCESS_CONTEXTUAL_SEARCH"
+        android:protectionLevel="signature|privileged"
+        android:featureFlag="android.app.contextualsearch.flags.enable_service"/>
+
     <!-- @SystemApi Allows an application to manage the wallpaper effects
      generation service.
      @hide  <p>Not for use by third-party applications.</p> -->
@@ -8150,6 +8158,14 @@
     <permission android:name="android.permission.EMERGENCY_INSTALL_PACKAGES"
                 android:protectionLevel="signature|privileged"/>
 
+    <!-- Allows internal applications to restrict display modes
+       <p>Not for use by third-party applications.
+       <p>Protection level: signature
+       @hide
+    -->
+    <permission android:name="android.permission.RESTRICT_DISPLAY_MODES"
+        android:protectionLevel="signature" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b879c97..417c6df 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -238,204 +238,204 @@
 
     <color name="conversation_important_highlight">#F9AB00</color>
 
-    <!-- Lightest shade of the accent color used by the system. White.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_0">#ffffff</color>
-    <!-- Shade of the accent system color at 99% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_10">#F9FCFF</color>
-    <!-- Shade of the accent system color at 95% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_50">#E0F3FF</color>
-    <!-- Shade of the accent system color at 90% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_100">#C1E8FF</color>
-    <!-- Shade of the accent system color at 80% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_200">#76D1FF</color>
-    <!-- Shade of the accent system color at 70% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_300">#4BB6E8</color>
-    <!-- Shade of the accent system color at 60% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_400">#219BCC</color>
-    <!-- Shade of the accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_500">#007FAC</color>
-    <!-- Shade of the accent system color at 40% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_600">#00668B</color>
-    <!-- Shade of the accent system color at 30% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_700">#004C69</color>
-    <!-- Shade of the accent system color at 20% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_800">#003549</color>
-    <!-- Shade of the accent system color at 10% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_900">#001E2C</color>
-    <!-- Darkest shade of the accent color used by the system. Black.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
+    <!--Lightest shade of the Primary color used by the system. White.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_0">#FFFFFF</color>
+    <!--Shade of the Primary system color at 99% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_10">#FEFBFF</color>
+    <!--Shade of the Primary system color at 95% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_50">#EEF0FF</color>
+    <!--Shade of the Primary system color at 90% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_100">#D9E2FF</color>
+    <!--Shade of the Primary system color at 80% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_200">#B0C6FF</color>
+    <!--Shade of the Primary system color at 70% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_300">#94AAE4</color>
+    <!--Shade of the Primary system color at 60% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_400">#7A90C8</color>
+    <!--Shade of the Primary system color at 50% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_500">#6076AC</color>
+    <!--Shade of the Primary system color at 40% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_600">#475D92</color>
+    <!--Shade of the Primary system color at 30% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_700">#2F4578</color>
+    <!--Shade of the Primary system color at 20% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_800">#152E60</color>
+    <!--Shade of the Primary system color at 10% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent1_900">#001945</color>
+    <!--Darkest shade of the Primary color used by the system. Black.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
     <color name="system_accent1_1000">#000000</color>
 
-    <!-- Lightest shade of the secondary accent color used by the system. White.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_0">#ffffff</color>
-    <!-- Shade of the secondary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_10">#F9FCFF</color>
-    <!-- Shade of the secondary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_50">#E0F3FF</color>
-    <!-- Shade of the secondary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_100">#D1E5F4</color>
-    <!-- Shade of the secondary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_200">#B5CAD7</color>
-    <!-- Shade of the secondary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_300">#9AAEBB</color>
-    <!-- Shade of the secondary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_400">#8094A0</color>
-    <!-- Shade of the secondary accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_500">#657985</color>
-    <!-- Shade of the secondary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_600">#4E616C</color>
-    <!-- Shade of the secondary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_700">#374955</color>
-    <!-- Shade of the secondary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_800">#20333D</color>
-    <!-- Shade of the secondary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_900">#091E28</color>
-    <!-- Darkest shade of the secondary accent color used by the system. Black.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
+    <!--Lightest shade of the Secondary color used by the system. White.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_0">#FFFFFF</color>
+    <!--Shade of the Secondary system color at 99% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_10">#FEFBFF</color>
+    <!--Shade of the Secondary system color at 95% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_50">#EEF0FF</color>
+    <!--Shade of the Secondary system color at 90% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_100">#DCE2F9</color>
+    <!--Shade of the Secondary system color at 80% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_200">#C0C6DC</color>
+    <!--Shade of the Secondary system color at 70% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_300">#A4ABC1</color>
+    <!--Shade of the Secondary system color at 60% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_400">#8A90A5</color>
+    <!--Shade of the Secondary system color at 50% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_500">#70778B</color>
+    <!--Shade of the Secondary system color at 40% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_600">#575E71</color>
+    <!--Shade of the Secondary system color at 30% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_700">#404659</color>
+    <!--Shade of the Secondary system color at 20% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_800">#2A3042</color>
+    <!--Shade of the Secondary system color at 10% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent2_900">#151B2C</color>
+    <!--Darkest shade of the Secondary color used by the system. Black.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
     <color name="system_accent2_1000">#000000</color>
 
-    <!-- Lightest shade of the tertiary accent color used by the system. White.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_0">#ffffff</color>
-    <!-- Shade of the tertiary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
+    <!--Lightest shade of the Tertiary color used by the system. White.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_0">#FFFFFF</color>
+    <!--Shade of the Tertiary system color at 99% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
     <color name="system_accent3_10">#FFFBFF</color>
-    <!-- Shade of the tertiary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_50">#F5EEFF</color>
-    <!-- Shade of the tertiary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_100">#E6DEFF</color>
-    <!-- Shade of the tertiary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_200">#CAC1EA</color>
-    <!-- Shade of the tertiary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_300">#AEA6CE</color>
-    <!-- Shade of the tertiary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_400">#938CB1</color>
-    <!-- Shade of the tertiary accent system color at 49% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_500">#787296</color>
-    <!-- Shade of the tertiary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_600">#605A7C</color>
-    <!-- Shade of the tertiary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_700">#484264</color>
-    <!-- Shade of the tertiary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_800">#322C4C</color>
-    <!-- Shade of the tertiary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_900">#1D1736</color>
-    <!-- Darkest shade of the tertiary accent color used by the system. Black.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
+    <!--Shade of the Tertiary system color at 95% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_50">#FFEBFA</color>
+    <!--Shade of the Tertiary system color at 90% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_100">#FDD7FA</color>
+    <!--Shade of the Tertiary system color at 80% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_200">#E0BBDD</color>
+    <!--Shade of the Tertiary system color at 70% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_300">#C3A0C1</color>
+    <!--Shade of the Tertiary system color at 60% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_400">#A886A6</color>
+    <!--Shade of the Tertiary system color at 50% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_500">#8C6D8C</color>
+    <!--Shade of the Tertiary system color at 40% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_600">#725572</color>
+    <!--Shade of the Tertiary system color at 30% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_700">#593D59</color>
+    <!--Shade of the Tertiary system color at 20% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_800">#412742</color>
+    <!--Shade of the Tertiary system color at 10% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_accent3_900">#2A122C</color>
+    <!--Darkest shade of the Tertiary color used by the system. Black.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
     <color name="system_accent3_1000">#000000</color>
 
-    <!-- Lightest shade of the neutral color used by the system. White.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_0">#ffffff</color>
-    <!-- Shade of the neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_10">#FCFCFF</color>
-    <!-- Shade of the neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_50">#F0F0F3</color>
-    <!-- Shade of the neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_100">#E1E3E5</color>
-    <!-- Shade of the neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_200">#C5C7C9</color>
-    <!-- Shade of the neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_300">#AAABAE</color>
-    <!-- Shade of the neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_400">#8F9193</color>
-    <!-- Shade of the neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_500">#747679</color>
-    <!-- Shade of the neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_600">#5C5F61</color>
-    <!-- Shade of the neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_700">#454749</color>
-    <!-- Shade of the neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_800">#2E3133</color>
-    <!-- Shade of the neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_900">#191C1E</color>
-    <!-- Darkest shade of the neutral color used by the system. Black.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
+    <!--Lightest shade of the Neutral color used by the system. White.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_0">#FFFFFF</color>
+    <!--Shade of the Neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_10">#FEFBFF</color>
+    <!--Shade of the Neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_50">#F1F0F7</color>
+    <!--Shade of the Neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_100">#E2E2E9</color>
+    <!--Shade of the Neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_200">#C6C6CD</color>
+    <!--Shade of the Neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_300">#ABABB1</color>
+    <!--Shade of the Neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_400">#909097</color>
+    <!--Shade of the Neutral system color at 50% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_500">#76777D</color>
+    <!--Shade of the Neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_600">#5D5E64</color>
+    <!--Shade of the Neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_700">#45464C</color>
+    <!--Shade of the Neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_800">#2F3036</color>
+    <!--Shade of the Neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral1_900">#1A1B20</color>
+    <!--Darkest shade of the Neutral color used by the system. Black.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
     <color name="system_neutral1_1000">#000000</color>
 
-    <!-- Lightest shade of the secondary neutral color used by the system. White.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_0">#ffffff</color>
-    <!-- Shade of the secondary neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_10">#F9FCFF</color>
-    <!-- Shade of the secondary neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_50">#EBF1F8</color>
-    <!-- Shade of the secondary neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_100">#DCE3E9</color>
-    <!-- Shade of the secondary neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_200">#C0C7CD</color>
-    <!-- Shade of the secondary neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_300">#A5ACB2</color>
-    <!-- Shade of the secondary neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_400">#8A9297</color>
-    <!-- Shade of the secondary neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_500">#70777C</color>
-    <!-- Shade of the secondary neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_600">#585F65</color>
-    <!-- Shade of the secondary neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_700">#40484D</color>
-    <!-- Shade of the secondary neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_800">#2A3136</color>
-    <!-- Shade of the secondary neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
-     This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_900">#161C20</color>
-    <!-- Darkest shade of the secondary neutral color used by the system. Black.
-     This value can be overlaid at runtime by OverlayManager RROs. -->
+    <!--Lightest shade of the Secondary Neutral color used by the system. White.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_0">#FFFFFF</color>
+    <!--Shade of the Secondary Neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_10">#FEFBFF</color>
+    <!--Shade of the Secondary Neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_50">#F0F0FA</color>
+    <!--Shade of the Secondary Neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_100">#E1E2EC</color>
+    <!--Shade of the Secondary Neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_200">#C5C6D0</color>
+    <!--Shade of the Secondary Neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_300">#A9ABB4</color>
+    <!--Shade of the Secondary Neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_400">#8F9099</color>
+    <!--Shade of the Secondary Neutral system color at 50% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_500">#757780</color>
+    <!--Shade of the Secondary Neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_600">#5C5E67</color>
+    <!--Shade of the Secondary Neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_700">#44464F</color>
+    <!--Shade of the Secondary Neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_800">#2E3038</color>
+    <!--Shade of the Secondary Neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
+     This value can be overlaid at runtime by OverlayManager RROs.-->
+    <color name="system_neutral2_900">#191B23</color>
+    <!--Darkest shade of the Secondary Neutral color used by the system. Black.
+     This value can be overlaid at runtime by OverlayManager RROs.-->
     <color name="system_neutral2_1000">#000000</color>
 
     <!-- Lightest shade of the error color used by the system. White.
@@ -480,106 +480,106 @@
 
     <!-- Colors used in Android system, from design system.
      These values can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_primary_container_light">#D8E2FF</color>
-    <color name="system_on_primary_container_light">#001A41</color>
-    <color name="system_primary_light">#445E91</color>
+    <color name="system_primary_container_light">#5E73A9</color>
+    <color name="system_on_primary_container_light">#FFFFFF</color>
+    <color name="system_primary_light">#2A4174</color>
     <color name="system_on_primary_light">#FFFFFF</color>
-    <color name="system_secondary_container_light">#DBE2F9</color>
-    <color name="system_on_secondary_container_light">#141B2C</color>
-    <color name="system_secondary_light">#575E71</color>
+    <color name="system_secondary_container_light">#6E7488</color>
+    <color name="system_on_secondary_container_light">#FFFFFF</color>
+    <color name="system_secondary_light">#3C4255</color>
     <color name="system_on_secondary_light">#FFFFFF</color>
-    <color name="system_tertiary_container_light">#FBD7FC</color>
-    <color name="system_on_tertiary_container_light">#29132D</color>
-    <color name="system_tertiary_light">#715573</color>
+    <color name="system_tertiary_container_light">#8A6A89</color>
+    <color name="system_on_tertiary_container_light">#FFFFFF</color>
+    <color name="system_tertiary_light">#553A55</color>
     <color name="system_on_tertiary_light">#FFFFFF</color>
-    <color name="system_background_light">#FAF9FD</color>
-    <color name="system_on_background_light">#1B1B1F</color>
-    <color name="system_surface_light">#FAF9FD</color>
-    <color name="system_on_surface_light">#1B1B1F</color>
-    <color name="system_surface_container_low_light">#F5F3F7</color>
+    <color name="system_background_light">#FAF8FF</color>
+    <color name="system_on_background_light">#1A1B20</color>
+    <color name="system_surface_light">#FAF8FF</color>
+    <color name="system_on_surface_light">#1A1B20</color>
+    <color name="system_surface_container_low_light">#F4F3FA</color>
     <color name="system_surface_container_lowest_light">#FFFFFF</color>
-    <color name="system_surface_container_light">#EFEDF1</color>
-    <color name="system_surface_container_high_light">#E9E7EC</color>
-    <color name="system_surface_container_highest_light">#E3E2E6</color>
-    <color name="system_surface_bright_light">#FAF9FD</color>
-    <color name="system_surface_dim_light">#DBD9DD</color>
+    <color name="system_surface_container_light">#EEEDF4</color>
+    <color name="system_surface_container_high_light">#E8E7EF</color>
+    <color name="system_surface_container_highest_light">#E2E2E9</color>
+    <color name="system_surface_bright_light">#FAF8FF</color>
+    <color name="system_surface_dim_light">#DAD9E0</color>
     <color name="system_surface_variant_light">#E1E2EC</color>
-    <color name="system_on_surface_variant_light">#44474F</color>
-    <color name="system_outline_light">#72747D</color>
-    <color name="system_outline_variant_light">#C4C7C5</color>
-    <color name="system_error_light">#C00003</color>
+    <color name="system_on_surface_variant_light">#40434B</color>
+    <color name="system_outline_light">#5D5F67</color>
+    <color name="system_outline_variant_light">#797A83</color>
+    <color name="system_error_light">#8C0009</color>
     <color name="system_on_error_light">#FFFFFF</color>
-    <color name="system_error_container_light">#FFDAD5</color>
-    <color name="system_on_error_container_light">#410000</color>
-    <color name="system_primary_fixed">#D8E2FF</color>
-    <color name="system_primary_fixed_dim">#ADC6FF</color>
-    <color name="system_on_primary_fixed">#001A41</color>
-    <color name="system_on_primary_fixed_variant">#2B4678</color>
-    <color name="system_secondary_fixed">#DBE2F9</color>
-    <color name="system_secondary_fixed_dim">#BFC6DC</color>
-    <color name="system_on_secondary_fixed">#141B2C</color>
-    <color name="system_on_secondary_fixed_variant">#3F4759</color>
-    <color name="system_tertiary_fixed">#FBD7FC</color>
-    <color name="system_tertiary_fixed_dim">#DEBCDF</color>
-    <color name="system_on_tertiary_fixed">#29132D</color>
-    <color name="system_on_tertiary_fixed_variant">#583E5B</color>
-    <color name="system_control_activated_light">#D8E2FF</color>
-    <color name="system_control_normal_light">#44474F</color>
-    <color name="system_control_highlight_light">#1F000000</color>
-    <color name="system_text_primary_inverse_light">#E3E2E6</color>
-    <color name="system_text_secondary_and_tertiary_inverse_light">#C4C6D0</color>
-    <color name="system_text_primary_inverse_disable_only_light">#E3E2E6</color>
-    <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E3E2E6</color>
-    <color name="system_text_hint_inverse_light">#E3E2E6</color>
-    <color name="system_palette_key_color_primary_light">#5D77AC</color>
-    <color name="system_palette_key_color_secondary_light">#6C7488</color>
-    <color name="system_palette_key_color_tertiary_light">#907292</color>
-    <color name="system_palette_key_color_neutral_light">#838387</color>
-    <color name="system_palette_key_color_neutral_variant_light">#777982</color>
-    <color name="system_primary_container_dark">#2B4678</color>
-    <color name="system_on_primary_container_dark">#D8E2FF</color>
-    <color name="system_primary_dark">#ADC6FF</color>
-    <color name="system_on_primary_dark">#102F60</color>
-    <color name="system_secondary_container_dark">#3F4759</color>
-    <color name="system_on_secondary_container_dark">#DBE2F9</color>
-    <color name="system_secondary_dark">#BFC6DC</color>
-    <color name="system_on_secondary_dark">#293041</color>
-    <color name="system_tertiary_container_dark">#583E5B</color>
-    <color name="system_on_tertiary_container_dark">#FBD7FC</color>
-    <color name="system_tertiary_dark">#DEBCDF</color>
-    <color name="system_on_tertiary_dark">#402843</color>
-    <color name="system_background_dark">#121316</color>
-    <color name="system_on_background_dark">#E3E2E6</color>
-    <color name="system_surface_dark">#121316</color>
-    <color name="system_on_surface_dark">#E3E2E6</color>
-    <color name="system_surface_container_low_dark">#1B1B1F</color>
-    <color name="system_surface_container_lowest_dark">#0D0E11</color>
-    <color name="system_surface_container_dark">#1F1F23</color>
-    <color name="system_surface_container_high_dark">#292A2D</color>
-    <color name="system_surface_container_highest_dark">#343538</color>
-    <color name="system_surface_bright_dark">#38393C</color>
-    <color name="system_surface_dim_dark">#121316</color>
-    <color name="system_surface_variant_dark">#44474F</color>
-    <color name="system_on_surface_variant_dark">#C4C6D0</color>
-    <color name="system_outline_dark">#8E9099</color>
-    <color name="system_outline_variant_dark">#444746</color>
-    <color name="system_error_dark">#FFB4A8</color>
-    <color name="system_on_error_dark">#690001</color>
-    <color name="system_error_container_dark">#930001</color>
-    <color name="system_on_error_container_dark">#FFDAD5</color>
-    <color name="system_control_activated_dark">#2B4678</color>
-    <color name="system_control_normal_dark">#C4C6D0</color>
-    <color name="system_control_highlight_dark">#33FFFFFF</color>
-    <color name="system_text_primary_inverse_dark">#1B1B1F</color>
-    <color name="system_text_secondary_and_tertiary_inverse_dark">#44474F</color>
-    <color name="system_text_primary_inverse_disable_only_dark">#1B1B1F</color>
-    <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1B1B1F</color>
-    <color name="system_text_hint_inverse_dark">#1B1B1F</color>
-    <color name="system_palette_key_color_primary_dark">#5D77AC</color>
-    <color name="system_palette_key_color_secondary_dark">#6C7488</color>
-    <color name="system_palette_key_color_tertiary_dark">#907292</color>
-    <color name="system_palette_key_color_neutral_dark">#838387</color>
-    <color name="system_palette_key_color_neutral_variant_dark">#777982</color>
+    <color name="system_error_container_light">#DA342E</color>
+    <color name="system_on_error_container_light">#FFFFFF</color>
+    <color name="system_control_activated_light">#D9E2FF</color>
+    <color name="system_control_normal_light">#44464F</color>
+    <color name="system_control_highlight_light">#000000</color>
+    <color name="system_text_primary_inverse_light">#E2E2E9</color>
+    <color name="system_text_secondary_and_tertiary_inverse_light">#C5C6D0</color>
+    <color name="system_text_primary_inverse_disable_only_light">#E2E2E9</color>
+    <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E2E2E9</color>
+    <color name="system_text_hint_inverse_light">#E2E2E9</color>
+    <color name="system_palette_key_color_primary_light">#6076AC</color>
+    <color name="system_palette_key_color_secondary_light">#70778B</color>
+    <color name="system_palette_key_color_tertiary_light">#8C6D8C</color>
+    <color name="system_palette_key_color_neutral_light">#76777D</color>
+    <color name="system_palette_key_color_neutral_variant_light">#757780</color>
+    <color name="system_primary_container_dark">#7A90C8</color>
+    <color name="system_on_primary_container_dark">#000000</color>
+    <color name="system_primary_dark">#B7CAFF</color>
+    <color name="system_on_primary_dark">#00143B</color>
+    <color name="system_secondary_container_dark">#8A90A5</color>
+    <color name="system_on_secondary_container_dark">#000000</color>
+    <color name="system_secondary_dark">#C4CAE1</color>
+    <color name="system_on_secondary_dark">#0F1626</color>
+    <color name="system_tertiary_container_dark">#A886A6</color>
+    <color name="system_on_tertiary_container_dark">#000000</color>
+    <color name="system_tertiary_dark">#E4BFE2</color>
+    <color name="system_on_tertiary_dark">#240D26</color>
+    <color name="system_background_dark">#121318</color>
+    <color name="system_on_background_dark">#E2E2E9</color>
+    <color name="system_surface_dark">#121318</color>
+    <color name="system_on_surface_dark">#FCFAFF</color>
+    <color name="system_surface_container_low_dark">#1A1B20</color>
+    <color name="system_surface_container_lowest_dark">#0C0E13</color>
+    <color name="system_surface_container_dark">#1E1F25</color>
+    <color name="system_surface_container_high_dark">#282A2F</color>
+    <color name="system_surface_container_highest_dark">#33343A</color>
+    <color name="system_surface_bright_dark">#38393F</color>
+    <color name="system_surface_dim_dark">#121318</color>
+    <color name="system_surface_variant_dark">#44464F</color>
+    <color name="system_on_surface_variant_dark">#C9CAD4</color>
+    <color name="system_outline_dark">#A1A2AC</color>
+    <color name="system_outline_variant_dark">#81838C</color>
+    <color name="system_error_dark">#FFBAB1</color>
+    <color name="system_on_error_dark">#370001</color>
+    <color name="system_error_container_dark">#FF5449</color>
+    <color name="system_on_error_container_dark">#000000</color>
+    <color name="system_control_activated_dark">#2F4578</color>
+    <color name="system_control_normal_dark">#C5C6D0</color>
+    <color name="system_control_highlight_dark">#FFFFFF</color>
+    <color name="system_text_primary_inverse_dark">#1A1B20</color>
+    <color name="system_text_secondary_and_tertiary_inverse_dark">#44464F</color>
+    <color name="system_text_primary_inverse_disable_only_dark">#1A1B20</color>
+    <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1A1B20</color>
+    <color name="system_text_hint_inverse_dark">#1A1B20</color>
+    <color name="system_palette_key_color_primary_dark">#6076AC</color>
+    <color name="system_palette_key_color_secondary_dark">#70778B</color>
+    <color name="system_palette_key_color_tertiary_dark">#8C6D8C</color>
+    <color name="system_palette_key_color_neutral_dark">#76777D</color>
+    <color name="system_palette_key_color_neutral_variant_dark">#757780</color>
+    <color name="system_primary_fixed">#5E73A9</color>
+    <color name="system_primary_fixed_dim">#455B8F</color>
+    <color name="system_on_primary_fixed">#FFFFFF</color>
+    <color name="system_on_primary_fixed_variant">#FFFFFF</color>
+    <color name="system_secondary_fixed">#6E7488</color>
+    <color name="system_secondary_fixed_dim">#555C6F</color>
+    <color name="system_on_secondary_fixed">#FFFFFF</color>
+    <color name="system_on_secondary_fixed_variant">#FFFFFF</color>
+    <color name="system_tertiary_fixed">#8A6A89</color>
+    <color name="system_tertiary_fixed_dim">#705270</color>
+    <color name="system_on_tertiary_fixed">#FFFFFF</color>
+    <color name="system_on_tertiary_fixed_variant">#FFFFFF</color>
 
     <!-- Accessibility shortcut icon background color -->
     <color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f59c099..e3f1cb6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3586,14 +3586,6 @@
 
     <!-- The minimum number of visible recent tasks to be presented to the user through the
          SystemUI. Can be -1 if there is no minimum limit. -->
-    <integer name="config_minNumVisibleRecentTasks_grid">-1</integer>
-
-    <!-- The maximum number of visible recent tasks to be presented to the user through the
-         SystemUI. Can be -1 if there is no maximum limit. -->
-    <integer name="config_maxNumVisibleRecentTasks_grid">9</integer>
-
-    <!-- The minimum number of visible recent tasks to be presented to the user through the
-         SystemUI. Can be -1 if there is no minimum limit. -->
     <integer name="config_minNumVisibleRecentTasks_lowRam">-1</integer>
 
     <!-- The maximum number of visible recent tasks to be presented to the user through the
@@ -6985,4 +6977,8 @@
 
     <!-- The key containing the branching boolean for legacy Search. -->
     <string name="config_defaultContextualSearchLegacyEnabled" translatable="false" />
+
+    <!-- Whether WM DisplayContent supports high performance transitions
+         (lower-end devices may want to disable) -->
+    <bool name="config_deviceSupportsHighPerfTransitions">true</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c603fa7..9628d30 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -392,8 +392,6 @@
   <java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
-  <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
-  <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_grid" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks" />
   <java-symbol type="integer" name="config_maxNumVisibleRecentTasks" />
   <java-symbol type="integer" name="config_activeTaskDurationHours" />
@@ -5376,4 +5374,7 @@
   <java-symbol type="string" name="config_defaultContextualSearchLegacyEnabled" />
 
   <java-symbol type="string" name="unarchival_session_app_label" />
+
+  <!-- Whether WM DisplayContent supports high performance transitions -->
+  <java-symbol type="bool" name="config_deviceSupportsHighPerfTransitions" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 9a41fe0..5cc1ee4 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -1249,31 +1249,25 @@
     }
 
     @Test
-    @Ignore // TODO: b/329402256 - Restore or delete
-    public void testBigPictureStyle_setExtras_pictureIconNull_pictureIconKeyNull() {
+    public void testBigPictureStyle_setExtras_pictureIconNull_noPictureIconKey() {
         Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
         bpStyle.bigPicture((Bitmap) null);
 
         Bundle extras = new Bundle();
         bpStyle.addExtras(extras);
 
-        assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
-        final Parcelable pictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
-        assertThat(pictureIcon).isNull();
+        assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
     }
 
     @Test
-    @Ignore // TODO: b/329402256 - Restore or delete
-    public void testBigPictureStyle_setExtras_pictureIconNull_pictureKeyNull() {
+    public void testBigPictureStyle_setExtras_pictureIconNull_noPictureKey() {
         Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
         bpStyle.bigPicture((Bitmap) null);
 
         Bundle extras = new Bundle();
         bpStyle.addExtras(extras);
 
-        assertThat(extras.containsKey(EXTRA_PICTURE)).isTrue();
-        final Parcelable picture = extras.getParcelable(EXTRA_PICTURE);
-        assertThat(picture).isNull();
+        assertThat(extras.containsKey(EXTRA_PICTURE)).isFalse();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 2905a5a..e118c98d 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -31,7 +31,6 @@
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.test.AndroidTestCase;
 import android.util.Log;
-import android.util.Printer;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -47,15 +46,12 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Vector;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Phaser;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -407,138 +403,4 @@
         }
         assertFalse(allowed);
     }
-
-    /** Dumpsys information about a single database. */
-
-    /**
-     * Collect and parse dumpsys output.  This is not a full parser.  It is only enough to support
-     * the unit tests.
-     */
-    private static class Dumpsys {
-        // Regular expressions for parsing the output.  Reportedly, regular expressions are
-        // expensive, so these are created only if a dumpsys object is created.
-        private static final Object sLock = new Object();
-        static Pattern mPool;
-        static Pattern mConnection;
-        static Pattern mEntry;
-        static Pattern mSingleWord;
-        static Pattern mNone;
-
-        // The raw strings read from dumpsys.  Once loaded, this list never changes.
-        final ArrayList<String> mRaw = new ArrayList<>();
-
-        // Parsed dumpsys.  This contains only the bits that are being tested.
-        static class Connection {
-            ArrayList<String> mRecent = new ArrayList<>();
-            ArrayList<String> mLong = new ArrayList<>();
-        }
-        static class Database {
-            String mPath;
-            ArrayList<Connection> mConnection = new ArrayList<>();
-        }
-        ArrayList<Database> mDatabase;
-        ArrayList<String> mConcurrent;
-
-        Dumpsys() {
-            SQLiteDebug.dump(
-                new Printer() { public void println(String x) { mRaw.add(x); } },
-                new String[0]);
-            parse();
-        }
-
-        /** Parse the raw text. Return true if no errors were detected. */
-        boolean parse() {
-            initialize();
-
-            // Reset the parsed information.  This method may be called repeatedly.
-            mDatabase = new ArrayList<>();
-            mConcurrent = new ArrayList<>();
-
-            Database current = null;
-            Connection connection = null;
-            Matcher matcher;
-            for (int i = 0; i < mRaw.size(); i++) {
-                final String line = mRaw.get(i);
-                matcher = mPool.matcher(line);
-                if (matcher.lookingAt()) {
-                    current = new Database();
-                    mDatabase.add(current);
-                    current.mPath = matcher.group(1);
-                    continue;
-                }
-                matcher = mConnection.matcher(line);
-                if (matcher.lookingAt()) {
-                    connection = new Connection();
-                    current.mConnection.add(connection);
-                    continue;
-                }
-
-                if (line.contains("Most recently executed operations")) {
-                    i += readTable(connection.mRecent, i, mEntry);
-                    continue;
-                }
-
-                if (line.contains("Operations exceeding 2000ms")) {
-                    i += readTable(connection.mLong, i, mEntry);
-                    continue;
-                }
-                if (line.contains("Concurrently opened database files")) {
-                    i += readTable(mConcurrent, i, mSingleWord);
-                    continue;
-                }
-            }
-            return true;
-        }
-
-        /**
-         * Read a series of lines following a header.  Return the number of lines read.  The input
-         * line number is the number of the header.
-         */
-        private int readTable(List<String> s, int header, Pattern p) {
-            // Special case: if the first line is "<none>" then there are no more lines to the
-            // table.
-            if (lookingAt(header+1, mNone)) return 1;
-
-            int i;
-            for (i = header + 1; i < mRaw.size() && lookingAt(i, p); i++) {
-                s.add(mRaw.get(i).trim());
-            }
-            return i - header;
-        }
-
-        /** Return true if the n'th raw line matches the pattern. */
-        boolean lookingAt(int n, Pattern p) {
-            return p.matcher(mRaw.get(n)).lookingAt();
-        }
-
-        /** Compile the regular expressions the first time. */
-        private static void initialize() {
-            synchronized (sLock) {
-                if (mPool != null) return;
-                mPool = Pattern.compile("Connection pool for (\\S+):");
-                mConnection = Pattern.compile("\\s+Connection #(\\d+):");
-                mEntry = Pattern.compile("\\s+(\\d+): ");
-                mSingleWord = Pattern.compile("  (\\S+)$");
-                mNone = Pattern.compile("\\s+<none>$");
-            }
-        }
-    }
-
-    @Test
-    public void testDumpsys() throws Exception {
-        Dumpsys dumpsys = new Dumpsys();
-
-        assertEquals(1, dumpsys.mDatabase.size());
-        // Note: cannot test mConcurrent because that attribute is not hermitic with respect to
-        // the tests.
-
-        Dumpsys.Database db = dumpsys.mDatabase.get(0);
-
-        // Work with normalized paths.
-        String wantPath = mDatabaseFile.toPath().toRealPath().toString();
-        String realPath = new File(db.mPath).toPath().toRealPath().toString();
-        assertEquals(wantPath, realPath);
-
-        assertEquals(1, db.mConnection.size());
-    }
 }
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 48ba526..47b28899 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.AdditionalMatchers.not;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
@@ -120,6 +121,10 @@
             final var statsToken = ImeTracker.Token.empty();
             mImeConsumer.onWindowFocusGained(true);
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
+            // Called once through the show flow.
+            verify(mController).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
+                    eq(statsToken));
 
             // set control and verify visibility is applied.
             InsetsSourceControl control = new InsetsSourceControl(ID_IME,
@@ -127,11 +132,55 @@
             mController.onControlsChanged(new InsetsSourceControl[] { control });
             // IME show animation should be triggered when control becomes available.
             verify(mController).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+                    and(not(eq(statsToken)), notNull()));
+            verify(mController, never()).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
+                    and(not(eq(statsToken)), notNull()));
+        });
+    }
+
+    @Test
+    public void testImeRequestedVisibleAwaitingLeash() {
+        // Set null control, then request show.
+        mController.onControlsChanged(new InsetsSourceControl[] { null });
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // Request IME visible before control is available.
+            final var statsToken = ImeTracker.Token.empty();
+            mImeConsumer.onWindowFocusGained(true);
+            mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
+            // Called once through the show flow.
+            verify(mController).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
                     eq(statsToken));
+            // Clear previous invocations to verify this is never called with control without leash.
+            clearInvocations(mController);
+
+            // set control without leash and verify visibility is not applied.
+            InsetsSourceControl control = new InsetsSourceControl(ID_IME,
+                    WindowInsets.Type.ime(), null /* leash */, false, new Point(), Insets.NONE);
+            mController.onControlsChanged(new InsetsSourceControl[] { control });
+            // IME show animation should not be triggered when control becomes available,
+            // as we have no leash.
             verify(mController, never()).applyAnimation(
-                    eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */,
-                    eq(statsToken));
+                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+                    and(not(eq(statsToken)), notNull()));
+            verify(mController, never()).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
+                    and(not(eq(statsToken)), notNull()));
+
+            // set control with leash and verify visibility is applied.
+            InsetsSourceControl controlWithLeash = new InsetsSourceControl(ID_IME,
+                    WindowInsets.Type.ime(), mLeash, false, new Point(), Insets.NONE);
+            mController.onControlsChanged(new InsetsSourceControl[] { controlWithLeash });
+            // IME show animation should be triggered when control with leash becomes available.
+            verify(mController).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+                    and(not(eq(statsToken)), notNull()));
+            verify(mController, never()).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
+                    and(not(eq(statsToken)), notNull()));
         });
     }
 
@@ -159,6 +208,10 @@
             final var statsToken = ImeTracker.Token.empty();
             if (imeVisible) {
                 mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
+                // Called once through the show flow.
+                verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+                        eq(true) /* show */, eq(true) /* fromIme */,
+                        eq(false) /* skipAnim */, eq(statsToken));
             }
 
             // set control and verify visibility is applied.
@@ -184,13 +237,17 @@
             if (!hasViewFocus) {
                 final var statsTokenNext = ImeTracker.Token.empty();
                 mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsTokenNext);
+                // Called once through the show flow.
+                verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+                        eq(true) /* show */, eq(true) /* fromIme */,
+                        eq(false) /* skipAnim */, eq(statsTokenNext));
                 mController.onControlsChanged(new InsetsSourceControl[]{ control });
                 // Verify IME show animation should be triggered when control becomes available and
                 // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
                 verify(control).getAndClearSkipAnimationOnce();
                 verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
-                        eq(true) /* show */, eq(true) /* fromIme */,
-                        eq(false) /* skipAnim */, eq(statsTokenNext));
+                        eq(true) /* show */, eq(false) /* fromIme */,
+                        eq(true) /* skipAnim */, and(not(eq(statsToken)), notNull()));
             }
         });
     }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 749f0e1..9c1c7006 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -454,7 +454,7 @@
         <!-- Permissions required for CTS test - android.server.biometrics -->
         <permission name="android.permission.USE_BIOMETRIC" />
         <permission name="android.permission.TEST_BIOMETRIC" />
-        <permission name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
+        <permission name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
         <permission name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
         <!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
         <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index fa35b63..98935e9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -19,13 +19,14 @@
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
 
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
-import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE;
 import static androidx.window.common.CommonFoldingFeature.parseListFromString;
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.hardware.devicestate.DeviceStateUtil;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -54,29 +55,27 @@
     private static final boolean DEBUG = false;
 
     /**
-     * Emulated device state {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} to
+     * Emulated device state
+     * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} to
      * {@link CommonFoldingFeature.State} map.
      */
     private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
 
     /**
-     * Emulated device state received via
-     * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
-     * "Emulated" states differ from "base" state in the sense that they may not correspond 1:1 with
-     * physical device states. They represent the state of the device when various software
-     * features and APIs are applied. The emulated states generally consist of all "base" states,
-     * but may have additional states such as "concurrent" or "rear display". Concurrent mode for
-     * example is activated via public API and can be active in both the "open" and "half folded"
-     * device states.
+     * Device state received via
+     * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
+     * The identifier returned through {@link DeviceState#getIdentifier()} may not correspond 1:1
+     * with the physical state of the device. This could correspond to the system state of the
+     * device when various software features or overrides are applied. The emulated states generally
+     * consist of all "base" states, but may have additional states such as "concurrent" or
+     * "rear display". Concurrent mode for example is activated via public API and can be active in
+     * both the "open" and "half folded" device states.
      */
-    private int mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER;
+    private DeviceState mCurrentDeviceState = new DeviceState(
+            new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
+                    "INVALID").build());
 
-    /**
-     * Base device state received via
-     * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)}.
-     * "Base" in this context means the "physical" state of the device.
-     */
-    private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE_IDENTIFIER;
+    private List<DeviceState> mSupportedStates;
 
     @NonNull
     private final RawFoldingFeatureProducer mRawFoldSupplier;
@@ -85,22 +84,11 @@
 
     private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
         @Override
-        public void onStateChanged(int state) {
+        public void onDeviceStateChanged(@NonNull DeviceState state) {
             mCurrentDeviceState = state;
             mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
                     .this::notifyFoldingFeatureChange);
         }
-
-        @Override
-        public void onBaseStateChanged(int state) {
-            mCurrentBaseDeviceState = state;
-
-            if (mDeviceStateToPostureMap.get(mCurrentDeviceState)
-                    == COMMON_STATE_USE_BASE_STATE) {
-                mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
-                        .this::notifyFoldingFeatureChange);
-            }
-        }
     };
 
     public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
@@ -109,6 +97,7 @@
         mRawFoldSupplier = rawFoldSupplier;
         String[] deviceStatePosturePairs = context.getResources()
                 .getStringArray(R.array.config_device_state_postures);
+        mSupportedStates = deviceStateManager.getSupportedDeviceStates();
         boolean isHalfOpenedSupported = false;
         for (String deviceStatePosturePair : deviceStatePosturePairs) {
             String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
@@ -168,7 +157,7 @@
      */
     private boolean isCurrentStateValid() {
         // If the device state is not found in the map, indexOfKey returns a negative number.
-        return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState) >= 0;
+        return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState.getIdentifier()) >= 0;
     }
 
     @Override
@@ -177,7 +166,9 @@
         if (hasListeners()) {
             mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange);
         } else {
-            mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER;
+            mCurrentDeviceState = new DeviceState(
+                    new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
+                            "INVALID").build());
             mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChange);
         }
     }
@@ -251,10 +242,13 @@
     @CommonFoldingFeature.State
     private int currentHingeState() {
         @CommonFoldingFeature.State
-        int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+        int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState.getIdentifier(),
+                COMMON_STATE_UNKNOWN);
 
         if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
-            posture = mDeviceStateToPostureMap.get(mCurrentBaseDeviceState, COMMON_STATE_UNKNOWN);
+            posture = mDeviceStateToPostureMap.get(
+                    DeviceStateUtil.calculateBaseStateIdentifier(mCurrentDeviceState,
+                            mSupportedStates), COMMON_STATE_UNKNOWN);
         }
 
         return posture;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index d31bf2a..a3d2d7f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -20,6 +20,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateRequest;
 import android.hardware.display.DisplayManager;
@@ -40,6 +41,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -101,7 +103,9 @@
         mDisplayManager = context.getSystemService(DisplayManager.class);
         mExecutor = context.getMainExecutor();
 
-        mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedStates();
+        // TODO(b/329436166): Update the usage of device state manager API's
+        mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(
+                mDeviceStateManager.getSupportedDeviceStates());
         mFoldedDeviceStates = context.getResources().getIntArray(
                 R.array.config_foldedDeviceStates);
 
@@ -446,9 +450,10 @@
     }
 
     @Override
-    public void onSupportedStatesChanged(int[] supportedStates) {
+    public void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {
         synchronized (mLock) {
-            mCurrentSupportedDeviceStates = supportedStates;
+            // TODO(b/329436166): Update the usage of device state manager API's
+            mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(supportedStates);
             updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
             updateRearDisplayPresentationStatusListeners(
                     getCurrentRearDisplayPresentationModeStatus());
@@ -456,9 +461,10 @@
     }
 
     @Override
-    public void onStateChanged(int state) {
+    public void onDeviceStateChanged(@NonNull DeviceState state) {
         synchronized (mLock) {
-            mCurrentDeviceState = state;
+            // TODO(b/329436166): Update the usage of device state manager API's
+            mCurrentDeviceState = state.getIdentifier();
             updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
             updateRearDisplayPresentationStatusListeners(
                     getCurrentRearDisplayPresentationModeStatus());
@@ -482,6 +488,15 @@
         return WindowAreaComponent.STATUS_AVAILABLE;
     }
 
+    // TODO(b/329436166): Remove and update the usage of device state manager API's
+    private int[] getSupportedStateIdentifiers(@NonNull List<DeviceState> states) {
+        int[] identifiers = new int[states.size()];
+        for (int i = 0; i < states.size(); i++) {
+            identifiers[i] = states.get(i).getIdentifier();
+        }
+        return identifiers;
+    }
+
     /**
      * Helper method to determine if a rear display session is currently active by checking
      * if the current device state is that which corresponds to {@code mRearDisplayState}.
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 8989fc5..f3d70f7 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -18,27 +18,32 @@
 
 import android.content.Context
 import android.content.Intent
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
 import android.graphics.Color
 import android.graphics.drawable.Icon
 import android.os.UserHandle
 import android.view.IWindowManager
 import android.view.WindowManager
 import android.view.WindowManagerGlobal
-import androidx.test.annotation.UiThreadTest
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.protolog.common.ProtoLog
 import com.android.launcher3.icons.BubbleIconFactory
 import com.android.wm.shell.R
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
 import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
 import com.android.wm.shell.common.FloatingContentCoordinator
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.taskview.TaskView
 import com.android.wm.shell.taskview.TaskViewTaskController
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.After
 import java.util.concurrent.Semaphore
 import java.util.concurrent.TimeUnit
 import java.util.function.Consumer
@@ -64,6 +69,7 @@
 
     @Before
     fun setUp() {
+        PhysicsAnimatorTestUtils.prepareForTest()
         // Disable protolog tool when running the tests from studio
         ProtoLog.REQUIRE_PROTOLOGTOOL = false
         windowManager = WindowManagerGlobal.getWindowManagerService()!!
@@ -104,34 +110,158 @@
                 { sysuiProxy },
                 shellExecutor
             )
+
+        context
+            .getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+            .edit()
+            .putBoolean(StackEducationView.PREF_STACK_EDUCATION, true)
+            .apply()
     }
 
-    @UiThreadTest
+    @After
+    fun tearDown() {
+        PhysicsAnimatorTestUtils.tearDown()
+    }
+
     @Test
     fun addBubble() {
         val bubble = createAndInflateBubble()
-        bubbleStackView.addBubble(bubble)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble)
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
         assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
     }
 
-    @UiThreadTest
     @Test
     fun tapBubbleToExpand() {
         val bubble = createAndInflateBubble()
-        bubbleStackView.addBubble(bubble)
-        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
 
-        bubble.iconView!!.performClick()
-        // we're checking the expanded state in BubbleData because that's the source of truth. This
-        // will eventually propagate an update back to the stack view, but setting the entire
-        // pipeline is outside the scope of a unit test.
-        assertThat(bubbleData.isExpanded).isTrue()
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble)
+        }
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+        var lastUpdate: BubbleData.Update? = null
+        val semaphore = Semaphore(0)
+        val listener =
+            BubbleData.Listener { update ->
+                lastUpdate = update
+                semaphore.release()
+            }
+        bubbleData.setListener(listener)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubble.iconView!!.performClick()
+            // we're checking the expanded state in BubbleData because that's the source of truth.
+            // This will eventually propagate an update back to the stack view, but setting the
+            // entire pipeline is outside the scope of a unit test.
+            assertThat(bubbleData.isExpanded).isTrue()
+        }
+
+        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        assertThat(lastUpdate).isNotNull()
+        assertThat(lastUpdate!!.expandedChanged).isTrue()
+        assertThat(lastUpdate!!.expanded).isTrue()
+    }
+
+    @Test
+    fun tapDifferentBubble_shouldReorder() {
+        val bubble1 = createAndInflateChatBubble(key = "bubble1")
+        val bubble2 = createAndInflateChatBubble(key = "bubble2")
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble1)
+            bubbleStackView.addBubble(bubble2)
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(2)
+        assertThat(bubbleData.bubbles).hasSize(2)
+        assertThat(bubbleData.selectedBubble).isEqualTo(bubble2)
+        assertThat(bubble2.iconView).isNotNull()
+
+        var lastUpdate: BubbleData.Update? = null
+        val semaphore = Semaphore(0)
+        val listener =
+            BubbleData.Listener { update ->
+                lastUpdate = update
+                semaphore.release()
+            }
+        bubbleData.setListener(listener)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubble2.iconView!!.performClick()
+            assertThat(bubbleData.isExpanded).isTrue()
+
+            bubbleStackView.setSelectedBubble(bubble2)
+            bubbleStackView.isExpanded = true
+        }
+
+        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        assertThat(lastUpdate!!.expanded).isTrue()
+        assertThat(lastUpdate!!.bubbles.map { it.key })
+            .containsExactly("bubble2", "bubble1")
+            .inOrder()
+
+        // wait for idle to allow the animation to start
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        // wait for the expansion animation to complete before interacting with the bubbles
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+                AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
+
+        // tap on bubble1 to select it
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubble1.iconView!!.performClick()
+        }
+        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
+
+        // tap on bubble1 again to collapse the stack
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // we have to set the selected bubble in the stack view manually because we don't have a
+            // listener wired up.
+            bubbleStackView.setSelectedBubble(bubble1)
+            bubble1.iconView!!.performClick()
+        }
+
+        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
+        assertThat(bubbleData.isExpanded).isFalse()
+        assertThat(lastUpdate!!.orderChanged).isTrue()
+        assertThat(lastUpdate!!.bubbles.map { it.key })
+            .containsExactly("bubble1", "bubble2")
+            .inOrder()
+    }
+
+    private fun createAndInflateChatBubble(key: String): Bubble {
+        val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
+        val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
+        val bubble =
+            Bubble(
+                key,
+                shortcutInfo,
+                /* desiredHeight= */ 6,
+                Resources.ID_NULL,
+                "title",
+                /* taskId= */ 0,
+                "locus",
+                /* isDismissable= */ true,
+                directExecutor()
+            ) {}
+        inflateBubble(bubble)
+        return bubble
     }
 
     private fun createAndInflateBubble(): Bubble {
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
         val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
         val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
+        inflateBubble(bubble)
+        return bubble
+    }
+
+    private fun inflateBubble(bubble: Bubble) {
         bubble.setInflateSynchronously(true)
         bubbleData.notificationEntryUpdated(bubble, true, false)
 
@@ -152,7 +282,6 @@
 
         assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
         assertThat(bubble.isInflated).isTrue()
-        return bubble
     }
 
     private class FakeBubbleStackViewManager : BubbleStackViewManager {
@@ -176,7 +305,7 @@
             r.run()
         }
 
-        override fun removeCallbacks(r: Runnable) {}
+        override fun removeCallbacks(r: Runnable?) {}
 
         override fun hasCallback(r: Runnable): Boolean = false
     }
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7dd3961..00fb298 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -272,6 +272,10 @@
     <dimen name="bubble_bar_expanded_view_corner_radius">16dp</dimen>
     <!-- Corner radius for expanded view while it is being dragged -->
     <dimen name="bubble_bar_expanded_view_corner_radius_dragged">28dp</dimen>
+    <!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
+    <dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
+    <!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
+    <dimen name="bubble_bar_dismiss_zone_height">242dp</dimen>
 
     <!-- Bottom and end margin for compat buttons. -->
     <dimen name="compat_button_margin">24dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index ee8c414..b7f0890 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -505,7 +505,6 @@
             // Check for a spring configuration. If one is present, we're either springing, or
             // flinging-then-springing.
             if (springConfig != null) {
-
                 // If there is no corresponding fling config, we're only springing.
                 if (flingConfig == null) {
                     // Apply the configuration and start the animation.
@@ -679,7 +678,6 @@
             value: Float,
             velocity: Float
         ) {
-
             // If this property animation isn't relevant to this listener, ignore it.
             if (!properties.contains(property)) {
                 return
@@ -702,7 +700,6 @@
             finalVelocity: Float,
             isFling: Boolean
         ): Boolean {
-
             // If this property animation isn't relevant to this listener, ignore it.
             if (!properties.contains(property)) {
                 return false
@@ -971,17 +968,18 @@
     companion object {
 
         /**
-         * Constructor to use to for new physics animator instances in [getInstance]. This is
-         * typically the default constructor, but [PhysicsAnimatorTestUtils] can change it so that
-         * all code using the physics animator is given testable instances instead.
+         * Callback to notify that a new animator was created. Used in [PhysicsAnimatorTestUtils]
+         * to be able to keep track of animators and wait for them to finish.
          */
-        internal var instanceConstructor: (Any) -> PhysicsAnimator<*> = ::PhysicsAnimator
+        internal var onAnimatorCreated: (PhysicsAnimator<*>, Any) -> Unit = { _, _ -> }
 
         @JvmStatic
         @Suppress("UNCHECKED_CAST")
         fun <T : Any> getInstance(target: T): PhysicsAnimator<T> {
             if (!animators.containsKey(target)) {
-                animators[target] = instanceConstructor(target)
+                val animator = PhysicsAnimator(target)
+                onAnimatorCreated(animator, target)
+                animators[target] = animator
             }
 
             return animators[target] as PhysicsAnimator<T>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt
index 86eb8da..7defc26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt
@@ -62,12 +62,9 @@
      */
     @JvmStatic
     fun prepareForTest() {
-        val defaultConstructor = PhysicsAnimator.instanceConstructor
-        PhysicsAnimator.instanceConstructor = fun(target: Any): PhysicsAnimator<*> {
-            val animator = defaultConstructor(target)
+        PhysicsAnimator.onAnimatorCreated = { animator, target ->
             allAnimatedObjects.add(target)
             animatorTestHelpers[animator] = AnimatorTestHelper(animator)
-            return animator
         }
 
         timeoutMs = 2000
@@ -158,12 +155,12 @@
     @Throws(InterruptedException::class)
     @Suppress("UNCHECKED_CAST")
     fun <T : Any> blockUntilAnimationsEnd(
-        properties: FloatPropertyCompat<in T>
+        vararg properties: FloatPropertyCompat<in T>
     ) {
         for (target in allAnimatedObjects) {
             try {
                 blockUntilAnimationsEnd(
-                        PhysicsAnimator.getInstance(target) as PhysicsAnimator<T>, properties)
+                        PhysicsAnimator.getInstance(target) as PhysicsAnimator<T>, *properties)
             } catch (e: ClassCastException) {
                 // Keep checking the other objects for ones whose types match the provided
                 // properties.
@@ -267,10 +264,8 @@
 
         // Loop through the updates from the testable animator.
         for (update in framesForProperty) {
-
             // Check whether this frame satisfies the current matcher.
             if (curMatcher(update)) {
-
                 // If that was the last unsatisfied matcher, we're good here. 'Verify' all remaining
                 // frames and return without failing.
                 if (matchers.size == 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 6a5f785..42de401 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -24,6 +24,7 @@
 
 import android.animation.ArgbEvaluator;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -74,7 +75,7 @@
 
     private final int mFlyoutElevation;
     private final int mBubbleElevation;
-    private final int mFloatingBackgroundColor;
+    private int mFloatingBackgroundColor;
     private final float mCornerRadius;
 
     private final ViewGroup mFlyoutTextContainer;
@@ -107,6 +108,9 @@
     /** Color of the 'new' dot that the flyout will transform into. */
     private int mDotColor;
 
+    /** Keeps last used night mode flags **/
+    private int mNightModeFlags;
+
     /** The outline of the triangle, used for elevation shadows. */
     private final Outline mTriangleOutline = new Outline();
 
@@ -176,11 +180,8 @@
         mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
 
         final TypedArray ta = mContext.obtainStyledAttributes(
-                new int[] {
-                        com.android.internal.R.attr.materialColorSurfaceContainer,
-                        android.R.attr.dialogCornerRadius});
-        mFloatingBackgroundColor = ta.getColor(0, Color.WHITE);
-        mCornerRadius = ta.getDimensionPixelSize(1, 0);
+                new int[] {android.R.attr.dialogCornerRadius});
+        mCornerRadius = ta.getDimensionPixelSize(0, 0);
         ta.recycle();
 
         // Add padding for the pointer on either side, onDraw will draw it in this space.
@@ -198,19 +199,17 @@
         // Use locale direction so the text is aligned correctly.
         setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
 
-        mBgPaint.setColor(mFloatingBackgroundColor);
-
         mLeftTriangleShape =
                 new ShapeDrawable(TriangleShape.createHorizontal(
                         mPointerSize, mPointerSize, true /* isPointingLeft */));
         mLeftTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
-        mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
 
         mRightTriangleShape =
                 new ShapeDrawable(TriangleShape.createHorizontal(
                         mPointerSize, mPointerSize, false /* isPointingLeft */));
         mRightTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
-        mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+
+        applyConfigurationColors(getResources().getConfiguration());
     }
 
     @Override
@@ -244,6 +243,13 @@
         fade(false /* in */, stackPos, hideDot, afterFadeOut);
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        if (applyColorsAccordingToConfiguration(newConfig)) {
+            invalidate();
+        }
+    }
+
     /*
      * Fade-out above or fade-in from below.
      */
@@ -424,6 +430,42 @@
     }
 
     /**
+     * Resolving and applying colors according to the ui mode, remembering most recent mode.
+     *
+     * @return {@code true} if night mode setting has changed since the last invocation,
+     * {@code false} otherwise
+     */
+    boolean applyColorsAccordingToConfiguration(Configuration configuration) {
+        int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        boolean flagsChanged = nightModeFlags != mNightModeFlags;
+        if (flagsChanged) {
+            mNightModeFlags = nightModeFlags;
+            applyConfigurationColors(configuration);
+        }
+        return flagsChanged;
+    }
+
+    private void applyConfigurationColors(Configuration configuration) {
+        int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        boolean isNightModeOn = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
+        try (TypedArray ta = mContext.obtainStyledAttributes(
+                new int[]{
+                        com.android.internal.R.attr.materialColorSurfaceContainer,
+                        com.android.internal.R.attr.materialColorOnSurface,
+                        com.android.internal.R.attr.materialColorOnSurfaceVariant})) {
+            mFloatingBackgroundColor = ta.getColor(0,
+                    isNightModeOn ? Color.BLACK : Color.WHITE);
+            mSenderText.setTextColor(ta.getColor(1,
+                    isNightModeOn ? Color.WHITE : Color.BLACK));
+            mMessageText.setTextColor(ta.getColor(2,
+                    isNightModeOn ? Color.WHITE : Color.BLACK));
+            mBgPaint.setColor(mFloatingBackgroundColor);
+            mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+            mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+        }
+    }
+
+    /**
      * Renders the background, which is either the rounded 'chat bubble' flyout, or some state
      * between that and the 'new' dot over the bubbles.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 056598b..b5b8a81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -17,8 +17,10 @@
 package com.android.wm.shell.bubbles.bar
 
 import android.annotation.SuppressLint
+import android.graphics.RectF
 import android.view.MotionEvent
 import android.view.View
+import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner
 import com.android.wm.shell.common.bubbles.BubbleBarLocation
 import com.android.wm.shell.common.bubbles.DismissView
@@ -43,6 +45,8 @@
     private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
         MagnetizedObject.magnetizeView(expandedView)
     private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
+    private val dismissZoneHeight: Int
+    private val dismissZoneWidth: Int
 
     init {
         magnetizedExpandedView.magnetListener = MagnetListener()
@@ -74,6 +78,11 @@
             }
             return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
         }
+
+        dismissZoneHeight =
+            dismissView.resources.getDimensionPixelSize(R.dimen.bubble_bar_dismiss_zone_height)
+        dismissZoneWidth =
+            dismissView.resources.getDimensionPixelSize(R.dimen.bubble_bar_dismiss_zone_width)
     }
 
     /** Listener to receive callback about dragging events */
@@ -97,12 +106,23 @@
         private var isMoving = false
         private var screenCenterX: Int = -1
         private var isOnLeft = false
+        private val dismissZone = RectF()
 
         override fun onDown(v: View, ev: MotionEvent): Boolean {
             // While animating, don't allow new touch events
             if (expandedView.isAnimating) return false
-            screenCenterX = bubblePositioner.screenRect.centerX()
             isOnLeft = bubblePositioner.isBubbleBarOnLeft
+
+            val screenRect = bubblePositioner.screenRect
+            screenCenterX = screenRect.centerX()
+            val screenBottom = screenRect.bottom
+
+            dismissZone.set(
+                screenCenterX - dismissZoneWidth / 2f,
+                (screenBottom - dismissZoneHeight).toFloat(),
+                screenCenterX + dismissZoneHeight / 2f,
+                screenBottom.toFloat()
+            )
             return true
         }
 
@@ -122,6 +142,11 @@
             expandedView.translationY = expandedViewInitialTranslationY + dy
             dismissView.show()
 
+            // Check if we are in the zone around dismiss view where drag can only lead to dismiss
+            if (dismissZone.contains(ev.rawX, ev.rawY)) {
+                return
+            }
+
             if (isOnLeft && ev.rawX > screenCenterX) {
                 isOnLeft = false
                 dragListener.onLocationChanged(BubbleBarLocation.RIGHT)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
index 8b4ac1a..d17e862 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
@@ -107,7 +107,7 @@
                 DeviceStateManager.class);
         if (deviceStateManager != null) {
             deviceStateManager.registerCallback(mMainExecutor, state -> onDevicePostureChanged(
-                    mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN)));
+                    mDeviceStateToPostureMap.get(state.getIdentifier(), DEVICE_POSTURE_UNKNOWN)));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index 8826141..31214eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -17,6 +17,8 @@
 
 import android.app.ActivityManager
 import android.os.RemoteException
+import android.os.Trace
+import android.os.Trace.TRACE_TAG_WINDOW_MANAGER
 import android.util.Log
 import android.view.DragEvent
 import android.view.IWindowManager
@@ -27,6 +29,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import java.util.function.Consumer
+import kotlin.random.Random
 
 /**
  * Manages the listener and callbacks for unhandled global drags.
@@ -101,10 +104,15 @@
 
     @VisibleForTesting
     fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) {
+        val traceCookie = Random.nextInt()
+        Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+            traceCookie);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
             "onUnhandledDrop: %s", dragEvent)
         if (callback == null) {
             wmCallback.notifyUnhandledDropComplete(false)
+            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+                traceCookie);
             return
         }
 
@@ -112,6 +120,8 @@
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
                 "Notifying onUnhandledDrop complete: %b", it)
             wmCallback.notifyUnhandledDropComplete(it)
+            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+                traceCookie);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index f7ffdd1..2b433e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,9 +64,7 @@
         default void onSplitVisibilityChanged(boolean visible) {}
     }
 
-    /**
-     * Callback interface for listening to requests to enter split select. Used for desktop -> split
-     */
+    /** Callback interface for listening to requests to enter split select */
     interface SplitSelectListener {
         default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
                 int splitPosition, Rect taskBounds) {
@@ -74,15 +72,6 @@
         }
     }
 
-    interface SplitInvocationListener {
-        /**
-         * Called whenever shell starts or stops the split screen animation
-         * @param animationRunning if {@code true} the animation has begun, if {@code false} the
-         *                         animation has finished
-         */
-        default void onSplitAnimationInvoked(boolean animationRunning) { }
-    }
-
     /** Registers listener that gets split screen callback. */
     void registerSplitScreenListener(@NonNull SplitScreenListener listener,
             @NonNull Executor executor);
@@ -90,21 +79,15 @@
     /** Unregisters listener that gets split screen callback. */
     void unregisterSplitScreenListener(@NonNull SplitScreenListener listener);
 
-    /**
-     * Registers a {@link SplitInvocationListener} to notify when the animation to enter split
-     * screen has started and stopped
-     *
-     * @param executor callbacks to the listener will be executed on this executor
-     */
-    void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
-            @NonNull Executor executor);
-
     /** Called when device waking up finished. */
     void onFinishedWakingUp();
 
     /** Called when requested to go to fullscreen from the current active split app. */
     void goToFullscreenFromSplit();
 
+    /** Called when splitscreen focused app is changed. */
+    void setSplitscreenFocus(boolean leftOrTop);
+
     /** Get a string representation of a stage type */
     static String stageTypeToString(@StageType int stage) {
         switch (stage) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 218abe2..3e34c30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -485,6 +485,12 @@
         }
     }
 
+    public void setSplitscreenFocus(boolean leftOrTop) {
+        if (mStageCoordinator.isSplitActive()) {
+            mStageCoordinator.grantFocusToPosition(leftOrTop);
+        }
+    }
+
     /** Move the specified task to fullscreen, regardless of focus state. */
     public void moveTaskToFullscreen(int taskId, int exitReason) {
         mStageCoordinator.moveTaskToFullscreen(taskId, exitReason);
@@ -1138,12 +1144,6 @@
         }
 
         @Override
-        public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
-                @NonNull Executor executor) {
-            mStageCoordinator.registerSplitAnimationListener(listener, executor);
-        }
-
-        @Override
         public void onFinishedWakingUp() {
             mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
         }
@@ -1152,6 +1152,12 @@
         public void goToFullscreenFromSplit() {
             mMainExecutor.execute(SplitScreenController.this::goToFullscreenFromSplit);
         }
+
+        @Override
+        public void setSplitscreenFocus(boolean leftOrTop) {
+            mMainExecutor.execute(
+                    () -> SplitScreenController.this.setSplitscreenFocus(leftOrTop));
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index e69ff70..1a53a1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -55,7 +55,6 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.ArrayList;
-import java.util.concurrent.Executor;
 
 /** Manages transition animations for split-screen. */
 class SplitScreenTransitions {
@@ -80,8 +79,6 @@
 
     private Transitions.TransitionFinishCallback mFinishCallback = null;
     private SurfaceControl.Transaction mFinishTransaction;
-    private SplitScreen.SplitInvocationListener mSplitInvocationListener;
-    private Executor mSplitInvocationListenerExecutor;
 
     SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
             @NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
@@ -356,10 +353,6 @@
                     + " skip to start enter split transition since it already exist. ");
             return null;
         }
-        if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) {
-            mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener
-                    .onSplitAnimationInvoked(true /*animationRunning*/));
-        }
         final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
         setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
         return transition;
@@ -536,12 +529,6 @@
         mTransitions.getAnimExecutor().execute(va::start);
     }
 
-    public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener,
-            @NonNull Executor executor) {
-        mSplitInvocationListener = listener;
-        mSplitInvocationListenerExecutor = executor;
-    }
-
     /** Calls when the transition got consumed. */
     interface TransitionConsumedCallback {
         void onConsumed(boolean aborted);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8ab6cf7..41890df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -156,7 +156,6 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.Executor;
 
 /**
  * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -237,9 +236,6 @@
     private DefaultMixedHandler mMixedHandler;
     private final Toast mSplitUnsupportedToast;
     private SplitRequest mSplitRequest;
-    /** Used to notify others of when shell is animating into split screen */
-    private SplitScreen.SplitInvocationListener mSplitInvocationListener;
-    private Executor mSplitInvocationListenerExecutor;
 
     /**
      * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
@@ -250,14 +246,6 @@
         return false;
     }
 
-    /** NOTE: Will overwrite any previously set {@link #mSplitInvocationListener} */
-    public void registerSplitAnimationListener(
-            @NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) {
-        mSplitInvocationListener = listener;
-        mSplitInvocationListenerExecutor = executor;
-        mSplitTransitions.registerSplitAnimListener(listener, executor);
-    }
-
     class SplitRequest {
         @SplitPosition
         int mActivatePosition;
@@ -541,7 +529,7 @@
                             null /* childrenToTop */, EXIT_REASON_UNKNOWN));
                     Log.w(TAG, splitFailureMessage("startShortcut",
                             "side stage was not populated"));
-                    handleUnsupportedSplitStart();
+                    mSplitUnsupportedToast.show();
                 }
 
                 if (finishedCallback != null) {
@@ -672,7 +660,7 @@
                             null /* childrenToTop */, EXIT_REASON_UNKNOWN));
                     Log.w(TAG, splitFailureMessage("startIntentLegacy",
                             "side stage was not populated"));
-                    handleUnsupportedSplitStart();
+                    mSplitUnsupportedToast.show();
                 }
 
                 if (apps != null) {
@@ -1225,7 +1213,7 @@
                             ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
             Log.w(TAG, splitFailureMessage("onRemoteAnimationFinishedOrCancelled",
                     "main or side stage was not populated."));
-            handleUnsupportedSplitStart();
+            mSplitUnsupportedToast.show();
         } else {
             mSyncQueue.queue(evictWct);
             mSyncQueue.runInSync(t -> {
@@ -1246,7 +1234,7 @@
                     ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
             Log.w(TAG, splitFailureMessage("onRemoteAnimationFinished",
                     "main or side stage was not populated"));
-            handleUnsupportedSplitStart();
+            mSplitUnsupportedToast.show();
             return;
         }
 
@@ -1604,6 +1592,11 @@
         }
     }
 
+    protected void grantFocusToPosition(boolean leftOrTop) {
+        grantFocusToStage(mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+                ? getMainStagePosition() : getSideStagePosition());
+    }
+
     private void clearRequestIfPresented() {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
         if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
@@ -2813,7 +2806,6 @@
             if (hasEnteringPip) {
                 mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
                         startTransaction, finishTransaction, finishCallback);
-                notifySplitAnimationFinished();
                 return true;
             }
 
@@ -2848,7 +2840,6 @@
                 //                    the transition, or synchronize task-org callbacks.
             }
             // Use normal animations.
-            notifySplitAnimationFinished();
             return false;
         } else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) {
             // A display-change has been un-expectedly inserted into the transition. Redirect
@@ -2862,7 +2853,6 @@
                     mSplitLayout.update(startTransaction, true /* resetImePosition */);
                     startTransaction.apply();
                 }
-                notifySplitAnimationFinished();
                 return true;
             }
         }
@@ -3036,7 +3026,7 @@
                     pendingEnter.mRemoteHandler.onTransitionConsumed(transition,
                             false /*aborted*/, finishT);
                 }
-                handleUnsupportedSplitStart();
+                mSplitUnsupportedToast.show();
                 return true;
             }
         }
@@ -3065,7 +3055,6 @@
         final TransitionInfo.Change finalMainChild = mainChild;
         final TransitionInfo.Change finalSideChild = sideChild;
         enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
-            notifySplitAnimationFinished();
             if (finalMainChild != null) {
                 if (!mainNotContainOpenTask) {
                     mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
@@ -3482,19 +3471,6 @@
                 mSplitLayout.isLeftRightSplit());
     }
 
-    private void handleUnsupportedSplitStart() {
-        mSplitUnsupportedToast.show();
-        notifySplitAnimationFinished();
-    }
-
-    private void notifySplitAnimationFinished() {
-        if (mSplitInvocationListener == null || mSplitInvocationListenerExecutor == null) {
-            return;
-        }
-        mSplitInvocationListenerExecutor.execute(() ->
-                mSplitInvocationListener.onSplitAnimationInvoked(false /*animationRunning*/));
-    }
-
     /**
      * Logs the exit of splitscreen to a specific stage. This must be called before the exit is
      * executed.
@@ -3557,7 +3533,7 @@
                 if (!ENABLE_SHELL_TRANSITIONS) {
                     StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage,
                             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
-                    handleUnsupportedSplitStart();
+                    mSplitUnsupportedToast.show();
                     return;
                 }
 
@@ -3577,7 +3553,7 @@
                         "app package " + taskInfo.baseActivity.getPackageName()
                         + " does not support splitscreen, or is a controlled activity type"));
                 if (splitScreenVisible) {
-                    handleUnsupportedSplitStart();
+                    mSplitUnsupportedToast.show();
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 34b2eeb..befc702 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -39,13 +39,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -66,7 +63,6 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -109,8 +105,6 @@
     @Mock private ShellExecutor mMainExecutor;
     @Mock private LaunchAdjacentController mLaunchAdjacentController;
     @Mock private DefaultMixedHandler mMixedHandler;
-    @Mock private SplitScreen.SplitInvocationListener mInvocationListener;
-    private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
     private SplitLayout mSplitLayout;
     private MainStage mMainStage;
     private SideStage mSideStage;
@@ -153,7 +147,6 @@
                 .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
         doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
         doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
-        mStageCoordinator.registerSplitAnimationListener(mInvocationListener, mTestShellExecutor);
     }
 
     @Test
@@ -459,15 +452,6 @@
         mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */);
     }
 
-    @Test
-    @UiThreadTest
-    public void testSplitInvocationCallback() {
-        enterSplit();
-        mTestShellExecutor.flushAll();
-        verify(mInvocationListener, times(1))
-                .onSplitAnimationInvoked(eq(true));
-    }
-
     private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
         for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
             WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f5dc6ea..1905fa8 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5101,7 +5101,7 @@
      * size is larger than 16x16, then the qpOffset information of all 16x16 blocks that
      * encompass the coding unit is combined and used. The QP of target block will be calculated
      * as 'frameQP + offsetQP'. If the result exceeds minQP or maxQP configured then the value
-     * may be clamped. Negative offset results in blocks encoded at lower QP than frame QP and
+     * will be clamped. Negative offset results in blocks encoded at lower QP than frame QP and
      * positive offsets will result in encoding blocks at higher QP than frame QP. If the areas
      * of negative QP and positive QP are chosen wisely, the overall viewing experience can be
      * improved.
@@ -5128,7 +5128,7 @@
      * quantization parameter (QP) offset of the blocks in the bounding box. The bounding box
      * will get stretched outwards to align to LCU boundaries during encoding. The Qp Offset is
      * integral and shall be in the range [-128, 127]. The QP of target block will be calculated
-     * as frameQP + offsetQP. If the result exceeds minQP or maxQP configured then the value may
+     * as frameQP + offsetQP. If the result exceeds minQP or maxQP configured then the value will
      * be clamped. Negative offset results in blocks encoded at lower QP than frame QP and
      * positive offsets will result in blocks encoded at higher QP than frame QP. If the areas of
      * negative QP and positive QP are chosen wisely, the overall viewing experience can be
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 1e7bc47..abad460 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2651,11 +2651,10 @@
             mBlockAspectRatioRange = POSITIVE_RATIONALS;
             mAspectRatioRange      = POSITIVE_RATIONALS;
 
-            // YUV 4:2:0 requires 2:2 alignment
-            mWidthAlignment = 2;
-            mHeightAlignment = 2;
-            mBlockWidth = 2;
-            mBlockHeight = 2;
+            mWidthAlignment = 1;
+            mHeightAlignment = 1;
+            mBlockWidth = 1;
+            mBlockHeight = 1;
             mSmallerDimensionUpperLimit = getSizeRange().getUpper();
         }
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a4bc235..8a5dfef 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1485,7 +1485,7 @@
     <string name="user_nickname">Nickname</string>
 
     <!-- Confirmation message on dialog for editing user name and profile picture. Inform user on who will be able to see the changes [CHAR LIMIT=NONE]-->
-    <string name="edit_user_info_message">Your name and picture will be visible to anyone that uses this device.</string>
+    <string name="edit_user_info_message">The name and picture you choose will be visible to anyone who uses this device.</string>
 
     <!-- Label for adding a new user in the user switcher [CHAR LIMIT=35] -->
     <string name="user_add_user">Add user</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 3266c12..b151a53 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -60,10 +60,12 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.file.Files;
@@ -161,6 +163,11 @@
     private static final String APEX_DIR = "/apex";
     private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
 
+    private static final String STORAGE_MIGRATION_FLAG =
+            "core_experiments_team_internal/com.android.providers.settings.storage_test_mission_1";
+    private static final String STORAGE_MIGRATION_LOG =
+            "/metadata/aconfig/flags/storage_migration.log";
+
     /**
      * This tag is applied to all aconfig default value-loaded flags.
      */
@@ -1439,6 +1446,20 @@
                     }
                 }
 
+                if (name != null && name.equals(STORAGE_MIGRATION_FLAG) && value.equals("true")) {
+                    File file = new File(STORAGE_MIGRATION_LOG);
+                    if (!file.exists()) {
+                        try (BufferedWriter writer =
+                                new BufferedWriter(new FileWriter(STORAGE_MIGRATION_LOG))) {
+                            final long timestamp = System.currentTimeMillis();
+                            String entry = String.format("%d | Log init", timestamp);
+                            writer.write(entry);
+                        } catch (IOException e) {
+                            Slog.e(LOG_TAG, "failed to write storage migration file", e);
+                        }
+                    }
+                }
+
                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
                         fromSystem, id, isPreservedInRestore));
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index 2e14e9b..c572bdb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -33,3 +33,10 @@
     bug: "327383546"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "storage_test_mission_1"
+    namespace: "core_experiments_team_internal"
+    description: "If this flag is detected as true on boot, writes a logfile to track storage migration correctness."
+    bug: "328444881"
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 43ea3ec..02d19dc 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -564,7 +564,7 @@
     <uses-permission android:name="android.permission.TEST_BIOMETRIC" />
 
     <!-- Permission required for CTS test - android.server.biometrics -->
-    <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
+    <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
 
     <!-- Permissions required for CTS test - NotificationManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3c18f17..d9c371a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -434,7 +434,7 @@
             </intent-filter>
         </receiver>
 
-        <activity android:name=".screenshot.LongScreenshotActivity"
+        <activity android:name=".screenshot.scroll.LongScreenshotActivity"
                   android:theme="@style/LongScreenshotActivity"
                   android:process=":screenshot"
                   android:exported="false"
@@ -529,15 +529,6 @@
             </intent-filter>
         </activity-alias>
 
-        <!-- Springboard for launching the share and edit activity. This needs to be in the main
-             system ui process since we need to notify the status bar to dismiss the keyguard -->
-        <receiver android:name=".screenshot.ActionProxyReceiver"
-            android:exported="false" />
-
-        <!-- Callback for deleting screenshot notification -->
-        <receiver android:name=".screenshot.DeleteScreenshotReceiver"
-            android:exported="false" />
-
         <!-- Callback for invoking a smart action from the screenshot notification. -->
         <receiver android:name=".screenshot.SmartActionsReceiver"
                   android:exported="false"/>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e5e3469..79ae389 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -345,6 +345,16 @@
 }
 
 flag {
+   name: "activity_transition_use_largest_window"
+   namespace: "systemui"
+   description: "Target largest opening window during activity transitions."
+   bug: "323294573"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
    name: "centralized_status_bar_height_fix"
    namespace: "systemui"
    description: "Refactors shade header and keyguard status bar to read status bar dimens from a"
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 99b7c36..2268d16 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -44,6 +44,7 @@
         "androidx.core_core-animation-nodeps",
         "androidx.core_core-ktx",
         "androidx.annotation_annotation",
+        "com_android_systemui_flags_lib",
         "SystemUIShaderLib",
         "WindowManager-Shell-shared",
         "animationlib",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 1b99e19..ea1cb34 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -43,6 +43,7 @@
 import com.android.app.animation.Interpolators
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.policy.ScreenDecorationsUtils
+import com.android.systemui.Flags.activityTransitionUseLargestWindow
 import kotlin.math.roundToInt
 
 private const val TAG = "ActivityTransitionAnimator"
@@ -648,11 +649,27 @@
             var candidate: RemoteAnimationTarget? = null
             for (it in apps) {
                 if (it.mode == RemoteAnimationTarget.MODE_OPENING) {
-                    if (!it.hasAnimatingParent) {
-                        return it
-                    }
-                    if (candidate == null) {
-                        candidate = it
+                    if (activityTransitionUseLargestWindow()) {
+                        if (
+                            candidate == null ||
+                                !it.hasAnimatingParent && candidate.hasAnimatingParent
+                        ) {
+                            candidate = it
+                            continue
+                        }
+                        if (
+                            !it.hasAnimatingParent &&
+                                it.screenSpaceBounds.hasGreaterAreaThan(candidate.screenSpaceBounds)
+                        ) {
+                            candidate = it
+                        }
+                    } else {
+                        if (!it.hasAnimatingParent) {
+                            return it
+                        }
+                        if (candidate == null) {
+                            candidate = it
+                        }
                     }
                 }
             }
@@ -960,5 +977,9 @@
                 e.printStackTrace()
             }
         }
+
+        private fun Rect.hasGreaterAreaThan(other: Rect): Boolean {
+            return (this.width() * this.height()) > (other.width() * other.height())
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 53f400f..55f7f69a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeWeatherClockBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockBlueprintModule
 import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
@@ -32,7 +31,6 @@
             DefaultBlueprintModule::class,
             OptionalSectionModule::class,
             ShortcutsBesideUdfpsBlueprintModule::class,
-            SplitShadeBlueprintModule::class,
             SplitShadeWeatherClockBlueprintModule::class,
             WeatherClockBlueprintModule::class,
         ],
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
index c5ff859..d9ed497 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
@@ -38,6 +38,9 @@
         from(ClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
             transitioningToSmallClock()
         }
+        from(ClockScenes.splitShadeLargeClockScene, to = ClockScenes.largeClockScene) {
+            spec = tween(1000)
+        }
     }
 
     private fun TransitionBuilder.transitioningToLargeClock() {
@@ -70,6 +73,8 @@
 object ClockScenes {
     val smallClockScene = SceneKey("small-clock-scene")
     val largeClockScene = SceneKey("large-clock-scene")
+    val splitShadeSmallClockScene = SceneKey("split-shade-small-clock-scene")
+    val splitShadeLargeClockScene = SceneKey("split-shade-large-clock-scene")
 }
 
 object ClockElementKeys {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 9509fd2..320c455 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -22,18 +22,15 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import dagger.Binds
 import dagger.Module
@@ -50,13 +47,11 @@
 constructor(
     private val viewModel: LockscreenContentViewModel,
     private val statusBarSection: StatusBarSection,
-    private val clockSection: DefaultClockSection,
-    private val notificationSection: NotificationSection,
     private val lockSection: LockSection,
     private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
-    private val mediaCarouselSection: MediaCarouselSection,
+    private val topAreaSection: TopAreaSection,
 ) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "default"
@@ -64,7 +59,6 @@
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
-        val resources = LocalContext.current.resources
 
         LockscreenLongPress(
             viewModel = viewModel.longPress,
@@ -77,17 +71,7 @@
                         modifier = Modifier.fillMaxWidth(),
                     ) {
                         with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                        with(clockSection) { DefaultClockLayout() }
-                        with(mediaCarouselSection) { MediaCarousel() }
-
-                        if (viewModel.areNotificationsVisible(resources = resources)) {
-                            with(notificationSection) {
-                                Notifications(
-                                    modifier = Modifier.fillMaxWidth().weight(weight = 1f)
-                                )
-                            }
-                        }
-
+                        with(topAreaSection) { DefaultClockLayoutWithNotifications() }
                         if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
                             with(ambientIndicationSectionOptional.get()) {
                                 AmbientIndication(modifier = Modifier.fillMaxWidth())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 9abfa42..64c2cb3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -22,18 +22,15 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import dagger.Binds
 import dagger.Module
@@ -50,13 +47,11 @@
 constructor(
     private val viewModel: LockscreenContentViewModel,
     private val statusBarSection: StatusBarSection,
-    private val clockSection: DefaultClockSection,
-    private val notificationSection: NotificationSection,
     private val lockSection: LockSection,
     private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
-    private val mediaCarouselSection: MediaCarouselSection,
+    private val topAreaSection: TopAreaSection,
 ) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "shortcuts-besides-udfps"
@@ -64,7 +59,6 @@
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
-        val resources = LocalContext.current.resources
 
         LockscreenLongPress(
             viewModel = viewModel.longPress,
@@ -77,16 +71,7 @@
                         modifier = Modifier.fillMaxWidth(),
                     ) {
                         with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                        with(clockSection) { DefaultClockLayout() }
-                        with(mediaCarouselSection) { MediaCarousel() }
-
-                        if (viewModel.areNotificationsVisible(resources = resources)) {
-                            with(notificationSection) {
-                                Notifications(
-                                    modifier = Modifier.fillMaxWidth().weight(weight = 1f)
-                                )
-                            }
-                        }
+                        with(topAreaSection) { DefaultClockLayoutWithNotifications() }
 
                         if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
                             with(ambientIndicationSectionOptional.get()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
deleted file mode 100644
index 652412d..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.composable.blueprint
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
-import com.android.systemui.Flags
-import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
-import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
-import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
-import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
-import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
-import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import java.util.Optional
-import javax.inject.Inject
-
-/**
- * Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
- * tablet form factor).
- */
-class SplitShadeBlueprint
-@Inject
-constructor(
-    private val viewModel: LockscreenContentViewModel,
-    private val statusBarSection: StatusBarSection,
-    private val clockSection: DefaultClockSection,
-    private val notificationSection: NotificationSection,
-    private val lockSection: LockSection,
-    private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
-    private val bottomAreaSection: BottomAreaSection,
-    private val settingsMenuSection: SettingsMenuSection,
-    private val mediaCarouselSection: MediaCarouselSection,
-    private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
-) : ComposableLockscreenSceneBlueprint {
-
-    override val id: String = "split-shade"
-
-    @Composable
-    override fun SceneScope.Content(modifier: Modifier) {
-        val isUdfpsVisible = viewModel.isUdfpsVisible
-
-        LockscreenLongPress(
-            viewModel = viewModel.longPress,
-            modifier = modifier,
-        ) { onSettingsMenuPlaced ->
-            Layout(
-                content = {
-                    // Constrained to above the lock icon.
-                    Column(
-                        modifier = Modifier.fillMaxSize(),
-                    ) {
-                        with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                        Row(
-                            modifier = Modifier.fillMaxSize(),
-                        ) {
-                            Column(
-                                modifier = Modifier.fillMaxHeight().weight(weight = 1f),
-                                horizontalAlignment = Alignment.CenterHorizontally,
-                            ) {
-                                with(clockSection) { DefaultClockLayout() }
-                                with(mediaCarouselSection) { MediaCarousel() }
-                            }
-                            with(notificationSection) {
-                                val splitShadeTopMargin: Dp =
-                                    if (Flags.centralizedStatusBarHeightFix()) {
-                                        largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
-                                    } else {
-                                        dimensionResource(
-                                            id = R.dimen.large_screen_shade_header_height
-                                        )
-                                    }
-                                Notifications(
-                                    modifier =
-                                        Modifier.fillMaxHeight()
-                                            .weight(weight = 1f)
-                                            .padding(top = splitShadeTopMargin)
-                                )
-                            }
-                        }
-
-                        if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
-                            with(ambientIndicationSectionOptional.get()) {
-                                AmbientIndication(modifier = Modifier.fillMaxWidth())
-                            }
-                        }
-                    }
-
-                    with(lockSection) { LockIcon() }
-
-                    // Aligned to bottom and constrained to below the lock icon.
-                    Column(modifier = Modifier.fillMaxWidth()) {
-                        if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
-                            with(ambientIndicationSectionOptional.get()) {
-                                AmbientIndication(modifier = Modifier.fillMaxWidth())
-                            }
-                        }
-
-                        with(bottomAreaSection) {
-                            IndicationArea(modifier = Modifier.fillMaxWidth())
-                        }
-                    }
-
-                    // Aligned to bottom and NOT constrained by the lock icon.
-                    with(bottomAreaSection) {
-                        Shortcut(isStart = true, applyPadding = true)
-                        Shortcut(isStart = false, applyPadding = true)
-                    }
-                    with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
-                },
-                modifier = Modifier.fillMaxSize(),
-            ) { measurables, constraints ->
-                check(measurables.size == 6)
-                val aboveLockIconMeasurable = measurables[0]
-                val lockIconMeasurable = measurables[1]
-                val belowLockIconMeasurable = measurables[2]
-                val startShortcutMeasurable = measurables[3]
-                val endShortcutMeasurable = measurables[4]
-                val settingsMenuMeasurable = measurables[5]
-
-                val noMinConstraints =
-                    constraints.copy(
-                        minWidth = 0,
-                        minHeight = 0,
-                    )
-                val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
-                val lockIconBounds =
-                    IntRect(
-                        left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
-                        top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
-                        right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
-                        bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
-                    )
-
-                val aboveLockIconPlaceable =
-                    aboveLockIconMeasurable.measure(
-                        noMinConstraints.copy(maxHeight = lockIconBounds.top)
-                    )
-                val belowLockIconPlaceable =
-                    belowLockIconMeasurable.measure(
-                        noMinConstraints.copy(
-                            maxHeight =
-                                (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
-                        )
-                    )
-                val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
-                val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
-                val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
-                layout(constraints.maxWidth, constraints.maxHeight) {
-                    aboveLockIconPlaceable.place(
-                        x = 0,
-                        y = 0,
-                    )
-                    lockIconPlaceable.place(
-                        x = lockIconBounds.left,
-                        y = lockIconBounds.top,
-                    )
-                    belowLockIconPlaceable.place(
-                        x = 0,
-                        y = constraints.maxHeight - belowLockIconPlaceable.height,
-                    )
-                    startShortcutPleaceable.place(
-                        x = 0,
-                        y = constraints.maxHeight - startShortcutPleaceable.height,
-                    )
-                    endShortcutPleaceable.place(
-                        x = constraints.maxWidth - endShortcutPleaceable.width,
-                        y = constraints.maxHeight - endShortcutPleaceable.height,
-                    )
-                    settingsMenuPlaceable.place(
-                        x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
-                        y = constraints.maxHeight - settingsMenuPlaceable.height,
-                    )
-                }
-            }
-        }
-    }
-}
-
-@Module
-interface SplitShadeBlueprintModule {
-    @Binds
-    @IntoSet
-    fun blueprint(blueprint: SplitShadeBlueprint): ComposableLockscreenSceneBlueprint
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index f86623f..ee4e2d6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -112,7 +112,7 @@
 
                         with(mediaCarouselSection) { MediaCarousel() }
 
-                        if (viewModel.areNotificationsVisible(resources = resources)) {
+                        if (viewModel.areNotificationsVisible) {
                             with(notificationSection) {
                                 Notifications(
                                     modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 1ab2bc76..82e19e7c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -16,39 +16,29 @@
 
 package com.android.systemui.keyguard.ui.composable.section
 
+import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.modifiers.padding
 import com.android.systemui.customization.R as customizationR
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smartspaceElementKey
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.defaultClockTransitions
-import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
 import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
 import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
 import javax.inject.Inject
 
 /** Provides small clock and large clock composables for the default clock face. */
@@ -56,91 +46,10 @@
 @Inject
 constructor(
     private val viewModel: KeyguardClockViewModel,
-    private val clockInteractor: KeyguardClockInteractor,
     private val aodBurnInViewModel: AodBurnInViewModel,
-    private val lockscreenContentViewModel: LockscreenContentViewModel,
-    private val smartSpaceSection: SmartSpaceSection,
 ) {
     @Composable
-    fun DefaultClockLayout(
-        modifier: Modifier = Modifier,
-    ) {
-        val isLargeClockVisible by viewModel.isLargeClockVisible.collectAsState()
-        val burnIn = rememberBurnIn(clockInteractor)
-        val currentScene =
-            if (isLargeClockVisible) {
-                largeClockScene
-            } else {
-                smallClockScene
-            }
-
-        LaunchedEffect(isLargeClockVisible) {
-            if (isLargeClockVisible) {
-                burnIn.onSmallClockTopChanged(null)
-            }
-        }
-
-        SceneTransitionLayout(
-            modifier = modifier,
-            currentScene = currentScene,
-            onChangeScene = {},
-            transitions = defaultClockTransitions,
-        ) {
-            scene(smallClockScene) {
-                Column {
-                    SmallClock(
-                        burnInParams = burnIn.parameters,
-                        onTopChanged = burnIn.onSmallClockTopChanged,
-                        modifier = Modifier.element(smallClockElementKey).fillMaxWidth()
-                    )
-                    SmartSpaceContent()
-                }
-            }
-
-            scene(largeClockScene) {
-                Column {
-                    SmartSpaceContent()
-                    LargeClock(modifier = Modifier.element(largeClockElementKey).fillMaxWidth())
-                }
-            }
-        }
-    }
-
-    @Composable
-    private fun SceneScope.SmartSpaceContent(
-        modifier: Modifier = Modifier,
-    ) {
-        val burnIn = rememberBurnIn(clockInteractor)
-        val resources = LocalContext.current.resources
-
-        MovableElement(key = smartspaceElementKey, modifier = modifier) {
-            content {
-                with(smartSpaceSection) {
-                    this@SmartSpaceContent.SmartSpace(
-                        burnInParams = burnIn.parameters,
-                        onTopChanged = burnIn.onSmartspaceTopChanged,
-                        modifier =
-                            Modifier.fillMaxWidth()
-                                .padding(
-                                    top = {
-                                        lockscreenContentViewModel.getSmartSpacePaddingTop(
-                                            resources
-                                        )
-                                    },
-                                    bottom = {
-                                        resources.getDimensionPixelSize(
-                                            R.dimen.keyguard_status_view_bottom_margin
-                                        )
-                                    }
-                                ),
-                    )
-                }
-            }
-        }
-    }
-
-    @Composable
-    private fun SceneScope.SmallClock(
+    fun SceneScope.SmallClock(
         burnInParams: BurnInParameters,
         onTopChanged: (top: Float?) -> Unit,
         modifier: Modifier = Modifier,
@@ -152,58 +61,60 @@
         viewModel.clock = currentClock
 
         val context = LocalContext.current
-
-        AndroidView(
-            factory = { context ->
-                FrameLayout(context).apply {
-                    val newClockView = checkNotNull(currentClock).smallClock.view
-                    (newClockView.parent as? ViewGroup)?.removeView(newClockView)
-                    addView(newClockView)
-                }
-            },
-            update = {
-                val newClockView = checkNotNull(currentClock).smallClock.view
-                it.removeAllViews()
-                (newClockView.parent as? ViewGroup)?.removeView(newClockView)
-                it.addView(newClockView)
-            },
-            modifier =
-                modifier
-                    .padding(
-                        horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
-                    )
-                    .padding(top = { viewModel.getSmallClockTopMargin(context) })
-                    .onTopPlacementChanged(onTopChanged)
-                    .burnInAware(
-                        viewModel = aodBurnInViewModel,
-                        params = burnInParams,
-                    ),
-        )
+        MovableElement(key = smallClockElementKey, modifier = modifier) {
+            content {
+                AndroidView(
+                    factory = { context ->
+                        FrameLayout(context).apply {
+                            addClockView(checkNotNull(currentClock).smallClock.view)
+                        }
+                    },
+                    update = { it.addClockView(checkNotNull(currentClock).smallClock.view) },
+                    modifier =
+                        Modifier.padding(
+                                horizontal =
+                                    dimensionResource(customizationR.dimen.clock_padding_start)
+                            )
+                            .padding(top = { viewModel.getSmallClockTopMargin(context) })
+                            .onTopPlacementChanged(onTopChanged)
+                            .burnInAware(
+                                viewModel = aodBurnInViewModel,
+                                params = burnInParams,
+                            ),
+                )
+            }
+        }
     }
 
     @Composable
-    private fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
+    fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
         val currentClock by viewModel.currentClock.collectAsState()
         viewModel.clock = currentClock
         if (currentClock?.largeClock?.view == null) {
             return
         }
 
-        AndroidView(
-            factory = { context ->
-                FrameLayout(context).apply {
-                    val newClockView = checkNotNull(currentClock).largeClock.view
-                    (newClockView.parent as? ViewGroup)?.removeView(newClockView)
-                    addView(newClockView)
-                }
-            },
-            update = {
-                val newClockView = checkNotNull(currentClock).largeClock.view
-                it.removeAllViews()
-                (newClockView.parent as? ViewGroup)?.removeView(newClockView)
-                it.addView(newClockView)
-            },
-            modifier = modifier.fillMaxSize()
-        )
+        MovableElement(key = largeClockElementKey, modifier = modifier) {
+            content {
+                AndroidView(
+                    factory = { context ->
+                        FrameLayout(context).apply {
+                            addClockView(checkNotNull(currentClock).largeClock.view)
+                        }
+                    },
+                    update = { it.addClockView(checkNotNull(currentClock).largeClock.view) },
+                    modifier = Modifier.fillMaxSize()
+                )
+            }
+        }
+    }
+
+    private fun FrameLayout.addClockView(clockView: View) {
+        if (contains(clockView)) {
+            return
+        }
+        removeAllViews()
+        (clockView.parent as? ViewGroup)?.removeView(clockView)
+        addView(clockView)
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index d1cc53e..fc8b3b9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -30,16 +30,20 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
 import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
 import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import javax.inject.Inject
@@ -51,6 +55,7 @@
     private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
     private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
     private val aodBurnInViewModel: AodBurnInViewModel,
+    private val lockscreenContentViewModel: LockscreenContentViewModel,
 ) {
     @Composable
     fun SceneScope.SmartSpace(
@@ -58,57 +63,71 @@
         onTopChanged: (top: Float?) -> Unit,
         modifier: Modifier = Modifier,
     ) {
-        Column(
-            modifier = modifier.onTopPlacementChanged(onTopChanged),
-        ) {
-            if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
-                return
-            }
+        val resources = LocalContext.current.resources
 
-            val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
-            val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+        MovableElement(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
+            Column(
+                modifier =
+                    modifier
+                        .onTopPlacementChanged(onTopChanged)
+                        .padding(
+                            top = { lockscreenContentViewModel.getSmartSpacePaddingTop(resources) },
+                            bottom = {
+                                resources.getDimensionPixelSize(
+                                    R.dimen.keyguard_status_view_bottom_margin
+                                )
+                            }
+                        )
+            ) {
+                if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+                    return@Column
+                }
 
-            if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
-                Row(
-                    verticalAlignment = Alignment.CenterVertically,
+                val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
+                val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+
+                if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+                    Row(
+                        verticalAlignment = Alignment.CenterVertically,
+                        modifier =
+                            Modifier.fillMaxWidth()
+                                // All items will be constrained to be as tall as the shortest item.
+                                .height(IntrinsicSize.Min)
+                                .padding(
+                                    start = paddingBelowClockStart,
+                                ),
+                    ) {
+                        Date(
+                            modifier =
+                                Modifier.burnInAware(
+                                    viewModel = aodBurnInViewModel,
+                                    params = burnInParams,
+                                ),
+                        )
+                        Spacer(modifier = Modifier.width(4.dp))
+                        Weather(
+                            modifier =
+                                Modifier.burnInAware(
+                                    viewModel = aodBurnInViewModel,
+                                    params = burnInParams,
+                                ),
+                        )
+                    }
+                }
+
+                Card(
                     modifier =
                         Modifier.fillMaxWidth()
-                            // All items will be constrained to be as tall as the shortest item.
-                            .height(IntrinsicSize.Min)
                             .padding(
                                 start = paddingBelowClockStart,
-                            ),
-                ) {
-                    Date(
-                        modifier =
-                            Modifier.burnInAware(
+                                end = paddingBelowClockEnd,
+                            )
+                            .burnInAware(
                                 viewModel = aodBurnInViewModel,
                                 params = burnInParams,
                             ),
-                    )
-                    Spacer(modifier = Modifier.width(4.dp))
-                    Weather(
-                        modifier =
-                            Modifier.burnInAware(
-                                viewModel = aodBurnInViewModel,
-                                params = burnInParams,
-                            ),
-                    )
-                }
+                )
             }
-
-            Card(
-                modifier =
-                    Modifier.fillMaxWidth()
-                        .padding(
-                            start = paddingBelowClockStart,
-                            end = paddingBelowClockEnd,
-                        )
-                        .burnInAware(
-                            viewModel = aodBurnInViewModel,
-                            params = burnInParams,
-                        ),
-            )
         }
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
new file mode 100644
index 0000000..7635841
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.systemui.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeSmallClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition
+import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.LargeScreenHeaderHelper
+import javax.inject.Inject
+
+class TopAreaSection
+@Inject
+constructor(
+    private val clockViewModel: KeyguardClockViewModel,
+    private val smartSpaceSection: SmartSpaceSection,
+    private val mediaCarouselSection: MediaCarouselSection,
+    private val notificationSection: NotificationSection,
+    private val clockSection: DefaultClockSection,
+    private val clockInteractor: KeyguardClockInteractor,
+) {
+    @Composable
+    fun DefaultClockLayoutWithNotifications(
+        modifier: Modifier = Modifier,
+    ) {
+        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+        val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
+        val currentScene =
+            when (currentClockLayout) {
+                KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
+                    splitShadeLargeClockScene
+                KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK ->
+                    splitShadeSmallClockScene
+                KeyguardClockViewModel.ClockLayout.LARGE_CLOCK -> largeClockScene
+                KeyguardClockViewModel.ClockLayout.SMALL_CLOCK -> smallClockScene
+            }
+
+        val splitShadeTopMargin: Dp =
+            if (Flags.centralizedStatusBarHeightFix()) {
+                LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
+            } else {
+                dimensionResource(id = R.dimen.large_screen_shade_header_height)
+            }
+        val burnIn = rememberBurnIn(clockInteractor)
+
+        LaunchedEffect(isLargeClockVisible) {
+            if (isLargeClockVisible) {
+                burnIn.onSmallClockTopChanged(null)
+            }
+        }
+
+        SceneTransitionLayout(
+            modifier = modifier.fillMaxSize(),
+            currentScene = currentScene,
+            onChangeScene = {},
+            transitions = ClockTransition.defaultClockTransitions,
+        ) {
+            scene(ClockScenes.splitShadeLargeClockScene) {
+                Row(
+                    modifier = Modifier.fillMaxSize(),
+                ) {
+                    Column(
+                        modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+                        horizontalAlignment = Alignment.CenterHorizontally,
+                    ) {
+                        with(smartSpaceSection) {
+                            SmartSpace(
+                                burnInParams = burnIn.parameters,
+                                onTopChanged = burnIn.onSmartspaceTopChanged,
+                            )
+                        }
+                        with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                    }
+                    with(notificationSection) {
+                        Notifications(
+                            modifier =
+                                Modifier.fillMaxHeight()
+                                    .weight(weight = 1f)
+                                    .padding(top = splitShadeTopMargin)
+                        )
+                    }
+                }
+            }
+
+            scene(ClockScenes.splitShadeSmallClockScene) {
+                Row(
+                    modifier = Modifier.fillMaxSize(),
+                ) {
+                    Column(
+                        modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+                        horizontalAlignment = Alignment.CenterHorizontally,
+                    ) {
+                        with(clockSection) {
+                            SmallClock(
+                                burnInParams = burnIn.parameters,
+                                onTopChanged = burnIn.onSmallClockTopChanged,
+                                modifier = Modifier.fillMaxWidth()
+                            )
+                        }
+                        with(smartSpaceSection) {
+                            SmartSpace(
+                                burnInParams = burnIn.parameters,
+                                onTopChanged = burnIn.onSmartspaceTopChanged,
+                            )
+                        }
+                        with(mediaCarouselSection) { MediaCarousel() }
+                    }
+                    with(notificationSection) {
+                        Notifications(
+                            modifier =
+                                Modifier.fillMaxHeight()
+                                    .weight(weight = 1f)
+                                    .padding(top = splitShadeTopMargin)
+                        )
+                    }
+                }
+            }
+
+            scene(ClockScenes.smallClockScene) {
+                Column {
+                    with(clockSection) {
+                        SmallClock(
+                            burnInParams = burnIn.parameters,
+                            onTopChanged = burnIn.onSmallClockTopChanged,
+                            modifier = Modifier.fillMaxWidth()
+                        )
+                    }
+                    with(smartSpaceSection) {
+                        SmartSpace(
+                            burnInParams = burnIn.parameters,
+                            onTopChanged = burnIn.onSmartspaceTopChanged,
+                        )
+                    }
+                    with(mediaCarouselSection) { MediaCarousel() }
+                    with(notificationSection) {
+                        Notifications(
+                            modifier =
+                                androidx.compose.ui.Modifier.fillMaxWidth().weight(weight = 1f)
+                        )
+                    }
+                }
+            }
+
+            scene(ClockScenes.largeClockScene) {
+                Column {
+                    with(smartSpaceSection) {
+                        SmartSpace(
+                            burnInParams = burnIn.parameters,
+                            onTopChanged = burnIn.onSmartspaceTopChanged,
+                        )
+                    }
+                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index fa94ea0..761292b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -87,7 +87,7 @@
         enabled = state.isEnabled,
         icon = { isDragging ->
             if (isDragging) {
-                Text(text = value.toInt().toString(), color = LocalContentColor.current)
+                Text(text = state.valueText, color = LocalContentColor.current)
             } else {
                 state.icon?.let {
                     IconButton(
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 63ec54f..82083f9 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
@@ -605,6 +605,8 @@
 
     override val isInitiatedByUserInput = true
 
+    override var bouncingScene: SceneKey? = null
+
     /** The current offset caused by the drag gesture. */
     var dragOffset by mutableFloatStateOf(0f)
 
@@ -694,14 +696,31 @@
     ): OffsetAnimation {
         return startOffsetAnimation {
             val animatable = Animatable(dragOffset, OffsetVisibilityThreshold)
+            val isTargetGreater = targetOffset > animatable.value
             val job =
                 coroutineScope
                     .launch {
-                        animatable.animateTo(
-                            targetValue = targetOffset,
-                            animationSpec = swipeSpec,
-                            initialVelocity = initialVelocity,
-                        )
+                        try {
+                            animatable.animateTo(
+                                targetValue = targetOffset,
+                                animationSpec = swipeSpec,
+                                initialVelocity = initialVelocity,
+                            ) {
+                                if (bouncingScene == null) {
+                                    val isBouncing =
+                                        if (isTargetGreater) {
+                                            value > targetOffset
+                                        } else {
+                                            value < targetOffset
+                                        }
+                                    if (isBouncing) {
+                                        bouncingScene = targetScene
+                                    }
+                                }
+                            }
+                        } finally {
+                            bouncingScene = null
+                        }
                     }
                     // Make sure that we settle to target scene at the end of the animation or if
                     // the animation is cancelled.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index c7186da..f1177a8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -588,7 +588,8 @@
             // TODO(b/290184746): Make sure that we don't overflow transformations associated to a
             // range.
             val directionSign = if (transition.isUpOrLeft) -1 else 1
-            val overscrollProgress = transition.progress.let { if (it > 1f) it - 1f else it }
+            val isToScene = overscroll.scene == transition.toScene
+            val overscrollProgress = transition.progress.let { if (isToScene) it - 1f else it }
             val progress = directionSign * overscrollProgress
             val rangeProgress = propertySpec.range?.progress(progress) ?: progress
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index e6f5d58..617a8ea 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -255,6 +255,12 @@
          */
         val overscrollScope: OverscrollScope
 
+        /**
+         * The scene around which the transition is currently bouncing. When not `null`, this
+         * transition is currently oscillating around this scene and will soon settle to that scene.
+         */
+        val bouncingScene: SceneKey?
+
         companion object {
             const val DistanceUnspecified = 0f
         }
@@ -287,9 +293,10 @@
             val transition = currentTransition ?: return null
             if (transition !is TransitionState.HasOverscrollProperties) return null
             val progress = transition.progress
+            val bouncingScene = transition.bouncingScene
             return when {
-                progress < 0f -> fromOverscrollSpec
-                progress > 1f -> toOverscrollSpec
+                progress < 0f || bouncingScene == transition.fromScene -> fromOverscrollSpec
+                progress > 1f || bouncingScene == transition.toScene -> toOverscrollSpec
                 else -> null
             }
         }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 26e01fe..0804761 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
@@ -752,4 +754,55 @@
         assertThat(state.currentOverscrollSpec).isNotNull()
         fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 1.5f)
     }
+
+    @Test
+    fun elementTransitionWithDistanceDuringOverscrollBouncing() {
+        val layoutWidth = 200.dp
+        val layoutHeight = 400.dp
+        val state =
+            setupOverscrollScenario(
+                layoutWidth = layoutWidth,
+                layoutHeight = layoutHeight,
+                sceneTransitions = {
+                    defaultSwipeSpec =
+                        spring(
+                            dampingRatio = Spring.DampingRatioMediumBouncy,
+                            stiffness = Spring.StiffnessLow,
+                        )
+
+                    overscroll(TestScenes.SceneB, Orientation.Vertical) {
+                        // On overscroll 100% -> Foo should translate by layoutHeight
+                        translate(TestElements.Foo, y = { absoluteDistance })
+                    }
+                },
+                firstScroll = 1f, // 100% scroll
+            )
+
+        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+        fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+
+        rule.onRoot().performTouchInput {
+            // Scroll another 50%
+            moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
+        }
+
+        val transition = state.currentTransition
+        assertThat(transition).isNotNull()
+        transition as TransitionState.HasOverscrollProperties
+
+        // Scroll 150% (100% scroll + 50% overscroll)
+        assertThat(transition.progress).isEqualTo(1.5f)
+        assertThat(state.currentOverscrollSpec).isNotNull()
+        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * (transition.progress - 1f))
+
+        // finger raised
+        rule.onRoot().performTouchInput { up() }
+
+        // The target value is 1f, but the spring (defaultSwipeSpec) allows you to go to a lower
+        // value.
+        rule.waitUntil(timeoutMillis = 10_000) { transition.progress < 1f }
+
+        assertThat(state.currentOverscrollSpec).isNotNull()
+        assertThat(transition.bouncingScene).isEqualTo(transition.toScene)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
index 73a66c6..a32fe22 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
@@ -27,6 +27,7 @@
     isInitiatedByUserInput: Boolean = false,
     isUserInputOngoing: Boolean = false,
     isUpOrLeft: Boolean = false,
+    bouncingScene: SceneKey? = null,
     orientation: Orientation = Orientation.Horizontal,
 ): TransitionState.Transition {
     return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
@@ -37,6 +38,7 @@
         override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
         override val isUserInputOngoing: Boolean = isUserInputOngoing
         override val isUpOrLeft: Boolean = isUpOrLeft
+        override val bouncingScene: SceneKey? = bouncingScene
         override val orientation: Orientation = orientation
         override val overscrollScope: OverscrollScope =
             object : OverscrollScope {
diff --git a/packages/SystemUI/monet/Android.bp b/packages/SystemUI/monet/Android.bp
index 98f7ace..c54fdab 100644
--- a/packages/SystemUI/monet/Android.bp
+++ b/packages/SystemUI/monet/Android.bp
@@ -24,6 +24,7 @@
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.core_core",
+        "libmonet",
     ],
     srcs: [
         "src/**/*.java",
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 46a90f6..47a00f4 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -20,11 +20,17 @@
 import android.app.WallpaperColors
 import android.graphics.Color
 import com.android.internal.graphics.ColorUtils
-import com.android.internal.graphics.cam.Cam
-import com.android.internal.graphics.cam.CamUtils
+import com.google.ux.material.libmonet.hct.Hct
+import com.google.ux.material.libmonet.scheme.DynamicScheme
+import com.google.ux.material.libmonet.scheme.SchemeContent
+import com.google.ux.material.libmonet.scheme.SchemeExpressive
+import com.google.ux.material.libmonet.scheme.SchemeFruitSalad
+import com.google.ux.material.libmonet.scheme.SchemeMonochrome
+import com.google.ux.material.libmonet.scheme.SchemeNeutral
+import com.google.ux.material.libmonet.scheme.SchemeRainbow
+import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
+import com.google.ux.material.libmonet.scheme.SchemeVibrant
 import kotlin.math.absoluteValue
-import kotlin.math.max
-import kotlin.math.min
 import kotlin.math.roundToInt
 
 const val TAG = "ColorScheme"
@@ -33,347 +39,65 @@
 const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
 const val MIN_CHROMA = 5
 
-internal interface Hue {
-    fun get(sourceColor: Cam): Double
-
-    /**
-     * Given a hue, and a mapping of hues to hue rotations, find which hues in the mapping the hue
-     * fall betweens, and use the hue rotation of the lower hue.
-     *
-     * @param sourceHue hue of source color
-     * @param hueAndRotations list of pairs, where the first item in a pair is a hue, and the second
-     *   item in the pair is a hue rotation that should be applied
-     */
-    fun getHueRotation(sourceHue: Float, hueAndRotations: List<Pair<Int, Int>>): Double {
-        val sanitizedSourceHue = (if (sourceHue < 0 || sourceHue >= 360) 0 else sourceHue).toFloat()
-        for (i in 0..hueAndRotations.size - 2) {
-            val thisHue = hueAndRotations[i].first.toFloat()
-            val nextHue = hueAndRotations[i + 1].first.toFloat()
-            if (thisHue <= sanitizedSourceHue && sanitizedSourceHue < nextHue) {
-                return ColorScheme.wrapDegreesDouble(
-                    sanitizedSourceHue.toDouble() + hueAndRotations[i].second
-                )
-            }
-        }
-
-        // If this statement executes, something is wrong, there should have been a rotation
-        // found using the arrays.
-        return sourceHue.toDouble()
-    }
-}
-
-internal class HueSource : Hue {
-    override fun get(sourceColor: Cam): Double {
-        return sourceColor.hue.toDouble()
-    }
-}
-
-internal class HueAdd(val amountDegrees: Double) : Hue {
-    override fun get(sourceColor: Cam): Double {
-        return ColorScheme.wrapDegreesDouble(sourceColor.hue.toDouble() + amountDegrees)
-    }
-}
-
-internal class HueSubtract(val amountDegrees: Double) : Hue {
-    override fun get(sourceColor: Cam): Double {
-        return ColorScheme.wrapDegreesDouble(sourceColor.hue.toDouble() - amountDegrees)
-    }
-}
-
-internal class HueVibrantSecondary() : Hue {
-    val hueToRotations =
-        listOf(
-            Pair(0, 18),
-            Pair(41, 15),
-            Pair(61, 10),
-            Pair(101, 12),
-            Pair(131, 15),
-            Pair(181, 18),
-            Pair(251, 15),
-            Pair(301, 12),
-            Pair(360, 12)
-        )
-
-    override fun get(sourceColor: Cam): Double {
-        return getHueRotation(sourceColor.hue, hueToRotations)
-    }
-}
-
-internal class HueVibrantTertiary() : Hue {
-    val hueToRotations =
-        listOf(
-            Pair(0, 35),
-            Pair(41, 30),
-            Pair(61, 20),
-            Pair(101, 25),
-            Pair(131, 30),
-            Pair(181, 35),
-            Pair(251, 30),
-            Pair(301, 25),
-            Pair(360, 25)
-        )
-
-    override fun get(sourceColor: Cam): Double {
-        return getHueRotation(sourceColor.hue, hueToRotations)
-    }
-}
-
-internal class HueExpressiveSecondary() : Hue {
-    val hueToRotations =
-        listOf(
-            Pair(0, 45),
-            Pair(21, 95),
-            Pair(51, 45),
-            Pair(121, 20),
-            Pair(151, 45),
-            Pair(191, 90),
-            Pair(271, 45),
-            Pair(321, 45),
-            Pair(360, 45)
-        )
-
-    override fun get(sourceColor: Cam): Double {
-        return getHueRotation(sourceColor.hue, hueToRotations)
-    }
-}
-
-internal class HueExpressiveTertiary() : Hue {
-    val hueToRotations =
-        listOf(
-            Pair(0, 120),
-            Pair(21, 120),
-            Pair(51, 20),
-            Pair(121, 45),
-            Pair(151, 20),
-            Pair(191, 15),
-            Pair(271, 20),
-            Pair(321, 120),
-            Pair(360, 120)
-        )
-
-    override fun get(sourceColor: Cam): Double {
-        return getHueRotation(sourceColor.hue, hueToRotations)
-    }
-}
-
-internal interface Chroma {
-    fun get(sourceColor: Cam): Double
-
-    companion object {
-        val MAX_VALUE = 120.0
-        val MIN_VALUE = 0.0
-    }
-}
-
-internal class ChromaMaxOut : Chroma {
-    override fun get(sourceColor: Cam): Double {
-        // Intentionally high. Gamut mapping from impossible HCT to sRGB will ensure that
-        // the maximum chroma is reached, even if lower than this constant.
-        return Chroma.MAX_VALUE + 10.0
-    }
-}
-
-internal class ChromaMultiple(val multiple: Double) : Chroma {
-    override fun get(sourceColor: Cam): Double {
-        return sourceColor.chroma * multiple
-    }
-}
-
-internal class ChromaAdd(val amount: Double) : Chroma {
-    override fun get(sourceColor: Cam): Double {
-        return sourceColor.chroma + amount
-    }
-}
-
-internal class ChromaBound(
-    val baseChroma: Chroma,
-    val minVal: Double,
-    val maxVal: Double,
-) : Chroma {
-    override fun get(sourceColor: Cam): Double {
-        val result = baseChroma.get(sourceColor)
-        return min(max(result, minVal), maxVal)
-    }
-}
-
-internal class ChromaConstant(val chroma: Double) : Chroma {
-    override fun get(sourceColor: Cam): Double {
-        return chroma
-    }
-}
-
-internal class ChromaSource : Chroma {
-    override fun get(sourceColor: Cam): Double {
-        return sourceColor.chroma.toDouble()
-    }
-}
-
-internal class TonalSpec(val hue: Hue = HueSource(), val chroma: Chroma) {
-    fun shades(sourceColor: Cam): List<Int> {
-        val hue = hue.get(sourceColor)
-        val chroma = chroma.get(sourceColor)
-        return Shades.of(hue.toFloat(), chroma.toFloat()).toList()
-    }
-
-    fun getAtTone(sourceColor: Cam, tone: Float): Int {
-        val hue = hue.get(sourceColor)
-        val chroma = chroma.get(sourceColor)
-        return ColorUtils.CAMToColor(hue.toFloat(), chroma.toFloat(), (1000f - tone) / 10f)
-    }
-}
-
-internal class CoreSpec(
-    val a1: TonalSpec,
-    val a2: TonalSpec,
-    val a3: TonalSpec,
-    val n1: TonalSpec,
-    val n2: TonalSpec
-)
-
-enum class Style(internal val coreSpec: CoreSpec) {
-    SPRITZ(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
-            a2 = TonalSpec(HueSource(), ChromaConstant(8.0)),
-            a3 = TonalSpec(HueSource(), ChromaConstant(16.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(2.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(2.0))
-        )
-    ),
-    TONAL_SPOT(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaConstant(36.0)),
-            a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
-            a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
-        )
-    ),
-    VIBRANT(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaMaxOut()),
-            a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
-            a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
-        )
-    ),
-    EXPRESSIVE(
-        CoreSpec(
-            a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
-            a2 = TonalSpec(HueExpressiveSecondary(), ChromaConstant(24.0)),
-            a3 = TonalSpec(HueExpressiveTertiary(), ChromaConstant(32.0)),
-            n1 = TonalSpec(HueAdd(15.0), ChromaConstant(8.0)),
-            n2 = TonalSpec(HueAdd(15.0), ChromaConstant(12.0))
-        )
-    ),
-    RAINBOW(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaConstant(48.0)),
-            a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
-            a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
-        )
-    ),
-    FRUIT_SALAD(
-        CoreSpec(
-            a1 = TonalSpec(HueSubtract(50.0), ChromaConstant(48.0)),
-            a2 = TonalSpec(HueSubtract(50.0), ChromaConstant(36.0)),
-            a3 = TonalSpec(HueSource(), ChromaConstant(36.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(16.0))
-        )
-    ),
-    CONTENT(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaSource()),
-            a2 = TonalSpec(HueSource(), ChromaMultiple(0.33)),
-            a3 = TonalSpec(HueSource(), ChromaMultiple(0.66)),
-            n1 = TonalSpec(HueSource(), ChromaMultiple(0.0833)),
-            n2 = TonalSpec(HueSource(), ChromaMultiple(0.1666))
-        )
-    ),
-    MONOCHROMATIC(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaConstant(.0)),
-            a2 = TonalSpec(HueSource(), ChromaConstant(.0)),
-            a3 = TonalSpec(HueSource(), ChromaConstant(.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(.0))
-        )
-    ),
-    CLOCK(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaBound(ChromaSource(), 20.0, Chroma.MAX_VALUE)),
-            a2 = TonalSpec(HueAdd(10.0), ChromaBound(ChromaMultiple(0.85), 17.0, 40.0)),
-            a3 = TonalSpec(HueAdd(20.0), ChromaBound(ChromaAdd(20.0), 50.0, Chroma.MAX_VALUE)),
-
-            // Not Used
-            n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
-        )
-    ),
-    CLOCK_VIBRANT(
-        CoreSpec(
-            a1 = TonalSpec(HueSource(), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)),
-            a2 = TonalSpec(HueAdd(20.0), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)),
-            a3 = TonalSpec(HueAdd(60.0), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)),
-
-            // Not Used
-            n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
-            n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
-        )
-    )
+enum class Style{
+    SPRITZ,
+    TONAL_SPOT,
+    VIBRANT,
+    EXPRESSIVE,
+    RAINBOW,
+    FRUIT_SALAD,
+    CONTENT,
+    MONOCHROMATIC,
+    CLOCK,
+    CLOCK_VIBRANT
 }
 
 class TonalPalette
 internal constructor(
-    private val spec: TonalSpec,
-    seedColor: Int,
+    private val materialTonalPalette: com.google.ux.material.libmonet.palettes.TonalPalette
 ) {
-    val seedCam: Cam = Cam.fromInt(seedColor)
-    val allShades: List<Int> = spec.shades(seedCam)
-    val allShadesMapped: Map<Int, Int> = SHADE_KEYS.zip(allShades).toMap()
-    val baseColor: Int
+    @Deprecated("Do not use. For color system only")
+    val allShades: List<Int>
+    val allShadesMapped: Map<Int, Int>
 
-    init {
-        val h = spec.hue.get(seedCam).toFloat()
-        val c = spec.chroma.get(seedCam).toFloat()
-        baseColor = ColorUtils.CAMToColor(h, c, CamUtils.lstarFromInt(seedColor))
+    init{
+        allShades = SHADE_KEYS.map {key -> getAtTone(key.toFloat()) }
+        allShadesMapped = SHADE_KEYS.zip(allShades).toMap()
     }
 
     // Dynamically computed tones across the full range from 0 to 1000
-    fun getAtTone(tone: Float) = spec.getAtTone(seedCam, tone)
+    fun getAtTone(shade: Float): Int = materialTonalPalette.tone(((1000.0f - shade) / 10f).toInt())
 
     // Predefined & precomputed tones
-    val s10: Int
+    val s0: Int
         get() = this.allShades[0]
-    val s50: Int
+    val s10: Int
         get() = this.allShades[1]
-    val s100: Int
+    val s50: Int
         get() = this.allShades[2]
-    val s200: Int
+    val s100: Int
         get() = this.allShades[3]
-    val s300: Int
+    val s200: Int
         get() = this.allShades[4]
-    val s400: Int
+    val s300: Int
         get() = this.allShades[5]
-    val s500: Int
+    val s400: Int
         get() = this.allShades[6]
-    val s600: Int
+    val s500: Int
         get() = this.allShades[7]
-    val s700: Int
+    val s600: Int
         get() = this.allShades[8]
-    val s800: Int
+    val s700: Int
         get() = this.allShades[9]
-    val s900: Int
+    val s800: Int
         get() = this.allShades[10]
-    val s1000: Int
+    val s900: Int
         get() = this.allShades[11]
+    val s1000: Int
+        get() = this.allShades[12]
 
     companion object {
-        val SHADE_KEYS = listOf(10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
+        val SHADE_KEYS = listOf(0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
     }
 }
 
@@ -381,9 +105,20 @@
         "instead")
 class ColorScheme(
     @ColorInt val seed: Int,
-    val darkTheme: Boolean,
-    val style: Style = Style.TONAL_SPOT
+    val isDark: Boolean,
+    val style: Style,
+    val contrastLevel: Double
 ) {
+    var materialScheme: DynamicScheme
+
+    private val proposedSeedHct: Hct = Hct.fromInt(seed)
+    private val seedHct: Hct = Hct.fromInt(if (seed == Color.TRANSPARENT) {
+        GOOGLE_BLUE
+    } else if (style != Style.CONTENT && proposedSeedHct.chroma < 5) {
+        GOOGLE_BLUE
+    } else {
+        seed
+    })
 
     val accent1: TonalPalette
     val accent2: TonalPalette
@@ -395,62 +130,49 @@
 
     @JvmOverloads
     constructor(
+        @ColorInt seed: Int,
+        darkTheme: Boolean,
+        style: Style
+    ) : this(seed, darkTheme, style, 0.5)
+
+    @JvmOverloads
+    constructor(
         wallpaperColors: WallpaperColors,
         darkTheme: Boolean,
         style: Style = Style.TONAL_SPOT
     ) : this(getSeedColor(wallpaperColors, style != Style.CONTENT), darkTheme, style)
 
-    val allHues: List<TonalPalette>
-        get() {
-            return listOf(accent1, accent2, accent3, neutral1, neutral2)
-        }
-
-    val allAccentColors: List<Int>
-        get() {
-            val allColors = mutableListOf<Int>()
-            allColors.addAll(accent1.allShades)
-            allColors.addAll(accent2.allShades)
-            allColors.addAll(accent3.allShades)
-            return allColors
-        }
-
-    val allNeutralColors: List<Int>
-        get() {
-            val allColors = mutableListOf<Int>()
-            allColors.addAll(neutral1.allShades)
-            allColors.addAll(neutral2.allShades)
-            return allColors
-        }
-
     val backgroundColor
-        get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1.s700 else neutral1.s10, 0xFF)
+        get() = ColorUtils.setAlphaComponent(if (isDark) neutral1.s700 else neutral1.s10, 0xFF)
 
     val accentColor
-        get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1.s100 else accent1.s500, 0xFF)
+        get() = ColorUtils.setAlphaComponent(if (isDark) accent1.s100 else accent1.s500, 0xFF)
 
     init {
-        val proposedSeedCam = Cam.fromInt(seed)
-        val seedArgb =
-            if (seed == Color.TRANSPARENT) {
-                GOOGLE_BLUE
-            } else if (style != Style.CONTENT && proposedSeedCam.chroma < 5) {
-                GOOGLE_BLUE
-            } else {
-                seed
-            }
+        materialScheme = when (style) {
+            Style.SPRITZ -> SchemeNeutral(seedHct, isDark, contrastLevel)
+            Style.TONAL_SPOT -> SchemeTonalSpot(seedHct, isDark, contrastLevel)
+            Style.VIBRANT -> SchemeVibrant(seedHct, isDark, contrastLevel)
+            Style.EXPRESSIVE -> SchemeExpressive(seedHct, isDark, contrastLevel)
+            Style.RAINBOW -> SchemeRainbow(seedHct, isDark, contrastLevel)
+            Style.FRUIT_SALAD -> SchemeFruitSalad(seedHct, isDark, contrastLevel)
+            Style.CONTENT -> SchemeContent(seedHct, isDark, contrastLevel)
+            Style.MONOCHROMATIC -> SchemeMonochrome(seedHct, isDark, contrastLevel)
 
-        accent1 = TonalPalette(style.coreSpec.a1, seedArgb)
-        accent2 = TonalPalette(style.coreSpec.a2, seedArgb)
-        accent3 = TonalPalette(style.coreSpec.a3, seedArgb)
-        neutral1 = TonalPalette(style.coreSpec.n1, seedArgb)
-        neutral2 = TonalPalette(style.coreSpec.n2, seedArgb)
+            // SystemUI Schemes
+            Style.CLOCK -> SchemeClock(seedHct, isDark, contrastLevel)
+            Style.CLOCK_VIBRANT -> SchemeClockVibrant(seedHct, isDark, contrastLevel)
+        }
+
+        accent1 = TonalPalette(materialScheme.primaryPalette)
+        accent2 = TonalPalette(materialScheme.secondaryPalette)
+        accent3 = TonalPalette(materialScheme.tertiaryPalette)
+        neutral1 = TonalPalette(materialScheme.neutralPalette)
+        neutral2 = TonalPalette(materialScheme.neutralVariantPalette)
     }
 
-    val shadeCount
-        get() = this.accent1.allShades.size
-
     val seedTone: Float
-        get() = 1000f - CamUtils.lstarFromInt(seed) * 10f
+        get() = 1000f - proposedSeedHct.tone.toFloat() * 10f
 
     override fun toString(): String {
         return "ColorScheme {\n" +
@@ -507,7 +229,7 @@
                             if (!filter) {
                                 true
                             } else {
-                                Cam.fromInt(it).chroma >= MIN_CHROMA
+                                Hct.fromInt(it).chroma >= MIN_CHROMA
                             }
                         }
                         .toList()
@@ -519,15 +241,15 @@
 
             val intToProportion =
                 wallpaperColors.allColors.mapValues { it.value.toDouble() / totalPopulation }
-            val intToCam = wallpaperColors.allColors.mapValues { Cam.fromInt(it.key) }
+            val intToHct = wallpaperColors.allColors.mapValues { Hct.fromInt(it.key) }
 
             // Get an array with 360 slots. A slot contains the percentage of colors with that hue.
-            val hueProportions = huePopulations(intToCam, intToProportion, filter)
+            val hueProportions = huePopulations(intToHct, intToProportion, filter)
             // Map each color to the percentage of the image with its hue.
             val intToHueProportion =
                 wallpaperColors.allColors.mapValues {
-                    val cam = intToCam[it.key]!!
-                    val hue = cam.hue.roundToInt()
+                    val hct = intToHct[it.key]!!
+                    val hue = hct.hue.roundToInt()
                     var proportion = 0.0
                     for (i in hue - 15..hue + 15) {
                         proportion += hueProportions[wrapDegrees(i)]
@@ -537,18 +259,18 @@
             // Remove any inappropriate seed colors. For example, low chroma colors look grayscale
             // raising their chroma will turn them to a much louder color that may not have been
             // in the image.
-            val filteredIntToCam =
-                if (!filter) intToCam
+            val filteredIntToHct =
+                if (!filter) intToHct
                 else
-                    (intToCam.filter {
-                        val cam = it.value
+                    (intToHct.filter {
+                        val hct = it.value
                         val proportion = intToHueProportion[it.key]!!
-                        cam.chroma >= MIN_CHROMA &&
+                        hct.chroma >= MIN_CHROMA &&
                             (totalPopulationMeaningless || proportion > 0.01)
                     })
             // Sort the colors by score, from high to low.
             val intToScoreIntermediate =
-                filteredIntToCam.mapValues { score(it.value, intToHueProportion[it.key]!!) }
+                filteredIntToHct.mapValues { score(it.value, intToHueProportion[it.key]!!) }
             val intToScore = intToScoreIntermediate.entries.toMutableList()
             intToScore.sortByDescending { it.value }
 
@@ -564,8 +286,8 @@
                     val int = entry.key
                     val existingSeedNearby =
                         seeds.find {
-                            val hueA = intToCam[int]!!.hue
-                            val hueB = intToCam[it]!!.hue
+                            val hueA = intToHct[int]!!.hue
+                            val hueB = intToHct[it]!!.hue
                             hueDiff(hueA, hueB) < i
                         } != null
                     if (existingSeedNearby) {
@@ -600,30 +322,16 @@
             }
         }
 
-        public fun wrapDegreesDouble(degrees: Double): Double {
-            return when {
-                degrees < 0 -> {
-                    (degrees % 360) + 360
-                }
-                degrees >= 360 -> {
-                    degrees % 360
-                }
-                else -> {
-                    degrees
-                }
-            }
-        }
-
-        private fun hueDiff(a: Float, b: Float): Float {
+        private fun hueDiff(a: Double, b: Double): Double {
             return 180f - ((a - b).absoluteValue - 180f).absoluteValue
         }
 
         private fun stringForColor(color: Int): String {
             val width = 4
-            val hct = Cam.fromInt(color)
+            val hct = Hct.fromInt(color)
             val h = "H${hct.hue.roundToInt().toString().padEnd(width)}"
             val c = "C${hct.chroma.roundToInt().toString().padEnd(width)}"
-            val t = "T${CamUtils.lstarFromInt(color).roundToInt().toString().padEnd(width)}"
+            val t = "T${hct.tone.roundToInt().toString().padEnd(width)}"
             val hex = Integer.toHexString(color and 0xffffff).padStart(6, '0').uppercase()
             return "$h$c$t = #$hex"
         }
@@ -633,16 +341,16 @@
                 colors.map { stringForColor(it) }.joinToString(separator = "\n") { it }
         }
 
-        private fun score(cam: Cam, proportion: Double): Double {
+        private fun score(hct: Hct, proportion: Double): Double {
             val proportionScore = 0.7 * 100.0 * proportion
             val chromaScore =
-                if (cam.chroma < ACCENT1_CHROMA) 0.1 * (cam.chroma - ACCENT1_CHROMA)
-                else 0.3 * (cam.chroma - ACCENT1_CHROMA)
+                if (hct.chroma < ACCENT1_CHROMA) 0.1 * (hct.chroma - ACCENT1_CHROMA)
+                else 0.3 * (hct.chroma - ACCENT1_CHROMA)
             return chromaScore + proportionScore
         }
 
         private fun huePopulations(
-            camByColor: Map<Int, Cam>,
+            hctByColor: Map<Int, Hct>,
             populationByColor: Map<Int, Double>,
             filter: Boolean = true
         ): List<Double> {
@@ -650,9 +358,9 @@
 
             for (entry in populationByColor.entries) {
                 val population = populationByColor[entry.key]!!
-                val cam = camByColor[entry.key]!!
-                val hue = cam.hue.roundToInt() % 360
-                if (filter && cam.chroma <= MIN_CHROMA) {
+                val hct = hctByColor[entry.key]!!
+                val hue = hct.hue.roundToInt() % 360
+                if (filter && hct.chroma <= MIN_CHROMA) {
                     continue
                 }
                 huePopulation[hue] = huePopulation[hue] + population
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClock.java b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClock.java
new file mode 100644
index 0000000..4747cc5
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClock.java
@@ -0,0 +1,55 @@
+/*
+ * 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.monet;
+
+import static com.google.ux.material.libmonet.utils.MathUtils.clampDouble;
+
+import static java.lang.Double.max;
+
+import com.google.ux.material.libmonet.hct.Hct;
+import com.google.ux.material.libmonet.palettes.TonalPalette;
+import com.google.ux.material.libmonet.scheme.DynamicScheme;
+import com.google.ux.material.libmonet.scheme.Variant;
+
+public class SchemeClock extends DynamicScheme {
+    public SchemeClock(Hct sourceColorHct, boolean isDark, double contrastLevel) {
+        super(
+                sourceColorHct,
+                Variant.MONOCHROME,
+                isDark,
+                contrastLevel,
+                /*primary*/
+                TonalPalette.fromHueAndChroma(
+                        /*hue*/ sourceColorHct.getHue(),
+                        /*chroma*/ max(sourceColorHct.getChroma(), 20)
+                ),
+                /*secondary*/
+                TonalPalette.fromHueAndChroma(
+                        /*hue*/ sourceColorHct.getHue() + 10.0,
+                        /*chroma*/ clampDouble(17, 40, sourceColorHct.getChroma() * 0.85)
+                ),
+                /*tertiary*/
+                TonalPalette.fromHueAndChroma(
+                        /*hue*/ sourceColorHct.getHue() + 20.0,
+                        /*chroma*/ max(sourceColorHct.getChroma() + 20, 50)
+                ),
+
+                //not used
+                TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0),
+                TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0));
+    }
+}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClockVibrant.java b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClockVibrant.java
new file mode 100644
index 0000000..fb5e972
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClockVibrant.java
@@ -0,0 +1,53 @@
+/*
+ * 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.monet;
+
+import static java.lang.Double.max;
+
+import com.google.ux.material.libmonet.hct.Hct;
+import com.google.ux.material.libmonet.palettes.TonalPalette;
+import com.google.ux.material.libmonet.scheme.DynamicScheme;
+import com.google.ux.material.libmonet.scheme.Variant;
+
+public class SchemeClockVibrant extends DynamicScheme {
+    public SchemeClockVibrant(Hct sourceColorHct, boolean isDark, double contrastLevel) {
+        super(
+                sourceColorHct,
+                Variant.MONOCHROME,
+                isDark,
+                contrastLevel,
+                /*primary*/
+                TonalPalette.fromHueAndChroma(
+                        /*hue*/ sourceColorHct.getHue(),
+                        /*chroma*/ max(sourceColorHct.getChroma(), 70)
+                ),
+                /*secondary*/
+                TonalPalette.fromHueAndChroma(
+                        /*hue*/ sourceColorHct.getHue() + 20.0,
+                        /*chroma*/ max(sourceColorHct.getChroma(), 70)
+                ),
+                /*tertiary*/
+                TonalPalette.fromHueAndChroma(
+                        /*hue*/ sourceColorHct.getHue() + 60.0,
+                        /*chroma*/ max(sourceColorHct.getChroma(), 70)
+                ),
+
+                //not used
+                TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0),
+                TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0));
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 24c651f3..a9541d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -61,6 +61,7 @@
     private val testScope = kosmos.testScope
     private val repository by lazy { kosmos.fakeKeyguardRepository }
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
     private val commandQueue by lazy { FakeCommandQueue() }
     private val bouncerRepository = FakeKeyguardBouncerRepository()
     private val shadeRepository = FakeShadeRepository()
@@ -79,6 +80,7 @@
             shadeRepository = shadeRepository,
             keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
             sceneInteractorProvider = { sceneInteractor },
+            fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 6e15c51..fc604aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -348,6 +348,48 @@
         }
 
     @Test
+    fun alpha_shadeClosedOverLockscreen_isOne() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha(viewState))
+
+            // Transition to the lockscreen.
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope,
+            )
+
+            // Open the shade.
+            shadeRepository.setQsExpansion(1f)
+            assertThat(alpha).isEqualTo(0f)
+
+            // Close the shade, alpha returns to 1.
+            shadeRepository.setQsExpansion(0f)
+            assertThat(alpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun alpha_shadeClosedOverDream_isZero() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha(viewState))
+
+            // Transition to dreaming.
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DREAMING,
+                testScope,
+            )
+
+            // Open the shade.
+            shadeRepository.setQsExpansion(1f)
+            assertThat(alpha).isEqualTo(0f)
+
+            // Close the shade, alpha is still 0 since we're not on the lockscreen.
+            shadeRepository.setQsExpansion(0f)
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
     fun alpha_idleOnOccluded_isZero() =
         testScope.runTest {
             val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index ad1cef1..751ac1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,7 +26,8 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -47,7 +48,7 @@
     fun setup() {
         with(kosmos) {
             fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
-            overrideResource(R.bool.config_use_split_notification_shade, false)
+            shadeRepository.setShadeMode(ShadeMode.Single)
             underTest = lockscreenContentViewModel
         }
     }
@@ -92,10 +93,10 @@
     fun areNotificationsVisible_splitShadeTrue_true() =
         with(kosmos) {
             testScope.runTest {
-                overrideResource(R.bool.config_use_split_notification_shade, true)
+                shadeRepository.setShadeMode(ShadeMode.Split)
                 kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
 
-                assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+                assertThat(underTest.areNotificationsVisible).isTrue()
             }
         }
     @Test
@@ -103,7 +104,7 @@
         with(kosmos) {
             testScope.runTest {
                 kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
-                assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+                assertThat(underTest.areNotificationsVisible).isTrue()
             }
         }
 
@@ -112,7 +113,34 @@
         with(kosmos) {
             testScope.runTest {
                 kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
-                assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
+                assertThat(underTest.areNotificationsVisible).isFalse()
+            }
+        }
+
+    @Test
+    fun shouldUseSplitNotificationShade_withConfigTrue_true() =
+        with(kosmos) {
+            testScope.runTest {
+                shadeRepository.setShadeMode(ShadeMode.Split)
+                assertThat(underTest.shouldUseSplitNotificationShade).isTrue()
+            }
+        }
+
+    @Test
+    fun shouldUseSplitNotificationShade_withConfigFalse_false() =
+        with(kosmos) {
+            testScope.runTest {
+                shadeRepository.setShadeMode(ShadeMode.Single)
+                assertThat(underTest.shouldUseSplitNotificationShade).isFalse()
+            }
+        }
+
+    @Test
+    fun sceneKey() =
+        with(kosmos) {
+            testScope.runTest {
+                shadeRepository.setShadeMode(ShadeMode.Single)
+                assertThat(underTest.shouldUseSplitNotificationShade).isFalse()
             }
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 183a58a..be63301 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
+import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.settings.FakeGlobalSettings
 import com.android.systemui.util.time.FakeSystemClock
@@ -97,6 +98,12 @@
         return entry
     }
 
+    private fun createFsiHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry {
+        val entry = testableHeadsUpManager!!.createHeadsUpEntry()
+        entry.setEntry(createFullScreenIntentEntry(id, mContext))
+        return entry
+    }
+
     @Test
     fun testUpdate_isShowing_runsRunnable() {
         // Entry is showing
@@ -238,4 +245,68 @@
         // Next entry is shown
         Truth.assertThat(mAvalancheController.headsUpEntryShowing).isEqualTo(nextEntry)
     }
+
+    @Test
+    fun testGetDurationMs_lastEntry_useAutoDismissTime() {
+        // Entry is showing
+        val showingEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // Nothing is next
+        mAvalancheController.clearNext()
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(5000)
+    }
+
+    @Test
+    fun testGetDurationMs_nextEntryLowerPriority_500() {
+        // Entry is showing
+        val showingEntry = createFsiHeadsUpEntry(id = 1)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // There's another entry waiting to show next
+        val nextEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+        // Next entry has lower priority
+        Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(1)
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(5000)
+    }
+
+    @Test
+    fun testGetDurationMs_nextEntrySamePriority_1000() {
+        // Entry is showing
+        val showingEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // There's another entry waiting to show next
+        val nextEntry = createHeadsUpEntry(id = 1)
+        mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+        // Same priority
+        Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(0)
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(1000)
+    }
+
+    @Test
+    fun testGetDurationMs_nextEntryHigherPriority_500() {
+        // Entry is showing
+        val showingEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // There's another entry waiting to show next
+        val nextEntry = createFsiHeadsUpEntry(id = 1)
+        mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+        // Next entry has higher priority
+        Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(-1)
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(500)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 830bcef..ed0d272 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -117,21 +117,6 @@
         return HeadsUpManagerTestUtil.createEntry(id, notif);
     }
 
-    private PendingIntent createFullScreenIntent() {
-        return PendingIntent.getActivity(
-                getContext(), 0, new Intent(getContext(), this.getClass()),
-                PendingIntent.FLAG_MUTABLE_UNAUDITED);
-    }
-
-    private NotificationEntry createFullScreenIntentEntry(int id) {
-        final Notification notif = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setFullScreenIntent(createFullScreenIntent(), /* highPriority */ true)
-                .build();
-        return HeadsUpManagerTestUtil.createEntry(id, notif);
-    }
-
-
     private void useAccessibilityTimeout(boolean use) {
         if (use) {
             doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
@@ -239,7 +224,8 @@
     @Test
     public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+        final NotificationEntry notifEntry =
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
 
         // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
         hum.showNotification(notifEntry);
@@ -254,7 +240,8 @@
     @Test
     public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+        final NotificationEntry notifEntry =
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
 
         // Add notifEntry to ANM mAlertEntries map and make it unpinned
         hum.showNotification(notifEntry);
@@ -443,7 +430,8 @@
     @Test
     public void testIsSticky_hasFullScreenIntent_true() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+        final NotificationEntry notifEntry =
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
 
         hum.showNotification(notifEntry);
 
@@ -554,7 +542,8 @@
 
         // Needs full screen intent in order to be pinned
         final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
-        entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0));
+        entryToPin.setEntry(
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext));
 
         // Note: the standard way to show a notification would be calling showNotification rather
         // than onAlertEntryAdded. However, in practice showNotification in effect adds
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
index c70b03b..bda8619 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Intent;
 import android.os.UserHandle;
 
 import android.content.Context;
@@ -67,4 +69,16 @@
         return new NotificationEntryBuilder().setSbn(
                 HeadsUpManagerTestUtil.createSbn(id, context)).build();
     }
+
+    protected static NotificationEntry createFullScreenIntentEntry(int id, Context context) {
+        final PendingIntent intent = PendingIntent.getActivity(
+                context, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
+
+        final Notification notif = new Notification.Builder(context, "")
+                .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+                .setFullScreenIntent(intent, /* highPriority */ true)
+                .build();
+        return HeadsUpManagerTestUtil.createEntry(id, notif);
+    }
 }
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index 5fe74aa..2d63c8d 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -26,7 +26,7 @@
         android:paddingHorizontal="16dp"
         android:paddingVertical="16dp"
         android:visibility="visible"
-        app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
         app:layout_constraintStart_toStartOf="@+id/leftGuideline"
         app:layout_constraintTop_toTopOf="@+id/topGuideline" />
@@ -59,7 +59,7 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:fillViewport="true"
-        android:padding="16dp"
+        android:padding="24dp"
         app:layout_constrainedHeight="true"
         app:layout_constrainedWidth="true"
         app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
@@ -82,20 +82,20 @@
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent" />
 
-            <LinearLayout
-                android:id="@+id/customized_view_container"
-                android:layout_width="wrap_content"
+            <TextView
+                android:id="@+id/logo_description"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:gravity="center_vertical"
-                android:orientation="vertical"
-                android:paddingHorizontal="0dp"
-                android:visibility="gone"
-                app:layout_constraintBottom_toBottomOf="parent"
+                android:ellipsize="marquee"
+                android:gravity="@integer/biometric_dialog_text_gravity"
+                android:marqueeRepeatLimit="1"
+                android:singleLine="true"
+                android:textAlignment="viewStart"
+                android:paddingLeft="8dp"
+                app:layout_constraintBottom_toBottomOf="@+id/logo"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintHorizontal_bias="0.0"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/subtitle"
-                app:layout_constraintVertical_bias="0.0" />
+                app:layout_constraintStart_toEndOf="@+id/logo"
+                app:layout_constraintTop_toTopOf="@+id/logo" />
 
             <Space
                 android:id="@+id/space_above_content"
@@ -108,6 +108,7 @@
                 style="@style/TextAppearance.AuthCredential.Title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
                 android:gravity="@integer/biometric_dialog_text_gravity"
                 android:paddingHorizontal="0dp"
                 android:textAlignment="viewStart"
@@ -124,6 +125,7 @@
                 style="@style/TextAppearance.AuthCredential.Subtitle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
                 android:gravity="@integer/biometric_dialog_text_gravity"
                 android:paddingHorizontal="0dp"
                 android:textAlignment="viewStart"
@@ -133,6 +135,21 @@
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/title" />
 
+            <LinearLayout
+                android:id="@+id/customized_view_container"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="vertical"
+                android:paddingHorizontal="0dp"
+                android:visibility="gone"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintHorizontal_bias="0.0"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/subtitle"
+                app:layout_constraintVertical_bias="0.0" />
+
             <TextView
                 android:id="@+id/description"
                 style="@style/TextAppearance.AuthCredential.Description"
@@ -148,20 +165,6 @@
                 app:layout_constraintTop_toBottomOf="@+id/subtitle"
                 app:layout_constraintVertical_bias="0.0" />
 
-            <TextView
-                android:id="@+id/logo_description"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:ellipsize="marquee"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                android:marqueeRepeatLimit="1"
-                android:singleLine="true"
-                android:textAlignment="viewStart"
-                android:paddingLeft="8dp"
-                app:layout_constraintBottom_toBottomOf="@+id/logo"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toEndOf="@+id/logo"
-                app:layout_constraintTop_toTopOf="@+id/logo" />
 
             <androidx.constraintlayout.widget.Barrier
                 android:id="@+id/contentBarrier"
@@ -178,7 +181,7 @@
         android:id="@+id/indicator"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
+        android:layout_marginTop="24dp"
         android:accessibilityLiveRegion="polite"
         android:fadingEdge="horizontal"
         android:gravity="center_horizontal"
@@ -192,80 +195,15 @@
         app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
         app:layout_constraintVertical_bias="0.0" />
 
-    <!-- Negative Button, reserved for app -->
-    <Button
-        android:id="@+id/button_negative"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
-        app:layout_constraintStart_toStartOf="@+id/scrollView" />
-
-    <!-- Cancel Button, replaces negative button when biometric is accepted -->
-    <Button
-        android:id="@+id/button_cancel"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:text="@string/cancel"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
-        app:layout_constraintStart_toStartOf="@+id/scrollView" />
-
-    <!-- "Use Credential" Button, replaces if device credential is allowed -->
-    <Button
-        android:id="@+id/button_use_credential"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
-        app:layout_constraintStart_toStartOf="@+id/scrollView" />
-
-    <!-- Positive Button -->
-    <Button
-        android:id="@+id/button_confirm"
-        style="@*android:style/Widget.DeviceDefault.Button.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginRight="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:text="@string/biometric_dialog_confirm"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
-        app:layout_constraintEnd_toEndOf="@+id/scrollView"
-        tools:visibility="invisible" />
-
-    <!-- Try Again Button -->
-    <Button
-        android:id="@+id/button_try_again"
-        style="@*android:style/Widget.DeviceDefault.Button.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginRight="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:text="@string/biometric_dialog_try_again"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
-        app:layout_constraintEnd_toEndOf="@+id/scrollView" />
+    <include
+        android:id="@+id/button_bar"
+        layout="@layout/biometric_prompt_button_bar"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toTopOf="@id/bottomGuideline"
+        app:layout_constraintEnd_toEndOf="@id/scrollView"
+        app:layout_constraintStart_toStartOf="@id/scrollView"
+        app:layout_constraintTop_toBottomOf="@id/scrollView" />
 
     <!-- Guidelines for setting panel border -->
     <androidx.constraintlayout.widget.Barrier
@@ -282,7 +220,7 @@
         android:layout_height="wrap_content"
         app:barrierAllowsGoneWidgets="false"
         app:barrierDirection="top"
-        app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+        app:constraint_referenced_ids="button_bar" />
 
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/leftGuideline"
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index 5b30dfb..329fc46 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -23,92 +23,29 @@
         android:clickable="true"
         android:clipToOutline="true"
         android:importantForAccessibility="no"
-        android:paddingHorizontal="16dp"
-        android:paddingVertical="16dp"
         android:visibility="visible"
-        app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
         app:layout_constraintStart_toStartOf="@+id/leftGuideline"
         app:layout_constraintTop_toTopOf="@+id/topBarrier" />
 
-    <Button
-        android:id="@+id/button_negative"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel" />
-
-    <Button
-        android:id="@+id/button_cancel"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:text="@string/cancel"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel" />
-
-    <Button
-        android:id="@+id/button_use_credential"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel" />
-
-    <Button
-        android:id="@+id/button_confirm"
-        style="@*android:style/Widget.DeviceDefault.Button.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginRight="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:text="@string/biometric_dialog_confirm"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintEnd_toEndOf="@+id/panel"
-        tools:visibility="invisible" />
-
-    <Button
-        android:id="@+id/button_try_again"
-        style="@*android:style/Widget.DeviceDefault.Button.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginRight="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:text="@string/biometric_dialog_try_again"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintEnd_toEndOf="@+id/panel" />
-
-    <!-- Negative Button, reserved for app -->
+    <include
+        layout="@layout/biometric_prompt_button_bar"
+        android:id="@+id/button_bar"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        app:layout_constraintBottom_toTopOf="@id/bottomGuideline"
+        app:layout_constraintEnd_toEndOf="@id/panel"
+        app:layout_constraintStart_toStartOf="@id/panel"/>
 
     <ScrollView
         android:id="@+id/scrollView"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:fillViewport="true"
-        android:padding="16dp"
+        android:paddingBottom="36dp"
+        android:paddingHorizontal="24dp"
+        android:paddingTop="24dp"
         app:layout_constrainedHeight="true"
         app:layout_constrainedWidth="true"
         app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
@@ -134,18 +71,19 @@
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent" />
 
-            <LinearLayout
-                android:id="@+id/customized_view_container"
-                android:layout_width="wrap_content"
+            <TextView
+                android:id="@+id/logo_description"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:gravity="center_vertical"
-                android:orientation="vertical"
-                android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
-                android:visibility="gone"
-                app:layout_constraintBottom_toBottomOf="parent"
+                android:ellipsize="marquee"
+                android:gravity="@integer/biometric_dialog_text_gravity"
+                android:marqueeRepeatLimit="1"
+                android:singleLine="true"
+                android:paddingTop="16dp"
+                app:layout_constraintBottom_toTopOf="@+id/title"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+                app:layout_constraintTop_toBottomOf="@+id/logo" />
 
             <Space
                 android:id="@+id/space_above_content"
@@ -159,6 +97,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:gravity="@integer/biometric_dialog_text_gravity"
+                android:paddingTop="16dp"
                 app:layout_constraintBottom_toTopOf="@+id/subtitle"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
@@ -170,23 +109,24 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:gravity="@integer/biometric_dialog_text_gravity"
+                android:paddingTop="16dp"
                 app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/title" />
 
-            <TextView
-                android:id="@+id/logo_description"
-                android:layout_width="match_parent"
+            <LinearLayout
+                android:id="@+id/customized_view_container"
+                android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:ellipsize="marquee"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                android:marqueeRepeatLimit="1"
-                android:singleLine="true"
-                app:layout_constraintBottom_toTopOf="@+id/title"
+                android:gravity="center_vertical"
+                android:orientation="vertical"
+                android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
+                android:visibility="gone"
+                app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/logo" />
+                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
 
             <TextView
                 android:id="@+id/description"
@@ -215,7 +155,7 @@
         android:id="@+id/indicator"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
+        android:layout_marginTop="24dp"
         android:accessibilityLiveRegion="polite"
         android:fadingEdge="horizontal"
         android:gravity="center_horizontal"
@@ -247,7 +187,7 @@
         android:layout_height="wrap_content"
         app:barrierAllowsGoneWidgets="false"
         app:barrierDirection="top"
-        app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+        app:constraint_referenced_ids="button_bar" />
 
     <!-- Guidelines for setting panel border -->
     <androidx.constraintlayout.widget.Guideline
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index cb638ee..bcc7bca 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -69,7 +69,7 @@
         tools:minHeight="100dp"
         tools:minWidth="100dp" />
 
-    <com.android.systemui.screenshot.CropView
+    <com.android.systemui.screenshot.scroll.CropView
         android:id="@+id/crop_view"
         android:layout_width="0px"
         android:layout_height="0px"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
new file mode 100644
index 0000000..810c7433
--- /dev/null
+++ b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <!-- Negative Button, reserved for app -->
+    <Button
+        android:id="@+id/button_negative"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="24dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <!-- Cancel Button, replaces negative button when biometric is accepted -->
+    <Button
+        android:id="@+id/button_cancel"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="24dp"
+        android:text="@string/cancel"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <!-- "Use Credential" Button, replaces if device credential is allowed -->
+    <Button
+        android:id="@+id/button_use_credential"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="24dp"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <!-- Positive Button -->
+    <Button
+        android:id="@+id/button_confirm"
+        style="@*android:style/Widget.DeviceDefault.Button.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginRight="24dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:text="@string/biometric_dialog_confirm"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <!-- Try Again Button -->
+    <Button
+        android:id="@+id/button_try_again"
+        style="@*android:style/Widget.DeviceDefault.Button.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginRight="24dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:text="@string/biometric_dialog_try_again"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index 74292b4..6391813 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -23,13 +23,189 @@
         android:clickable="true"
         android:clipToOutline="true"
         android:importantForAccessibility="no"
-        android:paddingHorizontal="16dp"
-        android:paddingVertical="16dp"
         android:visibility="visible"
-        app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
-        app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
-        app:layout_constraintStart_toStartOf="@+id/leftGuideline"
-        app:layout_constraintTop_toTopOf="@+id/topBarrier" />
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@id/rightGuideline"
+        app:layout_constraintStart_toStartOf="@id/leftGuideline"
+        app:layout_constraintTop_toTopOf="@id/topBarrier" />
+
+    <include
+        android:id="@+id/button_bar"
+        layout="@layout/biometric_prompt_button_bar"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        app:layout_constraintBottom_toTopOf="@id/bottomGuideline"
+        app:layout_constraintEnd_toEndOf="@id/panel"
+        app:layout_constraintStart_toStartOf="@id/panel" />
+
+    <ScrollView
+        android:id="@+id/scrollView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fillViewport="true"
+        android:paddingBottom="36dp"
+        android:paddingHorizontal="24dp"
+        android:paddingTop="24dp"
+        app:layout_constrainedHeight="true"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
+        app:layout_constraintEnd_toEndOf="@id/rightGuideline"
+        app:layout_constraintStart_toStartOf="@id/leftGuideline"
+        app:layout_constraintTop_toTopOf="@+id/topGuideline"
+        app:layout_constraintVertical_bias="1.0">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/innerConstraint"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                android:id="@+id/logo"
+                android:layout_width="@dimen/biometric_auth_icon_size"
+                android:layout_height="@dimen/biometric_auth_icon_size"
+                android:layout_gravity="center"
+                android:scaleType="fitXY"
+                app:layout_constraintBottom_toTopOf="@+id/logo_description"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:visibility="visible" />
+
+            <TextView
+                android:id="@+id/logo_description"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="@integer/biometric_dialog_text_gravity"
+                android:singleLine="true"
+                app:layout_constraintBottom_toTopOf="@+id/title"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/logo" />
+
+            <Space
+                android:id="@+id/space_above_content"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/biometric_prompt_space_above_content"
+                android:visibility="gone" />
+
+            <TextView
+                android:id="@+id/title"
+                style="@style/TextAppearance.AuthCredential.Title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="@integer/biometric_dialog_text_gravity"
+                android:paddingTop="16dp"
+                app:layout_constraintBottom_toTopOf="@+id/subtitle"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/logo_description" />
+
+            <TextView
+                android:id="@+id/subtitle"
+                style="@style/TextAppearance.AuthCredential.Subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="@integer/biometric_dialog_text_gravity"
+                android:paddingTop="16dp"
+                app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/title" />
+
+            <LinearLayout
+                android:id="@+id/customized_view_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="vertical"
+                android:visibility="gone"
+                android:paddingTop="8dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+            <TextView
+                android:id="@+id/description"
+                style="@style/TextAppearance.AuthCredential.Description"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="@integer/biometric_dialog_text_gravity"
+                android:paddingTop="16dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+            <androidx.constraintlayout.widget.Barrier
+                android:id="@+id/contentBarrier"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:barrierAllowsGoneWidgets="false"
+                app:barrierDirection="top"
+                app:constraint_referenced_ids="description, customized_view_container" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </ScrollView>
+
+    <TextView
+        android:id="@+id/indicator"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="24dp"
+        android:accessibilityLiveRegion="polite"
+        android:fadingEdge="horizontal"
+        android:gravity="center_horizontal"
+        android:scrollHorizontally="true"
+        app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel"
+        app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
+        app:layout_constraintVertical_bias="0.0" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/topBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierAllowsGoneWidgets="false"
+        app:barrierDirection="top"
+        app:constraint_referenced_ids="scrollView" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/buttonBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierAllowsGoneWidgets="false"
+        app:barrierDirection="top"
+        app:constraint_referenced_ids="button_bar" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/leftGuideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_begin="0dp" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/rightGuideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_end="0dp" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/bottomGuideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/topGuideline"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.25" />
 
     <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
         android:id="@+id/biometric_icon"
@@ -54,249 +230,4 @@
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
         app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
 
-    <ScrollView
-        android:id="@+id/scrollView"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:fillViewport="true"
-        android:padding="16dp"
-        app:layout_constrainedHeight="true"
-        app:layout_constrainedWidth="true"
-        app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
-        app:layout_constraintEnd_toEndOf="@id/rightGuideline"
-        app:layout_constraintStart_toStartOf="@id/leftGuideline"
-        app:layout_constraintTop_toTopOf="@+id/topGuideline"
-        app:layout_constraintVertical_bias="1.0">
-
-        <androidx.constraintlayout.widget.ConstraintLayout
-            android:id="@+id/innerConstraint"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <ImageView
-                android:id="@+id/logo"
-                android:layout_width="@dimen/biometric_auth_icon_size"
-                android:layout_height="@dimen/biometric_auth_icon_size"
-                android:layout_gravity="center"
-                android:scaleType="fitXY"
-                android:visibility="visible"
-                app:layout_constraintBottom_toTopOf="@+id/logo_description"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toTopOf="parent" />
-
-            <LinearLayout
-                android:id="@+id/customized_view_container"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="center_vertical"
-                android:orientation="vertical"
-                android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
-                android:visibility="gone"
-                app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
-
-            <Space
-                android:id="@+id/space_above_content"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/biometric_prompt_space_above_content"
-                android:visibility="gone" />
-
-            <TextView
-                android:id="@+id/title"
-                style="@style/TextAppearance.AuthCredential.Title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                app:layout_constraintBottom_toTopOf="@+id/subtitle"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/logo_description" />
-
-            <TextView
-                android:id="@+id/subtitle"
-                style="@style/TextAppearance.AuthCredential.Subtitle"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/title" />
-
-            <TextView
-                android:id="@+id/logo_description"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:ellipsize="marquee"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                android:marqueeRepeatLimit="1"
-                android:singleLine="true"
-                app:layout_constraintBottom_toTopOf="@+id/title"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/logo" />
-
-            <TextView
-                android:id="@+id/description"
-                style="@style/TextAppearance.AuthCredential.Description"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
-
-            <androidx.constraintlayout.widget.Barrier
-                android:id="@+id/contentBarrier"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                app:barrierAllowsGoneWidgets="false"
-                app:barrierDirection="top"
-                app:constraint_referenced_ids="description, customized_view_container" />
-
-        </androidx.constraintlayout.widget.ConstraintLayout>
-    </ScrollView>
-
-    <TextView
-        android:id="@+id/indicator"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:accessibilityLiveRegion="polite"
-        android:fadingEdge="horizontal"
-        android:gravity="center_horizontal"
-        android:marqueeRepeatLimit="marquee_forever"
-        android:scrollHorizontally="true"
-        android:textColor="@color/biometric_dialog_gray"
-        android:textSize="12sp"
-        app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
-        app:layout_constraintEnd_toEndOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel"
-        app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
-        app:layout_constraintVertical_bias="0.0" />
-
-    <!-- Negative Button, reserved for app -->
-    <Button
-        android:id="@+id/button_negative"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel" />
-
-    <!-- Cancel Button, replaces negative button when biometric is accepted -->
-    <Button
-        android:id="@+id/button_cancel"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:text="@string/cancel"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel" />
-
-    <!-- "Use Credential" Button, replaces if device credential is allowed -->
-    <Button
-        android:id="@+id/button_use_credential"
-        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginLeft="8dp"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel" />
-
-    <!-- Positive Button -->
-    <Button
-        android:id="@+id/button_confirm"
-        style="@*android:style/Widget.DeviceDefault.Button.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginRight="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:text="@string/biometric_dialog_confirm"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintEnd_toEndOf="@+id/panel"
-        tools:visibility="invisible" />
-
-    <!-- Try Again Button -->
-    <Button
-        android:id="@+id/button_try_again"
-        style="@*android:style/Widget.DeviceDefault.Button.Colored"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginBottom="8dp"
-        android:layout_marginRight="8dp"
-        android:ellipsize="end"
-        android:maxLines="2"
-        android:text="@string/biometric_dialog_try_again"
-        android:visibility="invisible"
-        app:layout_constraintBottom_toBottomOf="@+id/panel"
-        app:layout_constraintEnd_toEndOf="@+id/panel" />
-
-    <!-- Guidelines for setting panel border -->
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/topBarrier"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierAllowsGoneWidgets="false"
-        app:barrierDirection="top"
-        app:constraint_referenced_ids="scrollView" />
-
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/buttonBarrier"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierAllowsGoneWidgets="false"
-        app:barrierDirection="top"
-        app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/leftGuideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/rightGuideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/bottomGuideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/topGuideline"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.25171" />
-
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8a19c2e..4d207da 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -100,7 +100,7 @@
         app:layout_constraintStart_toStartOf="parent"
         android:transitionName="screenshot_preview_image"/>
 
-    <com.android.systemui.screenshot.CropView
+    <com.android.systemui.screenshot.scroll.CropView
         android:id="@+id/crop_view"
         android:layout_width="0px"
         android:layout_height="0px"
@@ -122,7 +122,7 @@
         tools:minHeight="100dp"
         tools:minWidth="100dp" />
 
-    <com.android.systemui.screenshot.MagnifierView
+    <com.android.systemui.screenshot.scroll.MagnifierView
         android:id="@+id/magnifier"
         android:visibility="invisible"
         android:layout_width="200dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b7eff38..2285550 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -898,8 +898,9 @@
     <dimen name="communal_tutorial_indicator_horizontal_offset">32dp</dimen>
 
     <!-- Size of the maximum radius for the enforced rounded rectangles on communal hub.
-        Keep it the same as in Launcher-->
-    <dimen name="communal_enforced_rounded_corner_max_radius">16dp</dimen>
+        Larger than the 16dp Launcher uses, to ensure consistency on the hub, where it's much more
+        obvious when corner radii differ.-->
+    <dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen>
 
     <!-- Width and height used to filter widgets displayed in the communal widget picker -->
     <dimen name="communal_widget_picker_desired_width">424dp</dimen>
@@ -1087,7 +1088,7 @@
     <dimen name="biometric_dialog_fingerprint_icon_height">80dp</dimen>
     <dimen name="biometric_dialog_button_negative_max_width">160dp</dimen>
     <dimen name="biometric_dialog_button_positive_max_width">136dp</dimen>
-    <dimen name="biometric_dialog_corner_size">4dp</dimen>
+    <dimen name="biometric_dialog_corner_size">28dp</dimen>
     <!-- Y translation when showing/dismissing the dialog-->
     <dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
     <dimen name="biometric_dialog_border_padding">4dp</dimen>
@@ -1967,4 +1968,7 @@
     <!-- Microns/ums (1000 um = 1mm) per pixel for the given device. If unspecified, UI that
          relies on this value will not be sized correctly. -->
     <item name="pixel_pitch" format="float" type="dimen">-1</item>
+
+    <!-- SliceView grid gutter for ANC Slice -->
+    <dimen name="abc_slice_grid_gutter">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e3a5e15..774bbe5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3358,7 +3358,7 @@
     <string name="microphone_blocked_dream_overlay_content_description">Microphone blocked</string>
 
     <!-- Content description for priority mode icon on dream [CHAR LIMIT=NONE] -->
-    <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
+    <string name="priority_mode_dream_overlay_content_description">Do not disturb</string>
 
     <!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
     <string name="assistant_attention_content_description">User presence is detected</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 69aa909..c08b083 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -77,7 +77,7 @@
     // settings is expanded.
     public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
     // Winscope tracing is enabled
-    public static final int SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1 << 12;
+    public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
     // The Assistant gesture should be constrained. It is up to the launcher implementation to
     // decide how to constrain it
     public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
@@ -148,7 +148,7 @@
             SYSUI_STATE_OVERVIEW_DISABLED,
             SYSUI_STATE_HOME_DISABLED,
             SYSUI_STATE_SEARCH_DISABLED,
-            SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION,
+            SYSUI_STATE_TRACING_ENABLED,
             SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
             SYSUI_STATE_BUBBLES_EXPANDED,
             SYSUI_STATE_DIALOG_SHOWING,
@@ -211,8 +211,8 @@
         if ((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0) {
             str.add("a11y_long_click");
         }
-        if ((flags & SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION) != 0) {
-            str.add("disable_gesture_split_invocation");
+        if ((flags & SYSUI_STATE_TRACING_ENABLED) != 0) {
+            str.add("tracing");
         }
         if ((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0) {
             str.add("asst_gesture_constrain");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 0bb5c17..fb331b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,8 +15,10 @@
  */
 package com.android.keyguard;
 
+import android.annotation.NonNull;
 import android.app.Presentation;
 import android.content.Context;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManager;
 import android.media.MediaRouter;
@@ -315,11 +317,11 @@
         }
 
         @Override
-        public void onStateChanged(int state) {
+        public void onDeviceStateChanged(@NonNull DeviceState state) {
             // When concurrent state ends, the display also turns off. This is enforced in various
             // ExtensionRearDisplayPresentationTest CTS tests. So, we don't need to invoke
             // hide() since that will happen through the onDisplayRemoved callback.
-            mIsInConcurrentDisplayState = state == mConcurrentState;
+            mIsInConcurrentDisplayState = state.getIdentifier() == mConcurrentState;
         }
 
         boolean isConcurrentDisplayActive(Display display) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8e98150..039a2e5 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -23,6 +23,7 @@
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
 import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
@@ -452,7 +453,7 @@
     private void updateLockIconLocation() {
         final float scaleFactor = mAuthController.getScaleFactor();
         final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
-        if (keyguardBottomAreaRefactor()) {
+        if (keyguardBottomAreaRefactor() || migrateClocksToBlueprint()) {
             mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
                     scaledPadding);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index da49201..c6716e4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -54,7 +54,7 @@
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
@@ -190,7 +190,7 @@
     private final NotificationShadeWindowController mNotificationShadeController;
     private final KeyguardStateController mKeyguardStateController;
     private final ShadeController mShadeController;
-    private final Lazy<ShadeViewController> mShadeViewController;
+    private final Lazy<PanelExpansionInteractor> mPanelExpansionInteractor;
     private final StatusBarWindowCallback mNotificationShadeCallback;
     private boolean mDismissNotificationShadeActionRegistered;
 
@@ -200,14 +200,14 @@
             NotificationShadeWindowController notificationShadeController,
             KeyguardStateController keyguardStateController,
             ShadeController shadeController,
-            Lazy<ShadeViewController> shadeViewController,
+            Lazy<PanelExpansionInteractor> panelExpansionInteractor,
             Optional<Recents> recentsOptional,
             DisplayTracker displayTracker) {
         mContext = context;
         mUserTracker = userTracker;
         mKeyguardStateController = keyguardStateController;
         mShadeController = shadeController;
-        mShadeViewController = shadeViewController;
+        mPanelExpansionInteractor = panelExpansionInteractor;
         mRecentsOptional = recentsOptional;
         mDisplayTracker = displayTracker;
         mReceiver = new SystemActionsBroadcastReceiver();
@@ -330,7 +330,7 @@
     private void registerOrUnregisterDismissNotificationShadeAction() {
         Assert.isMainThread();
 
-        if (mShadeViewController.get().isPanelExpanded()
+        if (mPanelExpansionInteractor.get().isPanelExpanded()
                 && !mKeyguardStateController.isShowing()) {
             if (!mDismissNotificationShadeActionRegistered) {
                 mA11yManager.registerSystemAction(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 9dd1454..0f4d63c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -302,6 +302,14 @@
         if (mEndAnimationCanceled || mController == null) {
             return;
         }
+
+        // If the animation is playing backwards, mStartSpec will be the final spec we would
+        // like to reach.
+        AnimationSpec spec = isReverse ? mStartSpec : mEndSpec;
+        mController.enableWindowMagnificationInternal(
+                spec.mScale, spec.mCenterX, spec.mCenterY,
+                mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
+
         if (mState == STATE_DISABLING) {
             mController.deleteWindowMagnification();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 8bd675c..99cdc01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -340,13 +340,19 @@
         mPanelInteractionDetector = panelInteractionDetector;
         mApplicationCoroutineScope = applicationCoroutineScope;
 
+        mPromptViewModel = promptViewModel;
         mTranslationY = getResources()
                 .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mBiometricCallback = new BiometricCallback();
 
+        mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
+        mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
+                config.mPromptInfo);
+
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        if (constraintBp()) {
+        if (constraintBp() && (Utils.isBiometricAllowed(config.mPromptInfo)
+                || mPromptViewModel.getShowBpWithoutIconForCredential().getValue())) {
             mLayout = (ConstraintLayout) layoutInflater.inflate(
                     R.layout.biometric_prompt_constraint_layout, this, false /* attachToRoot */);
         } else {
@@ -375,9 +381,7 @@
         mBackgroundExecutor = bgExecutor;
         mInteractionJankMonitor = jankMonitor;
         mPromptCredentialInteractor = credentialInteractor;
-        mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
         mCredentialViewModelProvider = credentialViewModelProvider;
-        mPromptViewModel = promptViewModel;
         mFpProps = fpProps;
         mFaceProps = faceProps;
 
@@ -408,10 +412,6 @@
             @Nullable FaceSensorPropertiesInternal faceProps,
             @NonNull VibratorHelper vibratorHelper
     ) {
-        // Set this value before showing either of the prompt.
-        mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
-                config.mPromptInfo);
-
         if (Utils.isBiometricAllowed(config.mPromptInfo)
                 || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
             addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 478ef8f..1dfd2e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -23,6 +23,7 @@
 import android.graphics.Rect
 import android.transition.AutoTransition
 import android.transition.TransitionManager
+import android.util.TypedValue
 import android.view.Surface
 import android.view.View
 import android.view.ViewGroup
@@ -54,13 +55,10 @@
 import com.android.systemui.biometrics.ui.viewmodel.isRight
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
 import com.android.systemui.biometrics.ui.viewmodel.isTop
-import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
-import kotlin.math.abs
 import kotlin.math.min
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
 
 /** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -106,6 +104,13 @@
             val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
             val panelView = view.requireViewById<View>(R.id.panel)
             val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
+            val cornerRadiusPx =
+                TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_DIP,
+                        cornerRadius,
+                        view.resources.displayMetrics
+                    )
+                    .toInt()
 
             // ConstraintSets for animating between prompt sizes
             val mediumConstraintSet = ConstraintSet()
@@ -116,9 +121,10 @@
 
             val largeConstraintSet = ConstraintSet()
             largeConstraintSet.clone(mediumConstraintSet)
-            largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
-            largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
-            largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
+            largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+            largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+            largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+            largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
 
             // TODO: Investigate better way to handle 180 rotations
             val flipConstraintSet = ConstraintSet()
@@ -141,7 +147,13 @@
             panelView.outlineProvider =
                 object : ViewOutlineProvider() {
                     override fun getOutline(view: View, outline: Outline) {
-                        outline.setRoundRect(0, 0, view.width, view.height, cornerRadius)
+                        outline.setRoundRect(
+                            0,
+                            0,
+                            view.width,
+                            view.height + cornerRadiusPx,
+                            cornerRadiusPx.toFloat()
+                        )
                     }
                 }
 
@@ -164,35 +176,81 @@
 
                     when {
                         position.isTop -> {
+                            // Round bottom corners
+                            panelView.outlineProvider =
+                                object : ViewOutlineProvider() {
+                                    override fun getOutline(view: View, outline: Outline) {
+                                        outline.setRoundRect(
+                                            0,
+                                            -cornerRadiusPx,
+                                            view.width,
+                                            view.height,
+                                            cornerRadiusPx.toFloat()
+                                        )
+                                    }
+                                }
                             left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
                             right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
                             bottom = iconHolderView.centerY() * 2 - iconHolderView.centerY() / 4
                         }
                         position.isBottom -> {
-                            left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
-                            right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
-                            bottom = viewModel.promptMargin
+                            // Round top corners
+                            panelView.outlineProvider =
+                                object : ViewOutlineProvider() {
+                                    override fun getOutline(view: View, outline: Outline) {
+                                        outline.setRoundRect(
+                                            0,
+                                            0,
+                                            view.width,
+                                            view.height + cornerRadiusPx,
+                                            cornerRadiusPx.toFloat()
+                                        )
+                                    }
+                                }
+
+                            left = windowBounds.centerX() - width / 2
+                            right = windowBounds.centerX() - width / 2
+                            bottom = if (view.isLandscape()) bottomInset else 0
                         }
                         position.isLeft -> {
-                            left = viewModel.promptMargin
-                            mid =
-                                abs(
-                                    windowBounds.width() - iconHolderView.centerX() * 2 +
-                                        viewModel.promptMargin
-                                )
+                            // Round right corners
+                            panelView.outlineProvider =
+                                object : ViewOutlineProvider() {
+                                    override fun getOutline(view: View, outline: Outline) {
+                                        outline.setRoundRect(
+                                            -cornerRadiusPx,
+                                            0,
+                                            view.width,
+                                            view.height,
+                                            cornerRadiusPx.toFloat()
+                                        )
+                                    }
+                                }
+
+                            left = 0
+                            mid = (windowBounds.width() * .85).toInt() / 2
                             right = windowBounds.width() - (windowBounds.width() * .85).toInt()
-                            bottom = bottomInset + viewModel.promptMargin
+                            bottom = if (view.isLandscape()) bottomInset else 0
                         }
                         position.isRight -> {
+                            // Round left corners
+                            panelView.outlineProvider =
+                                object : ViewOutlineProvider() {
+                                    override fun getOutline(view: View, outline: Outline) {
+                                        outline.setRoundRect(
+                                            0,
+                                            0,
+                                            view.width + cornerRadiusPx,
+                                            view.height,
+                                            cornerRadiusPx.toFloat()
+                                        )
+                                    }
+                                }
+
                             left = windowBounds.width() - (windowBounds.width() * .85).toInt()
-                            right = viewModel.promptMargin
-                            bottom = bottomInset + viewModel.promptMargin
-                            mid =
-                                abs(
-                                    iconHolderView.centerX() -
-                                        (windowBounds.width() - iconHolderView.centerX()) -
-                                        viewModel.promptMargin
-                                )
+                            right = 0
+                            bottom = if (view.isLandscape()) bottomInset else 0
+                            mid = windowBounds.width() - (windowBounds.width() * .85).toInt() / 2
                         }
                     }
 
@@ -226,16 +284,8 @@
                     }
                 }
 
-                fun setConstraintSetVisibility() {
-                    viewsToHideWhenSmall.forEach {
-                        mediumConstraintSet.setVisibility(it.id, it.showContentOrHide())
-                        largeConstraintSet.setVisibility(it.id, View.GONE)
-                        smallConstraintSet.setVisibility(it.id, View.GONE)
-                    }
-
-                    largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
-                    largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
-                    largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+                fun setVisibilities(size: PromptSize) {
+                    viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
 
                     if (viewModel.showBpWithoutIconForCredential.value) {
                         smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
@@ -261,7 +311,7 @@
                                 }
 
                                 measureBounds(position)
-                                setConstraintSetVisibility()
+                                setVisibilities(size)
                                 when {
                                     size.isSmall -> {
                                         val ratio =
@@ -353,7 +403,7 @@
 
                             // prepare for animated size transitions
                             for (v in viewsToHideWhenSmall) {
-                                v.visibility = v.showContentOrHide(forceHide = size.isSmall)
+                                v.showContentOrHide(forceHide = size.isSmall)
                             }
 
                             if (viewModel.showBpWithoutIconForCredential.value) {
@@ -490,14 +540,15 @@
     }
 }
 
-private fun View.showContentOrHide(forceHide: Boolean = false): Int {
+private fun View.showContentOrHide(forceHide: Boolean = false) {
     val isTextViewWithBlankText = this is TextView && this.text.isBlank()
     val isImageViewWithoutImage = this is ImageView && this.drawable == null
-    return if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
-        View.GONE
-    } else {
-        View.VISIBLE
-    }
+    visibility =
+        if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+            View.GONE
+        } else {
+            View.VISIBLE
+        }
 }
 
 private fun View.centerX(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index e457601..d8265c7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -63,12 +63,6 @@
 
                     iconOverlayView.layoutParams.width = iconViewLayoutParamSizeOverride.first
                     iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
-                } else {
-                    iconView.layoutParams.width = viewModel.fingerprintIconWidth.first()
-                    iconView.layoutParams.height = viewModel.fingerprintIconWidth.first()
-
-                    iconOverlayView.layoutParams.width = viewModel.fingerprintIconWidth.first()
-                    iconOverlayView.layoutParams.height = viewModel.fingerprintIconWidth.first()
                 }
 
                 var faceIcon: AnimatedVectorDrawable? = null
@@ -84,10 +78,8 @@
                     }
 
                 launch {
-                    var width = 0
-                    var height = 0
-                    combine(promptViewModel.size, viewModel.activeAuthType, ::Pair).collect {
-                        (_, activeAuthType) ->
+                    combine(viewModel.activeAuthType, viewModel.iconSize, ::Pair).collect {
+                        (activeAuthType, iconSize) ->
                         // Every time after bp shows, [isIconViewLoaded] is set to false in
                         // [BiometricViewSizeBinder]. Then when biometric prompt view is redrew
                         // (when size or activeAuthType changes), we need to update
@@ -109,8 +101,6 @@
                                 }
                             }
                             AuthType.Face -> {
-                                width = viewModel.faceIconWidth
-                                height = viewModel.faceIconHeight
                                 /**
                                  * Set to true by default since face icon is a drawable, which
                                  * doesn't have a LottieOnCompositionLoadedListener equivalent.
@@ -122,11 +112,12 @@
                             }
                         }
 
-                        if (width != 0 && height != 0) {
-                            iconView.layoutParams.width = width
-                            iconView.layoutParams.height = height
-                            iconOverlayView.layoutParams.width = width
-                            iconOverlayView.layoutParams.height = height
+                        if (iconViewLayoutParamSizeOverride == null) {
+                            iconView.layoutParams.width = iconSize.first
+                            iconView.layoutParams.height = iconSize.second
+
+                            iconOverlayView.layoutParams.width = iconSize.first
+                            iconOverlayView.layoutParams.height = iconSize.second
                         }
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 257eb4a..d0c140b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -21,6 +21,7 @@
 import android.annotation.RawRes
 import android.content.res.Configuration
 import android.graphics.Rect
+import android.hardware.face.Face
 import android.util.RotationUtils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -65,9 +66,10 @@
      */
     val activeAuthType: Flow<AuthType> =
         combine(
+            promptViewModel.size,
             promptViewModel.modalities.distinctUntilChanged(),
             promptViewModel.faceMode.distinctUntilChanged()
-        ) { modalities, faceMode ->
+        ) { _, modalities, faceMode ->
             if (modalities.hasFaceAndFingerprint && !faceMode) {
                 AuthType.Coex
             } else if (modalities.hasFaceOnly || faceMode) {
@@ -158,13 +160,18 @@
     /** Tracks whether a face iconView last pulsed light to dark (vs. dark to light) */
     val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow()
 
-    /** Layout params for fingerprint iconView */
-    val fingerprintIconWidth: Flow<Int> = promptViewModel.fingerprintSensorDiameter
-    val fingerprintIconHeight: Flow<Int> = promptViewModel.fingerprintSensorDiameter
-
-    /** Layout params for face iconView */
-    val faceIconWidth: Int = promptViewModel.faceIconWidth
-    val faceIconHeight: Int = promptViewModel.faceIconHeight
+    val iconSize: Flow<Pair<Int, Int>> =
+        combine(
+            activeAuthType,
+            promptViewModel.fingerprintSensorWidth,
+            promptViewModel.fingerprintSensorHeight,
+        ) { activeAuthType, fingerprintSensorWidth, fingerprintSensorHeight ->
+            if (activeAuthType == AuthType.Face) {
+                Pair(promptViewModel.faceIconWidth, promptViewModel.faceIconHeight)
+            } else {
+                Pair(fingerprintSensorWidth, fingerprintSensorHeight)
+            }
+        }
 
     /** Current BiometricPromptLayout.iconView asset. */
     val iconAsset: Flow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 86b0b44..fbd87fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -86,7 +86,7 @@
     val faceIconHeight: Int =
         context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
 
-    val fingerprintSensorDiameter: Flow<Int> =
+    val fingerprintSensorWidth: Flow<Int> =
         combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
             ->
             if (modalities.hasUdfps) {
@@ -96,6 +96,16 @@
             }
         }
 
+    val fingerprintSensorHeight: Flow<Int> =
+        combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
+            ->
+            if (modalities.hasUdfps) {
+                overlayParams.sensorBounds.height()
+            } else {
+                fingerprintIconHeight
+            }
+        }
+
     private val _accessibilityHint = MutableSharedFlow<String>()
 
     /** Hint for talkback directional guidance */
@@ -342,7 +352,7 @@
             position,
             message,
         ) { size, _, message ->
-            size.isNotSmall && message.message.isNotBlank()
+            size.isMedium && message.message.isNotBlank()
         }
 
     /** If the auth is pending confirmation and the confirm button should be shown. */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 9afd5ed..d2df276 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -24,9 +24,9 @@
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.people.PeopleSpaceActivity;
 import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.screenshot.LongScreenshotActivity;
 import com.android.systemui.screenshot.appclips.AppClipsActivity;
 import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.scroll.LongScreenshotActivity;
 import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 3d8e4cb..6aa5e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -22,8 +22,6 @@
 import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
-import com.android.systemui.screenshot.ActionProxyReceiver;
-import com.android.systemui.screenshot.DeleteScreenshotReceiver;
 import com.android.systemui.screenshot.SmartActionsReceiver;
 import com.android.systemui.volume.VolumePanelDialogReceiver;
 
@@ -42,24 +40,6 @@
      */
     @Binds
     @IntoMap
-    @ClassKey(ActionProxyReceiver.class)
-    public abstract BroadcastReceiver bindActionProxyReceiver(
-            ActionProxyReceiver broadcastReceiver);
-
-    /**
-     *
-     */
-    @Binds
-    @IntoMap
-    @ClassKey(DeleteScreenshotReceiver.class)
-    public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
-            DeleteScreenshotReceiver broadcastReceiver);
-
-    /**
-     *
-     */
-    @Binds
-    @IntoMap
     @ClassKey(SmartActionsReceiver.class)
     public abstract BroadcastReceiver bindSmartActionsReceiver(
             SmartActionsReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
index 83337f7..fa7603f 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
@@ -62,7 +62,7 @@
         conflatedCallbackFlow {
                 val callback =
                     DeviceStateManager.DeviceStateCallback { state ->
-                        trySend(deviceStateToPosture(state))
+                        trySend(deviceStateToPosture(state.identifier))
                     }
                 deviceStateManager.registerCallback(executor, callback)
                 awaitClose { deviceStateManager.unregisterCallback(callback) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 43a8b40..3b34750 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -40,6 +40,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
 import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
 import static com.android.systemui.Flags.refactorGetCurrentUser;
 import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -3401,6 +3402,11 @@
                 mSurfaceBehindRemoteAnimationFinishedCallback = null;
             }
         }
+
+        // Ensure that keyguard becomes visible if the going away animation is canceled
+        if (showKeyguard && !KeyguardWmStateRefactor.isEnabled() && migrateClocksToBlueprint()) {
+            mKeyguardInteractor.showKeyguard();
+        }
     }
 
     private void adjustStatusBarLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 88ddfd4..47f8046a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -33,7 +33,11 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -66,6 +70,25 @@
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
 
+    val surfaceBehindVisibility: Flow<Boolean?> =
+        combine(
+                transitionInteractor.startedKeyguardTransitionStep,
+                transitionInteractor.transitionStepsFromState(KeyguardState.ALTERNATE_BOUNCER)
+            ) { startedStep, fromBouncerStep ->
+                if (startedStep.to != KeyguardState.GONE) {
+                    return@combine null
+                }
+
+                // The alt bouncer is pretty fast to hide, so start the surface behind animation
+                // around 30%.
+                fromBouncerStep.value > 0.3f
+            }
+            .onStart {
+                // Default to null ("don't care, use a reasonable default").
+                emit(null)
+            }
+            .distinctUntilChanged()
+
     private fun listenForAlternateBouncerToLockscreenHubAodOrDozing() {
         scope.launch {
             keyguardInteractor.alternateBouncerShowing
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index d5a9bd1..4a3232e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -71,6 +71,10 @@
         listenForGoneToDreamingLockscreenHosted()
     }
 
+    fun showKeyguard() {
+        scope.launch { startTransitionTo(KeyguardState.LOCKSCREEN) }
+    }
+
     // Primarily for when the user chooses to lock down the device
     private fun listenForGoneToLockscreenOrHub() {
         if (KeyguardWmStateRefactor.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 391dccc..c7fafba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -96,36 +95,6 @@
             }
             .distinctUntilChanged()
 
-    val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
-        combine(
-                transitionInteractor.startedKeyguardTransitionStep,
-                transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER)
-            ) { startedStep, fromBouncerStep ->
-                if (startedStep.to != KeyguardState.GONE) {
-                    // BOUNCER to anything but GONE does not require any special surface
-                    // visibility handling.
-                    return@combine null
-                }
-
-                if (fromBouncerStep.value > 0.5f) {
-                    KeyguardSurfaceBehindModel(
-                        animateFromAlpha = 0f,
-                        alpha = 1f,
-                        animateFromTranslationY = 500f,
-                        translationY = 0f,
-                    )
-                } else {
-                    KeyguardSurfaceBehindModel(
-                        alpha = 0f,
-                    )
-                }
-            }
-            .onStart {
-                // Default to null ("don't care, use a reasonable default").
-                emit(null)
-            }
-            .distinctUntilChanged()
-
     fun dismissPrimaryBouncer() {
         scope.launch { startTransitionTo(KeyguardState.GONE) }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index c877192..d39bd3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -92,7 +92,7 @@
             when {
                 useWeatherClockLayout && useSplitShade -> SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
                 useWeatherClockLayout -> WEATHER_CLOCK_BLUEPRINT_ID
-                useSplitShade -> SplitShadeKeyguardBlueprint.ID
+                useSplitShade && !ComposeLockscreen.isEnabled -> SplitShadeKeyguardBlueprint.ID
                 else -> DefaultKeyguardBlueprint.DEFAULT
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7734973..283f160 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -83,6 +83,7 @@
     shadeRepository: ShadeRepository,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     sceneInteractorProvider: Provider<SceneInteractor>,
+    private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>,
 ) {
     // TODO(b/296118689): move to a repository
     private val _sharedNotificationContainerBounds = MutableStateFlow(NotificationContainerBounds())
@@ -383,6 +384,11 @@
         repository.topClippingBounds.value = top
     }
 
+    /** Temporary shim, until [KeyguardWmStateRefactor] is enabled */
+    fun showKeyguard() {
+        fromGoneTransitionInteractor.get().showKeyguard()
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index c496a6e..80e94a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -95,7 +95,17 @@
             }
             .distinctUntilChanged()
 
-    val isAnimatingSurface = repository.isAnimatingSurface
+    /**
+     * Whether we're animating the surface, or a notification launch animation is running (which
+     * means we're going to animate the surface, even if animators aren't yet running).
+     */
+    val isAnimatingSurface =
+        combine(
+            repository.isAnimatingSurface,
+            notificationLaunchInteractor.isLaunchAnimationRunning
+        ) { animatingSurface, animatingLaunch ->
+            animatingSurface || animatingLaunch
+        }
 
     fun setAnimatingSurface(animating: Boolean) {
         repository.setAnimatingSurface(animating)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index cff74b3..8d02e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -40,6 +40,7 @@
     surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
     fromLockscreenInteractor: FromLockscreenTransitionInteractor,
     fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
+    fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor,
     notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
 ) {
     private val defaultSurfaceBehindVisibility =
@@ -65,6 +66,9 @@
                     KeyguardState.PRIMARY_BOUNCER -> {
                         fromBouncerInteractor.surfaceBehindVisibility
                     }
+                    KeyguardState.ALTERNATE_BOUNCER -> {
+                        fromAlternateBouncerInteractor.surfaceBehindVisibility
+                    }
                     else -> flowOf(null)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 1085f94..8fd8bec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -29,6 +29,7 @@
 import com.android.keyguard.LockIconView
 import com.android.keyguard.LockIconViewController
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
@@ -70,7 +71,11 @@
     private val deviceEntryIconViewId = R.id.device_entry_icon_view
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
+        if (
+            !keyguardBottomAreaRefactor() &&
+                !migrateClocksToBlueprint() &&
+                !DeviceEntryUdfpsRefactor.isEnabled
+        ) {
             return
         }
 
@@ -82,7 +87,7 @@
             if (DeviceEntryUdfpsRefactor.isEnabled) {
                 DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
             } else {
-                // keyguardBottomAreaRefactor()
+                // keyguardBottomAreaRefactor() or migrateClocksToBlueprint()
                 LockIconView(context, null).apply { id = R.id.lock_icon_view }
             }
         constraintLayout.addView(view)
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 58c45c7..b6622e5 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
@@ -28,8 +28,9 @@
 import com.android.systemui.keyguard.shared.model.SettingsClockSize
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
-import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Utils
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -46,8 +47,8 @@
     keyguardInteractor: KeyguardInteractor,
     private val keyguardClockInteractor: KeyguardClockInteractor,
     @Application private val applicationScope: CoroutineScope,
-    private val splitShadeStateController: SplitShadeStateController,
     notifsKeyguardInteractor: NotificationsKeyguardInteractor,
+    private val shadeInteractor: ShadeInteractor,
 ) {
     var burnInLayer: Layer? = null
     val useLargeClock: Boolean
@@ -111,12 +112,33 @@
             initialValue = false
         )
 
+    val currentClockLayout: StateFlow<ClockLayout> =
+        combine(isLargeClockVisible, clockShouldBeCentered, shadeInteractor.shadeMode) {
+                isLargeClockVisible,
+                clockShouldBeCentered,
+                shadeMode ->
+                val shouldUseSplitShade = shadeMode == ShadeMode.Split
+                when {
+                    shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
+                    shouldUseSplitShade && isLargeClockVisible ->
+                        ClockLayout.SPLIT_SHADE_LARGE_CLOCK
+                    shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+                    isLargeClockVisible -> ClockLayout.LARGE_CLOCK
+                    else -> ClockLayout.SMALL_CLOCK
+                }
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = ClockLayout.SMALL_CLOCK
+            )
+
     /** Calculates the top margin for the small clock. */
     fun getSmallClockTopMargin(context: Context): Int {
         var topMargin: Int
         val statusBarHeight = Utils.getStatusBarHeaderHeightKeyguard(context)
 
-        if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) {
+        if (shadeInteractor.shadeMode.value == ShadeMode.Split) {
             topMargin =
                 context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
             if (ComposeLockscreen.isEnabled) {
@@ -130,4 +152,11 @@
         }
         return topMargin
     }
+
+    enum class ClockLayout {
+        LARGE_CLOCK,
+        SMALL_CLOCK,
+        SPLIT_SHADE_LARGE_CLOCK,
+        SPLIT_SHADE_SMALL_CLOCK,
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 5ca9215..41cc1d6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -56,6 +56,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.combineTransform
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
@@ -125,13 +126,36 @@
             .onStart { emit(false) }
             .distinctUntilChanged()
 
-    private val alphaOnShadeExpansion: Flow<Float> =
+    private val isOnLockscreen: Flow<Boolean> =
         combine(
+                keyguardTransitionInteractor.isFinishedInState(LOCKSCREEN).onStart { emit(false) },
+                keyguardTransitionInteractor
+                    .isInTransitionWhere { from, to -> from == LOCKSCREEN || to == LOCKSCREEN }
+                    .onStart { emit(false) }
+            ) { onLockscreen, transitioningToOrFromLockscreen ->
+                onLockscreen || transitioningToOrFromLockscreen
+            }
+            .distinctUntilChanged()
+
+    private val alphaOnShadeExpansion: Flow<Float> =
+        combineTransform(
+                isOnLockscreen,
                 shadeInteractor.qsExpansion,
                 shadeInteractor.shadeExpansion,
-            ) { qsExpansion, shadeExpansion ->
+            ) { isOnLockscreen, qsExpansion, shadeExpansion ->
                 // Fade out quickly as the shade expands
-                1f - MathUtils.constrainedMap(0f, 1f, 0f, 0.2f, max(qsExpansion, shadeExpansion))
+                if (isOnLockscreen) {
+                    val alpha =
+                        1f -
+                            MathUtils.constrainedMap(
+                                /* rangeMin = */ 0f,
+                                /* rangeMax = */ 1f,
+                                /* valueMin = */ 0f,
+                                /* valueMax = */ 0.2f,
+                                /* value = */ max(qsExpansion, shadeExpansion)
+                            )
+                    emit(alpha)
+                }
             }
             .distinctUntilChanged()
 
@@ -235,11 +259,7 @@
         burnInJob?.cancel()
 
         burnInJob =
-            scope.launch {
-                aodBurnInViewModel.movement(params).collect {
-                    burnInModel.value = it
-                }
-            }
+            scope.launch { aodBurnInViewModel.movement(params).collect { burnInModel.value = it } }
     }
 
     val scale: Flow<BurnInScaleViewModel> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 23320be..1f80441 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,7 +23,8 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -40,7 +41,7 @@
     private val interactor: KeyguardBlueprintInteractor,
     private val authController: AuthController,
     val longPress: KeyguardLongPressViewModel,
-    val splitShadeStateController: SplitShadeStateController,
+    val shadeInteractor: ShadeInteractor,
 ) {
     private val clockSize = clockInteractor.clockSize
 
@@ -48,10 +49,12 @@
         get() = authController.isUdfpsSupported
     val isLargeClockVisible: Boolean
         get() = clockSize.value == KeyguardClockSwitch.LARGE
-    fun areNotificationsVisible(resources: Resources): Boolean {
-        return !isLargeClockVisible ||
-            splitShadeStateController.shouldUseSplitNotificationShade(resources)
-    }
+
+    val areNotificationsVisible: Boolean
+        get() = !isLargeClockVisible || shouldUseSplitNotificationShade
+
+    val shouldUseSplitNotificationShade: Boolean
+        get() = shadeInteractor.shadeMode.value == ShadeMode.Split
 
     fun getSmartSpacePaddingTop(resources: Resources): Int {
         return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 730aa62..5dde14b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -65,7 +65,7 @@
                         it.topActivity,
                         it.baseIntent?.component,
                         it.taskDescription?.backgroundColor,
-                        isForegroundTask = it.taskId in foregroundTaskIds
+                        isForegroundTask = it.taskId in foregroundTaskIds && it.isVisible
                     )
                 }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index c657b55..9c88eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -27,6 +29,7 @@
 import android.widget.FrameLayout;
 
 import com.android.systemui.res.R;
+import com.android.systemui.shade.LargeScreenHeaderHelper;
 import com.android.systemui.util.LargeScreenUtils;
 
 /**
@@ -97,7 +100,9 @@
             qqsLP.topMargin = mContext.getResources()
                     .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
         } else {
-            qqsLP.topMargin = mContext.getResources()
+            qqsLP.topMargin = centralizedStatusBarHeightFix()
+                    ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
+                    : mContext.getResources()
                     .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
         }
         mHeaderQsPanel.setLayoutParams(qqsLP);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index aed08f8..4a8e33a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -37,7 +37,11 @@
     data class PlatformTileSpec
     internal constructor(
         override val spec: String,
-    ) : TileSpec(spec)
+    ) : TileSpec(spec) {
+        override fun toString(): String {
+            return "P($spec)"
+        }
+    }
 
     /**
      * Container for the spec of a tile provided by an app.
@@ -50,7 +54,7 @@
         val componentName: ComponentName,
     ) : TileSpec(spec) {
         override fun toString(): String {
-            return "CustomTileSpec(${componentName.toShortString()})"
+            return "C(${componentName.flattenToShortString()})"
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index 9076182..f02b871 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -16,12 +16,14 @@
 
 package com.android.systemui.reardisplay;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManagerGlobal;
 import android.view.LayoutInflater;
@@ -29,7 +31,6 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.LinearLayout;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -179,6 +180,7 @@
      */
     private void initializeValues(int startingBaseState) {
         mRearDisplayEducationDialog = mSystemUIDialogFactory.create();
+        // TODO(b/329170810): Refactor and remove with updated DeviceStateManager values.
         if (mFoldedStates == null) {
             mFoldedStates = mResources.getIntArray(
                     com.android.internal.R.array.config_foldedDeviceStates);
@@ -228,21 +230,19 @@
 
     private class DeviceStateManagerCallback implements DeviceStateManager.DeviceStateCallback {
         @Override
-        public void onBaseStateChanged(int state) {
-            if (mStartedFolded && !isFoldedState(state)) {
+        public void onDeviceStateChanged(@NonNull DeviceState state) {
+            if (mStartedFolded && !state.hasProperty(
+                    DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) {
                 // We've opened the device, we can close the overlay
                 mRearDisplayEducationDialog.dismiss();
                 closeOverlayAndNotifyService(false);
-            } else if (!mStartedFolded && isFoldedState(state)) {
+            } else if (!mStartedFolded && state.hasProperty(
+                    DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) {
                 // We've closed the device, finish activity
                 mRearDisplayEducationDialog.dismiss();
                 closeOverlayAndNotifyService(true);
             }
         }
-
-        // We only care about physical device changes in this scenario
-        @Override
-        public void onStateChanged(int state) {}
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9ae049d..d0ff338 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -37,6 +37,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_TRANSITION;
 
@@ -114,6 +115,8 @@
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInterface;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -125,8 +128,6 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import dagger.Lazy;
-
 /**
  * Class to send information from overview to launcher with a binder.
  */
@@ -669,7 +670,8 @@
             // Listen for tracing state changes
             @Override
             public void onTracingStateChanged(boolean enabled) {
-                // TODO(b/286509643) Cleanup callers of this; Unused downstream
+                mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled)
+                        .commitUpdate(mContext.getDisplayId());
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 1808d98..467089d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -19,9 +19,11 @@
 package com.android.systemui.scene.shared.flag
 
 import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.keyguardWmStateRefactor
 import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.Flags.sceneContainer
 import com.android.systemui.dagger.SysUISingleton
@@ -46,7 +48,8 @@
                 keyguardBottomAreaRefactor() &&
                 migrateClocksToBlueprint() &&
                 ComposeLockscreen.isEnabled &&
-                MediaInSceneContainerFlag.isEnabled
+                MediaInSceneContainerFlag.isEnabled &&
+                keyguardWmStateRefactor()
     // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
 
     /**
@@ -65,6 +68,7 @@
         sequenceOf(
             FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
             FlagToken(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, migrateClocksToBlueprint()),
+            FlagToken(FLAG_KEYGUARD_WM_STATE_REFACTOR, keyguardWmStateRefactor()),
             ComposeLockscreen.token,
             MediaInSceneContainerFlag.token,
             // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index ac94f39..b2c01e1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -82,7 +82,7 @@
     private final RecordingController mController;
     protected final KeyguardDismissUtil mKeyguardDismissUtil;
     private final Handler mMainHandler;
-    private ScreenRecordingAudioSource mAudioSource;
+    private ScreenRecordingAudioSource mAudioSource = ScreenRecordingAudioSource.NONE;
     private boolean mShowTaps;
     private boolean mOriginalShowTaps;
     private ScreenMediaRecorder mRecorder;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
deleted file mode 100644
index 7e234ae..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ /dev/null
@@ -1,107 +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.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import android.view.RemoteAnimationAdapter;
-import android.view.WindowManagerGlobal;
-
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import javax.inject.Inject;
-
-/**
- * Receiver to proxy the share or edit intent, used to clean up the notification and send
- * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
- */
-public class ActionProxyReceiver extends BroadcastReceiver {
-    private static final String TAG = "ActionProxyReceiver";
-
-    private final ActivityManagerWrapper mActivityManagerWrapper;
-    private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final DisplayTracker mDisplayTracker;
-    private final ActivityStarter mActivityStarter;
-
-    @Inject
-    public ActionProxyReceiver(ActivityManagerWrapper activityManagerWrapper,
-            ScreenshotSmartActions screenshotSmartActions,
-            DisplayTracker displayTracker,
-            ActivityStarter activityStarter) {
-        mActivityManagerWrapper = activityManagerWrapper;
-        mScreenshotSmartActions = screenshotSmartActions;
-        mDisplayTracker = displayTracker;
-        mActivityStarter = activityStarter;
-    }
-
-    @Override
-    public void onReceive(Context context, final Intent intent) {
-        Runnable startActivityRunnable = () -> {
-            mActivityManagerWrapper.closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
-
-            PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
-            ActivityOptions opts = ActivityOptions.makeBasic();
-            opts.setDisallowEnterPictureInPictureWhileLaunching(
-                    intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
-            opts.setPendingIntentBackgroundActivityStartMode(
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-            try {
-                actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
-                if (intent.getBooleanExtra(ScreenshotController.EXTRA_OVERRIDE_TRANSITION, false)) {
-                    RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
-                            ScreenshotController.SCREENSHOT_REMOTE_RUNNER, 0, 0);
-                    try {
-                        WindowManagerGlobal.getWindowManagerService()
-                                .overridePendingAppTransitionRemote(runner,
-                                        mDisplayTracker.getDefaultDisplayId());
-                    } catch (Exception e) {
-                        Log.e(TAG, "Error overriding screenshot app transition", e);
-                    }
-                }
-            } catch (PendingIntent.CanceledException e) {
-                Log.e(TAG, "Pending intent canceled", e);
-            }
-
-        };
-
-        mActivityStarter.executeRunnableDismissingKeyguard(startActivityRunnable, null,
-                true /* dismissShade */, true /* afterKeyguardGone */,
-                true /* deferred */);
-
-        if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
-            String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
-                    ? ACTION_TYPE_EDIT
-                    : ACTION_TYPE_SHARE;
-            mScreenshotSmartActions.notifyScreenshotAction(
-                    intent.getStringExtra(EXTRA_ID), actionType, false, null);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
deleted file mode 100644
index e0346f2..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ /dev/null
@@ -1,68 +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.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-
-import com.android.systemui.dagger.qualifiers.Background;
-
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * Removes the file at a provided URI.
- */
-public class DeleteScreenshotReceiver extends BroadcastReceiver {
-
-    private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final Executor mBackgroundExecutor;
-
-    @Inject
-    public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
-            @Background Executor backgroundExecutor) {
-        mScreenshotSmartActions = screenshotSmartActions;
-        mBackgroundExecutor = backgroundExecutor;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
-            return;
-        }
-
-        // And delete the image from the media store
-        final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
-        mBackgroundExecutor.execute(() -> {
-            ContentResolver resolver = context.getContentResolver();
-            resolver.delete(uri, null, null);
-        });
-        if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
-            mScreenshotSmartActions.notifyScreenshotAction(
-                    intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index c4287ca..864f29a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -126,7 +126,7 @@
     /**
      * Writes the given Bitmap to outputFile.
      */
-    ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
+    public ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
             final File outputFile) {
         return CallbackToFutureAdapter.getFuture(
                 (completer) -> {
@@ -196,7 +196,7 @@
      * @param bitmap   the bitmap to export
      * @return a listenable future result
      */
-    ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
+    public ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
             ZonedDateTime captureTime, UserHandle owner, int displayId) {
         return export(executor, new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat,
                 mQuality, /* publish */ true, owner, mFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
index 1f6d212..a1481f6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -34,6 +34,7 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.res.R
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
 import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER
 import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
index 6050c2b..440cf1c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
@@ -16,8 +16,9 @@
 
 package com.android.systemui.screenshot;
 
+/** Stores debug log configuration for screenshots. */
 @SuppressWarnings("PointlessBooleanExpression")
-class LogConfig {
+public class LogConfig {
 
     /** Log ALL the things... */
     private static final boolean DEBUG_ALL = false;
@@ -29,36 +30,37 @@
     private static final boolean TAG_WITH_CLASS_NAME = false;
 
     /** Action creation and user selection: Share, Save, Edit, Delete, Smart action, etc */
-    static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
+    public static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
 
     /** Debug info about animations such as start, complete and cancel */
-    static final boolean DEBUG_ANIM = DEBUG_ALL || false;
+    public static final boolean DEBUG_ANIM = DEBUG_ALL || false;
 
     /** Whenever Uri is supplied to consumer, or onComplete runnable is run() */
-    static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
+    public static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
 
     /** Logs information about dismissing the screenshot tool */
-    static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
+    public static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
 
     /** Touch or key event driven action or side effects */
-    static final boolean DEBUG_INPUT = DEBUG_ALL || false;
+    public static final boolean DEBUG_INPUT = DEBUG_ALL || false;
 
     /** Scroll capture usage */
-    static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
+    public static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
 
     /** Service lifecycle events and callbacks */
-    static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
+    public static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
 
     /** Storage related actions, Bitmap.compress, ContentManager, etc */
-    static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
+    public static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
 
     /** High level logical UI actions: timeout, onConfigChanged, insets, show actions, reset  */
-    static final boolean DEBUG_UI = DEBUG_ALL || false;
+    public static final boolean DEBUG_UI = DEBUG_ALL || false;
 
     /** Interactions with Window and WindowManager */
-    static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
+    public static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
 
-    static String logTag(Class<?> cls) {
+    /** Get the appropriate class name */
+    public static String logTag(Class<?> cls) {
         return TAG_WITH_CLASS_NAME ? cls.getSimpleName() : TAG_SS;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 198a29c..c8e13bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -87,6 +87,10 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
+import com.android.systemui.screenshot.scroll.LongScreenshotActivity;
+import com.android.systemui.screenshot.scroll.LongScreenshotData;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController;
 import com.android.systemui.util.Assert;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -200,7 +204,7 @@
         void onActionsReady(ScreenshotController.QuickShareData quickShareData);
     }
 
-    interface TransitionDestination {
+    public interface TransitionDestination {
         /**
          * Allows the long screenshot activity to call back with a destination location (the bounds
          * on screen of the destination for the transitioning view) and a Runnable to be run once
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 1c5a8a1..cb2dba0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -92,6 +92,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.QuickStepContract;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
index 182b889..6be32a9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -25,6 +25,7 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowInsets
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
 
 /** Abstraction of the surface between ScreenshotController and ScreenshotView */
 interface ScreenshotViewProxy {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index aa23d6b..d87d85b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -52,7 +52,7 @@
 import com.android.internal.logging.UiEventLogger.UiEventEnum;
 import com.android.settingslib.Utils;
 import com.android.systemui.res.R;
-import com.android.systemui.screenshot.CropView;
+import com.android.systemui.screenshot.scroll.CropView;
 import com.android.systemui.settings.UserTracker;
 
 import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
index 2f411ea..5e561cf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -265,7 +265,7 @@
                 Log.w(TAG, "No boundary selected");
                 break;
         }
-        Log.i(TAG,  "Updated mCrop: " + mCrop);
+        Log.i(TAG, "Updated mCrop: " + mCrop);
 
         invalidate();
     }
@@ -385,7 +385,7 @@
 
     /**
      * @param action either ACTION_DOWN, ACTION_UP or ACTION_MOVE.
-     * @param x coordinate of the relevant pointer.
+     * @param x      x-coordinate of the relevant pointer.
      */
     private void updateListener(int action, float x) {
         if (mCropInteractionListener != null && isVertical(mCurrentDraggingBoundary)) {
@@ -643,11 +643,13 @@
     /**
      * Listen for crop motion events and state.
      */
-    public interface CropInteractionListener {
+    interface CropInteractionListener {
         void onCropDragStarted(CropBoundary boundary, float boundaryPosition,
                 int boundaryPositionPx, float horizontalCenter, float x);
+
         void onCropDragMoved(CropBoundary boundary, float boundaryPosition,
                 int boundaryPositionPx, float horizontalCenter, float x);
+
         void onCropDragComplete();
     }
 
@@ -675,8 +677,7 @@
             out.writeParcelable(mCrop, 0);
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
+        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<>() {
             public SavedState createFromParcel(Parcel in) {
                 return new SavedState(in);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
index 7ee7c31..df86d69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.annotation.Nullable;
 import android.content.ContentResolver;
@@ -35,20 +35,20 @@
 import javax.inject.Inject;
 
 /** Loads images. */
-public class ImageLoader {
+class ImageLoader {
     private final ContentResolver mResolver;
 
     static class Result {
-        @Nullable Uri uri;
-        @Nullable File fileName;
-        @Nullable Bitmap bitmap;
+        @Nullable Uri mUri;
+        @Nullable File mFilename;
+        @Nullable Bitmap mBitmap;
 
         @Override
         public String toString() {
             final StringBuilder sb = new StringBuilder("Result{");
-            sb.append("uri=").append(uri);
-            sb.append(", fileName=").append(fileName);
-            sb.append(", bitmap=").append(bitmap);
+            sb.append("uri=").append(mUri);
+            sb.append(", fileName=").append(mFilename);
+            sb.append(", bitmap=").append(mBitmap);
             sb.append('}');
             return sb.toString();
         }
@@ -69,11 +69,10 @@
         return CallbackToFutureAdapter.getFuture(completer -> {
             Result result = new Result();
             try (InputStream in = mResolver.openInputStream(uri)) {
-                result.uri = uri;
-                result.bitmap = BitmapFactory.decodeStream(in);
+                result.mUri = uri;
+                result.mBitmap = BitmapFactory.decodeStream(in);
                 completer.set(result);
-            }
-            catch (IOException e) {
+            } catch (IOException e) {
                 completer.setException(e);
             }
             return "BitmapFactory#decodeStream";
@@ -91,8 +90,8 @@
         return CallbackToFutureAdapter.getFuture(completer -> {
             try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
                 Result result = new Result();
-                result.fileName = file;
-                result.bitmap = BitmapFactory.decodeStream(in);
+                result.mFilename = file;
+                result.mBitmap = BitmapFactory.decodeStream(in);
                 completer.set(result);
             } catch (IOException e) {
                 completer.setException(e);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
index a95c91b..c9c297e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import static android.graphics.ColorSpace.Named.SRGB;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
index 356f67e..76a72f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.annotation.AnyThread;
 import android.graphics.Bitmap;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
index 00d480a..1e1a577 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.app.Activity;
 import android.app.ActivityOptions;
@@ -47,11 +47,14 @@
 import com.android.internal.view.OneShotPreDrawListener;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.res.R;
-import com.android.systemui.screenshot.CropView.CropBoundary;
-import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
-import com.android.systemui.settings.UserTracker;
+import com.android.systemui.screenshot.ActionIntentCreator;
+import com.android.systemui.screenshot.ActionIntentExecutor;
+import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.LogConfig;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.screenshot.scroll.CropView.CropBoundary;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController.LongScreenshot;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -81,8 +84,6 @@
     private final ImageExporter mImageExporter;
     private final LongScreenshotData mLongScreenshotHolder;
     private final ActionIntentExecutor mActionExecutor;
-    private final FeatureFlags mFeatureFlags;
-    private final UserTracker mUserTracker;
 
     private ImageView mPreview;
     private ImageView mTransitionView;
@@ -113,16 +114,13 @@
     @Inject
     public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
             @Main Executor mainExecutor, @Background Executor bgExecutor,
-            LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor,
-            FeatureFlags featureFlags, UserTracker userTracker) {
+            LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor) {
         mUiEventLogger = uiEventLogger;
         mUiExecutor = mainExecutor;
         mBackgroundExecutor = bgExecutor;
         mImageExporter = imageExporter;
         mLongScreenshotHolder = longScreenshotHolder;
         mActionExecutor = actionExecutor;
-        mFeatureFlags = featureFlags;
-        mUserTracker = userTracker;
     }
 
 
@@ -265,13 +263,13 @@
     private void onCachedImageLoaded(ImageLoader.Result imageResult) {
         mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED);
 
-        BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
+        BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.mBitmap);
         mPreview.setImageDrawable(drawable);
         mPreview.setAlpha(1f);
-        mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
-                imageResult.bitmap.getHeight());
+        mMagnifierView.setDrawable(drawable, imageResult.mBitmap.getWidth(),
+                imageResult.mBitmap.getHeight());
         mCropView.setVisibility(View.VISIBLE);
-        mSavedImagePath = imageResult.fileName;
+        mSavedImagePath = imageResult.mFilename;
 
         setButtonsEnabled(true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
index f549faf..ebac5bf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.ScreenshotController;
 
 import java.util.concurrent.atomic.AtomicReference;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
index 0c543cd..0a1a747 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -64,16 +64,16 @@
     private ViewPropertyAnimator mTranslationAnimator;
     private final Animator.AnimatorListener mTranslationAnimatorListener =
             new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mTranslationAnimator = null;
-        }
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    mTranslationAnimator = null;
+                }
 
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mTranslationAnimator = null;
-        }
-    };
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mTranslationAnimator = null;
+                }
+            };
 
     public MagnifierView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
index e93f737..0e43343 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL;
 
@@ -46,6 +46,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.screenshot.LogConfig;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
index 8a2678c..f4c77da 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -30,8 +30,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.LogConfig;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -85,7 +87,7 @@
             mImageTileSet = imageTileSet;
         }
 
-        /** Returns a bitmap containing the combinded result. */
+        /** Returns a bitmap containing the combined result. */
         public Bitmap toBitmap() {
             return mImageTileSet.toBitmap();
         }
@@ -167,7 +169,7 @@
      *                 {@link ScrollCaptureResponse#isConnected() connected}.
      * @return a future ImageTile set containing the result
      */
-    ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
+    public ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
         mCancelled = false;
         return CallbackToFutureAdapter.getFuture(completer -> {
             mCaptureCompleter = completer;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
index 71df369..00455bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.annotation.Nullable;
 import android.graphics.Canvas;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 8197b66..db06c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -31,12 +31,6 @@
  * @see NotificationPanelViewController
  */
 interface ShadeViewController {
-    /**
-     * Returns whether the shade height is greater than zero or the shade is expecting a synthesized
-     * down event.
-     */
-    val isPanelExpanded: Boolean
-
     /** Returns whether the shade is tracking touches for expand/collapse of the shade or QS. */
     val isTracking: Boolean
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
index 79ffe06..60cb061 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
@@ -43,6 +43,12 @@
     @Deprecated("Use SceneInteractor.currentScene instead.") val legacyPanelExpansion: Flow<Float>
 
     /**
+     * Returns whether the shade height is greater than zero or the shade is expecting a synthesized
+     * down event.
+     */
+    @Deprecated("Use ShadeInteractor.isAnyExpanded instead.") val isPanelExpanded: Boolean
+
+    /**
      * This method should not be used anymore, you should probably use [.isShadeFullyOpen] instead.
      * It was overused as indicating if shade is open or we're on keyguard/AOD. Moving forward we
      * should be explicit about the what state we're checking.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
index 3ad2b56..3877677 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
@@ -111,4 +111,6 @@
     private fun SceneKey.isExpandable(): Boolean {
         return this == Scenes.Shade || this == Scenes.QuickSettings
     }
+
+    override val isPanelExpanded = shadeInteractor.isAnyExpanded.value
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4275fc6..4406813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -178,6 +178,7 @@
     private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
     private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
     private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;
+    private static final int MSG_SET_SPLITSCREEN_FOCUS = 81 << MSG_SHIFT;
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
     public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -508,6 +509,11 @@
         default void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop) {}
 
         /**
+         * @see IStatusBar#setSplitscreenFocus
+         */
+        default void setSplitscreenFocus(boolean leftOrTop) {}
+
+        /**
          * @see IStatusBar#showMediaOutputSwitcher
          */
         default void showMediaOutputSwitcher(String packageName) {}
@@ -1349,6 +1355,12 @@
     }
 
     @Override
+    public void setSplitscreenFocus(boolean leftOrTop) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SET_SPLITSCREEN_FOCUS, leftOrTop).sendToTarget();
+        }
+    }
+    @Override
     public void showMediaOutputSwitcher(String packageName) {
         int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
@@ -1919,6 +1931,11 @@
                     }
                     break;
                 }
+                case MSG_SET_SPLITSCREEN_FOCUS:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).setSplitscreenFocus((Boolean) msg.obj);
+                    }
+                    break;
                 case MSG_SHOW_MEDIA_OUTPUT_SWITCHER:
                     args = (SomeArgs) msg.obj;
                     String clientPackageName = (String) args.arg1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
index 1bebbfd..ca73081 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
@@ -18,12 +18,16 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.distinctUntilChangedBy
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
@@ -46,6 +50,8 @@
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
     powerInteractor: PowerInteractor,
+    wmLockscreenVisibilityInteractor: WindowManagerLockscreenVisibilityInteractor,
+    surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
 ) {
     /** Occlusion state to apply whenever a keyguard transition is STARTED, if any. */
     private val occlusionStateFromStartedStep: Flow<OccludedState> =
@@ -98,11 +104,21 @@
                 OccludedState(occluded = occluded, animate = false)
             }
 
-    /** Occlusion state to apply to SKBVM's setOccluded call. */
+    /** Occlusion state to apply to SBKVM's setOccluded call. */
     val keyguardViewOcclusionState =
         merge(occlusionStateFromStartedStep, occlusionStateFromFinishedStep)
             .distinctUntilChangedBy {
                 // Don't switch 'animate' values mid-transition.
                 it.occluded
             }
+
+    /** Visibility state to apply to SBKVM via show() and hide(). */
+    val keyguardViewVisibility =
+        combine(
+                wmLockscreenVisibilityInteractor.lockscreenVisibility,
+                surfaceBehindInteractor.isAnimatingSurface,
+            ) { lockscreenVisible, animatingSurface ->
+                lockscreenVisible || animatingSurface
+            }
+            .distinctUntilChanged()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 1faca00..1906b7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -59,7 +59,6 @@
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -97,7 +96,6 @@
     private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
     private final PowerManager mPowerManager;
     private final Optional<Vibrator> mVibratorOptional;
-    private final DisableFlagsLogger mDisableFlagsLogger;
     private final int mDisplayId;
     private final UserTracker mUserTracker;
     private final boolean mVibrateOnOpening;
@@ -137,7 +135,6 @@
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             PowerManager powerManager,
             Optional<Vibrator> vibratorOptional,
-            DisableFlagsLogger disableFlagsLogger,
             @DisplayId int displayId,
             Lazy<CameraLauncher> cameraLauncherLazy,
             UserTracker userTracker,
@@ -165,7 +162,6 @@
         mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
         mPowerManager = powerManager;
         mVibratorOptional = vibratorOptional;
-        mDisableFlagsLogger = disableFlagsLogger;
         mDisplayId = displayId;
         mCameraLauncherLazy = cameraLauncherLazy;
         mUserTracker = userTracker;
@@ -489,7 +485,7 @@
 
     @Override
     public void togglePanel() {
-        if (mShadeViewController.isPanelExpanded()) {
+        if (mPanelExpansionInteractor.isPanelExpanded()) {
             mShadeController.animateCollapseShade();
         } else {
             mShadeController.animateExpandShade();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
index 56b0d598..db237e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.content.Context
+import android.hardware.devicestate.DeviceState
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback
 import com.android.internal.R
 
@@ -45,13 +46,13 @@
 
     private var wasFolded: Boolean? = null
 
-    override fun onStateChanged(state: Int) {
-        val isFolded = foldedDeviceStates.contains(state)
+    override fun onDeviceStateChanged(state: DeviceState) {
+        val isFolded = foldedDeviceStates.contains(state.identifier)
         if (wasFolded == isFolded) {
             return
         }
         wasFolded = isFolded
-        val willGoToSleep = goToSleepDeviceStates.contains(state)
+        val willGoToSleep = goToSleepDeviceStates.contains(state.identifier)
         listener.onFoldStateChanged(isFolded, willGoToSleep)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 14e934fa..f99817a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -107,8 +106,6 @@
 
 import dagger.Lazy;
 
-import kotlin.Unit;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -118,6 +115,7 @@
 
 import javax.inject.Inject;
 
+import kotlin.Unit;
 import kotlinx.coroutines.CoroutineDispatcher;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 import kotlinx.coroutines.Job;
@@ -523,16 +521,7 @@
         if (KeyguardWmStateRefactor.isEnabled()) {
             // Show the keyguard views whenever we've told WM that the lockscreen is visible.
             mJavaAdapter.alwaysCollectFlow(
-                    combineFlows(
-                            mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
-                            mSurfaceBehindInteractor.get().isAnimatingSurface(),
-                            (lockscreenVis, animatingSurface) ->
-                                    // TODO(b/322546110): Waiting until we're not animating the
-                                    // surface is a workaround to avoid jank. We should actually
-                                    // fix the source of the jank, and then hide the keyguard
-                                    // view without waiting for the animation to end.
-                                    lockscreenVis || animatingSurface
-                    ),
+                    mStatusBarKeyguardViewManagerInteractor.getKeyguardViewVisibility(),
                     this::consumeShowStatusBarKeyguardView);
 
             mJavaAdapter.alwaysCollectFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 88374d6..67d2299 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -12,16 +12,19 @@
 import android.view.Surface
 import android.view.View
 import android.view.WindowManager.fixScale
+import com.android.app.animation.Interpolators
+import com.android.app.tracing.namedRunnable
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
 import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
 import com.android.systemui.DejankUtils
-import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.lightRevealMigration
 import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.CircleReveal
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -32,9 +35,8 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.Flags.lightRevealMigration
-import com.android.app.tracing.namedRunnable
 import com.android.systemui.util.settings.GlobalSettings
+import dagger.Lazy
 import javax.inject.Inject
 
 /**
@@ -60,14 +62,15 @@
     private val context: Context,
     private val wakefulnessLifecycle: WakefulnessLifecycle,
     private val statusBarStateControllerImpl: StatusBarStateControllerImpl,
-    private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>,
+    private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
     private val keyguardStateController: KeyguardStateController,
-    private val dozeParameters: dagger.Lazy<DozeParameters>,
+    private val dozeParameters: Lazy<DozeParameters>,
     private val globalSettings: GlobalSettings,
-    private val notifShadeWindowControllerLazy: dagger.Lazy<NotificationShadeWindowController>,
+    private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val powerManager: PowerManager,
-    private val handler: Handler = Handler()
+    private val panelExpansionInteractorLazy: Lazy<PanelExpansionInteractor>,
+    private val handler: Handler = Handler(),
 ) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
     private lateinit var centralSurfaces: CentralSurfaces
     private lateinit var shadeViewController: ShadeViewController
@@ -346,7 +349,7 @@
         // already expanded and showing notifications/QS, the animation looks really messy. For now,
         // disable it if the notification panel is expanded.
         if ((!this::centralSurfaces.isInitialized ||
-                shadeViewController.isPanelExpanded) &&
+                    panelExpansionInteractorLazy.get().isPanelExpanded) &&
                 // Status bar might be expanded because we have started
                 // playing the animation already
                 !isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 6aaf5d6..55a0f59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -131,21 +131,37 @@
     }
 
     /**
-     * Returns true if given HeadsUpEntry is the last one tracked by AvalancheController. Used by
-     * BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration during active
-     * avalanche.
+     * Returns duration based on
+     * 1) Whether HeadsUpEntry is the last one tracked byAvalancheController
+     * 2) The priority of the top HUN in the next batch Used by
+     *    BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration.
      */
-    fun shortenDuration(entry: HeadsUpEntry): Boolean {
+    fun getDurationMs(entry: HeadsUpEntry, autoDismissMs: Int): Int {
         if (!NotificationThrottleHun.isEnabled) {
-            // Use default display duration, like we always did before AvalancheController existed
-            return false
+            // Use default duration, like we did before AvalancheController existed
+            return autoDismissMs
         }
         val showingList: MutableList<HeadsUpEntry> = mutableListOf()
         headsUpEntryShowing?.let { showingList.add(it) }
-        val allEntryList = showingList + nextList
 
-        // Shorten duration if not last entry
-        return allEntryList.indexOf(entry) != allEntryList.size - 1
+        val entryList = showingList + nextList
+        if (entryList.indexOf(entry) == entryList.size - 1) {
+            // Use default duration if last entry
+            return autoDismissMs
+        }
+
+        nextList.sort()
+        val nextEntry = nextList[0]
+
+        if (nextEntry.compareNonTimeFields(entry) == -1) {
+            // Next entry is higher priority
+            return 500
+        } else if (nextEntry.compareNonTimeFields(entry) == 0) {
+            // Next entry is same priority
+            return 1000
+        } else {
+            return autoDismissMs
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 05cc73e..50de3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -776,7 +776,7 @@
             return mEarliestRemovalTime < mSystemClock.elapsedRealtime();
         }
 
-        public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+        public int compareNonTimeFields(HeadsUpEntry headsUpEntry) {
             boolean isPinned = mEntry.isRowPinned();
             boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
             if (isPinned && !otherPinned) {
@@ -806,7 +806,14 @@
             } else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) {
                 return 1;
             }
+            return 0;
+        }
 
+        public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+            int nonTimeCompareResult = compareNonTimeFields(headsUpEntry);
+            if (nonTimeCompareResult != 0) {
+                return nonTimeCompareResult;
+            }
             if (mPostTime > headsUpEntry.mPostTime) {
                 return -1;
             } else if (mPostTime == headsUpEntry.mPostTime) {
@@ -929,10 +936,8 @@
             int requestedTimeOutMs;
             if (isStickyForSomeTime()) {
                 requestedTimeOutMs = mStickyForSomeTimeAutoDismissTime;
-            } else if (mAvalancheController.shortenDuration(this)) {
-                requestedTimeOutMs = 1000;
             } else {
-                requestedTimeOutMs = mAutoDismissTime;
+                requestedTimeOutMs = mAvalancheController.getDurationMs(this, mAutoDismissTime);
             }
             final long duration = getRecommendedHeadsUpTimeoutMs(requestedTimeOutMs);
             return mPostTime + duration;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 422aa4d..de0eb49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -16,12 +16,13 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.annotation.NonNull;
 import android.content.Context;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateUtil;
 import android.util.SparseIntArray;
 
-import androidx.annotation.NonNull;
-
 import com.android.app.tracing.ListenersTracing;
 import com.android.internal.R;
 import com.android.systemui.dagger.SysUISingleton;
@@ -42,8 +43,9 @@
     /** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
     private static final int COMMON_STATE_USE_BASE_STATE = 1000;
     private final List<Callback> mListeners = new ArrayList<>();
+    private final List<DeviceState> mSupportedStates;
+    private DeviceState mCurrentDeviceState;
     private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
-    private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
 
     private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
 
@@ -76,21 +78,17 @@
             mDeviceStateToPostureMap.put(deviceState, posture);
         }
 
+        mSupportedStates = deviceStateManager.getSupportedDeviceStates();
         deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
             @Override
-            public void onStateChanged(int state) {
+            public void onDeviceStateChanged(@NonNull DeviceState state) {
+                mCurrentDeviceState = state;
                 Assert.isMainThread();
-                mCurrentDevicePosture =
-                        mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
-                sendUpdatePosture();
-            }
-
-            @Override
-            public void onBaseStateChanged(int state) {
-                Assert.isMainThread();
-                mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
-
-                if (useBaseState()) {
+                int newDevicePosture =
+                        mDeviceStateToPostureMap.get(state.getIdentifier(), DEVICE_POSTURE_UNKNOWN);
+                if (newDevicePosture != mCurrentDevicePosture
+                        || newDevicePosture == COMMON_STATE_USE_BASE_STATE) {
+                    mCurrentDevicePosture = newDevicePosture;
                     sendUpdatePosture();
                 }
             }
@@ -120,7 +118,8 @@
     @Override
     public int getDevicePosture() {
         if (useBaseState()) {
-            return mCurrentBasePosture;
+            return DeviceStateUtil.calculateBaseStateIdentifier(mCurrentDeviceState,
+                    mSupportedStates);
         } else {
             return mCurrentDevicePosture;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 3008c866d..88cf46a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
 
 import android.annotation.Nullable;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Trace;
 import android.util.IndentingPrintWriter;
@@ -120,18 +121,18 @@
         mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
     }
 
-    private void updateDeviceState(int state) {
-        mLogger.logUpdateDeviceState(mDeviceState, state);
-        if (Trace.isEnabled()) {
-            Trace.traceBegin(
-                    Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]");
-        }
+    private void updateDeviceState(@NonNull DeviceState state) {
+        mLogger.logUpdateDeviceState(mDeviceState, state.getIdentifier());
         try {
-            if (mDeviceState == state) {
+            if (Trace.isEnabled()) {
+                Trace.traceBegin(Trace.TRACE_TAG_APP,
+                        "updateDeviceState [state=" + state.getIdentifier() + "]");
+            }
+            if (mDeviceState == state.getIdentifier()) {
                 return;
             }
 
-            readPersistedSetting("updateDeviceState", state);
+            readPersistedSetting("updateDeviceState", state.getIdentifier());
         } finally {
             Trace.endSection();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index b5efc44..aac18e3 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -86,15 +86,6 @@
 import com.android.systemui.util.settings.SecureSettings;
 
 import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
-import com.google.ux.material.libmonet.hct.Hct;
-import com.google.ux.material.libmonet.scheme.DynamicScheme;
-import com.google.ux.material.libmonet.scheme.SchemeExpressive;
-import com.google.ux.material.libmonet.scheme.SchemeFruitSalad;
-import com.google.ux.material.libmonet.scheme.SchemeMonochrome;
-import com.google.ux.material.libmonet.scheme.SchemeNeutral;
-import com.google.ux.material.libmonet.scheme.SchemeRainbow;
-import com.google.ux.material.libmonet.scheme.SchemeTonalSpot;
-import com.google.ux.material.libmonet.scheme.SchemeVibrant;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -150,7 +141,7 @@
     // Dominant color extracted from wallpaper, NOT the color used on the overlay
     protected int mMainWallpaperColor = Color.TRANSPARENT;
     // UI contrast as reported by UiModeManager
-    private float mContrast = 0;
+    private double mContrast = 0.0;
     // Theme variant: Vibrant, Tonal, Expressive, etc
     @VisibleForTesting
     protected Style mThemeStyle = Style.TONAL_SPOT;
@@ -170,8 +161,8 @@
     private final JavaAdapter mJavaAdapter;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final UiModeManager mUiModeManager;
-    private DynamicScheme mDynamicSchemeDark;
-    private DynamicScheme mDynamicSchemeLight;
+    private ColorScheme mDarkColorScheme;
+    private ColorScheme mLightColorScheme;
 
     // Defers changing themes until Setup Wizard is done.
     private boolean mDeferredThemeEvaluation;
@@ -210,7 +201,7 @@
             boolean currentUser = userId == mUserTracker.getUserId();
             boolean isAsleep = themeOverlayControllerWakefulnessDeprecation()
                     ? mKeyguardTransitionInteractor.isFinishedInStateWhereValue(
-                        state -> KeyguardState.Companion.deviceIsAsleepInState(state))
+                    KeyguardState.Companion::deviceIsAsleepInState)
                     : mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP;
 
             if (currentUser && !mAcceptColorEvents && isAsleep) {
@@ -483,19 +474,14 @@
             reevaluateSystemTheme(true /* forceReload */);
         });
 
+        // All wallpaper color and keyguard logic only applies when Monet is enabled.
         if (!mIsMonetEnabled) {
             return;
         }
 
         mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor);
-
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
 
-        // All wallpaper color and keyguard logic only applies when Monet is enabled.
-        if (!mIsMonetEnabled) {
-            return;
-        }
-
         // Upon boot, make sure we have the most up to date colors
         Runnable updateColors = () -> {
             WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
@@ -589,29 +575,6 @@
         return ColorScheme.getSeedColor(wallpaperColors);
     }
 
-    private static DynamicScheme dynamicSchemeFromStyle(Style style, int color,
-            boolean isDark, double contrastLevel) {
-        Hct sourceColorHct = Hct.fromInt(color);
-        switch (style) {
-            case EXPRESSIVE:
-                return new SchemeExpressive(sourceColorHct, isDark, contrastLevel);
-            case SPRITZ:
-                return new SchemeNeutral(sourceColorHct, isDark, contrastLevel);
-            case TONAL_SPOT:
-                return new SchemeTonalSpot(sourceColorHct, isDark, contrastLevel);
-            case FRUIT_SALAD:
-                return new SchemeFruitSalad(sourceColorHct, isDark, contrastLevel);
-            case RAINBOW:
-                return new SchemeRainbow(sourceColorHct, isDark, contrastLevel);
-            case VIBRANT:
-                return new SchemeVibrant(sourceColorHct, isDark, contrastLevel);
-            case MONOCHROMATIC:
-                return new SchemeMonochrome(sourceColorHct, isDark, contrastLevel);
-            default:
-                return null;
-        }
-    }
-
     @VisibleForTesting
     protected boolean isNightMode() {
         return (mResources.getConfiguration().uiMode
@@ -626,22 +589,17 @@
     @VisibleForTesting
     protected boolean isPrivateProfile(UserHandle userHandle) {
         Context usercontext = mContext.createContextAsUser(userHandle,0);
-        if (usercontext.getSystemService(UserManager.class).isPrivateProfile()) {
-            return true;
-        }
-        return false;
+        return usercontext.getSystemService(UserManager.class).isPrivateProfile();
     }
 
     private void createOverlays(int color) {
-        boolean nightMode = isNightMode();
-        mColorScheme = new ColorScheme(color, nightMode, mThemeStyle);
+        mDarkColorScheme = new ColorScheme(color, true /* isDark */, mThemeStyle, mContrast);
+        mLightColorScheme = new ColorScheme(color, false /* isDark */, mThemeStyle, mContrast);
+        mColorScheme = isNightMode() ? mDarkColorScheme : mLightColorScheme;
+
         mNeutralOverlay = createNeutralOverlay();
         mSecondaryOverlay = createAccentOverlay();
 
-        mDynamicSchemeDark = dynamicSchemeFromStyle(
-                mThemeStyle, color, true /* isDark */, mContrast);
-        mDynamicSchemeLight = dynamicSchemeFromStyle(
-                mThemeStyle, color, false /* isDark */, mContrast);
         mDynamicOverlay = createDynamicOverlay();
     }
 
@@ -682,10 +640,10 @@
 
     private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) {
         String suffix = isDark ? "dark" : "light";
-        DynamicScheme scheme = isDark ? mDynamicSchemeDark : mDynamicSchemeLight;
+        ColorScheme scheme = isDark ? mDarkColorScheme : mLightColorScheme;
         DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled).forEach(p -> {
             String resourceName = "android:color/system_" + p.first + "_" + suffix;
-            int colorValue = p.second.getArgb(scheme);
+            int colorValue = p.second.getArgb(scheme.getMaterialScheme());
             overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
                     null /* configuration */);
         });
@@ -694,7 +652,7 @@
     private void assignFixedColorsToOverlay(FabricatedOverlay overlay) {
         DynamicColors.getFixedColorsMapped(mIsFidelityEnabled).forEach(p -> {
             String resourceName = "android:color/system_" + p.first;
-            int colorValue = p.second.getArgb(mDynamicSchemeLight);
+            int colorValue = p.second.getArgb(mLightColorScheme.getMaterialScheme());
             overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
                     null /* configuration */);
         });
@@ -702,6 +660,7 @@
 
     /**
      * Checks if the color scheme in mColorScheme matches the current system palettes.
+     *
      * @param managedProfiles List of managed profiles for this user.
      */
     private boolean colorSchemeIsApplied(Set<UserHandle> managedProfiles) {
@@ -723,15 +682,18 @@
                     && res.getColor(android.R.color.system_neutral2_500, theme)
                     == mColorScheme.getNeutral2().getS500()
                     && res.getColor(android.R.color.system_outline_variant_dark, theme)
-                    == dynamicColors.outlineVariant().getArgb(mDynamicSchemeDark)
+                    == dynamicColors.outlineVariant().getArgb(mDarkColorScheme.getMaterialScheme())
                     && res.getColor(android.R.color.system_outline_variant_light, theme)
-                    == dynamicColors.outlineVariant().getArgb(mDynamicSchemeLight)
+                    == dynamicColors.outlineVariant().getArgb(mLightColorScheme.getMaterialScheme())
                     && res.getColor(android.R.color.system_primary_container_dark, theme)
-                    == dynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
+                    == dynamicColors.primaryContainer().getArgb(
+                    mDarkColorScheme.getMaterialScheme())
                     && res.getColor(android.R.color.system_primary_container_light, theme)
-                    == dynamicColors.primaryContainer().getArgb(mDynamicSchemeLight)
+                    == dynamicColors.primaryContainer().getArgb(
+                    mLightColorScheme.getMaterialScheme())
                     && res.getColor(android.R.color.system_primary_fixed, theme)
-                    == dynamicColors.primaryFixed().getArgb(mDynamicSchemeLight))) {
+                    == dynamicColors.primaryFixed().getArgb(
+                    mLightColorScheme.getMaterialScheme()))) {
                 return false;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index e18bc04..7931fab 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -21,7 +21,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -260,14 +259,12 @@
             public void moveFocusedTaskToFullscreen(int displayId) {
                 splitScreen.goToFullscreenFromSplit();
             }
-        });
-        splitScreen.registerSplitAnimationListener(new SplitScreen.SplitInvocationListener() {
+
             @Override
-            public void onSplitAnimationInvoked(boolean animationRunning) {
-                mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION, animationRunning)
-                        .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+            public void setSplitscreenFocus(boolean leftOrTop) {
+                splitScreen.setSplitscreenFocus(leftOrTop);
             }
-        }, mSysUiMainExecutor);
+        });
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 5882b56..572a6c1 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -113,7 +113,7 @@
             android:excludeFromRecents="true"
             />
 
-        <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
+        <activity android:name="com.android.systemui.screenshot.scroll.ScrollViewActivity"
                   android:exported="false" />
 
         <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
diff --git a/packages/SystemUI/tests/AndroidTest.xml b/packages/SystemUI/tests/AndroidTest.xml
index 31e7bd2..cd2a62d 100644
--- a/packages/SystemUI/tests/AndroidTest.xml
+++ b/packages/SystemUI/tests/AndroidTest.xml
@@ -32,4 +32,11 @@
         <option name="test-filter-dir" value="/data/data/com.android.systemui.tests" />
         <option name="hidden-api-checks" value="false"/>
     </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys"
+            value="/data/user/0/com.android.systemui.tests/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
 </configuration>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 90587d7..f1dfdf4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -20,16 +20,18 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
 
-import com.android.internal.jank.InteractionJankMonitor;
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.power.data.repository.FakePowerRepository;
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
 import com.android.systemui.res.R;
@@ -46,6 +48,7 @@
 
 public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
 
+    private KosmosJavaAdapter mKosmos;
     @Mock protected KeyguardStatusView mKeyguardStatusView;
 
     @Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
@@ -57,7 +60,6 @@
     @Mock protected ScreenOffAnimationController mScreenOffAnimationController;
     @Mock protected KeyguardLogger mKeyguardLogger;
     @Mock protected KeyguardStatusViewController mControllerMock;
-    @Mock protected InteractionJankMonitor mInteractionJankMonitor;
     @Mock protected ViewTreeObserver mViewTreeObserver;
     @Mock protected DumpManager mDumpManager;
     protected FakeKeyguardRepository mFakeKeyguardRepository;
@@ -71,6 +73,7 @@
 
     @Before
     public void setup() {
+        mKosmos = new KosmosJavaAdapter(this);
         MockitoAnnotations.initMocks(this);
 
         KeyguardInteractorFactory.WithDependencies deps = KeyguardInteractorFactory.create();
@@ -87,7 +90,7 @@
                 mDozeParameters,
                 mScreenOffAnimationController,
                 mKeyguardLogger,
-                mInteractionJankMonitor,
+                mKosmos.getInteractionJankMonitor(),
                 deps.getKeyguardInteractor(),
                 mDumpManager,
                 PowerInteractorFactory.create(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 336a97e..fde45d3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -135,6 +135,7 @@
 import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
 import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
@@ -195,7 +196,7 @@
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
             TEST_CARRIER_ID, PROFILE_CLASS_PROVISIONING);
     private static final int FINGERPRINT_SENSOR_ID = 1;
-
+    private KosmosJavaAdapter mKosmos;
     @Mock
     private UserTracker mUserTracker;
     @Mock
@@ -240,7 +241,6 @@
     private AuthController mAuthController;
     @Mock
     private TelephonyListenerManager mTelephonyListenerManager;
-    @Mock
     private InteractionJankMonitor mInteractionJankMonitor;
     @Mock
     private LatencyTracker mLatencyTracker;
@@ -300,6 +300,8 @@
 
     @Before
     public void setup() throws RemoteException {
+        mKosmos = new KosmosJavaAdapter(this);
+        mInteractionJankMonitor = mKosmos.getInteractionJankMonitor();
         MockitoAnnotations.initMocks(this);
         when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index d742da7..1c77351 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -147,6 +147,7 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
 
         mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
+        mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
 
         mFeatureFlags = new FakeFeatureFlags();
         mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
index b478d5c..c674294 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
@@ -38,7 +39,7 @@
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -65,7 +66,7 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
-    private ShadeViewController mShadeViewController;
+    private PanelExpansionInteractor mPanelExpansionInteractor;
     @Mock
     private Optional<Recents> mRecentsOptional;
     @Mock
@@ -87,7 +88,7 @@
                 mNotificationShadeController,
                 mKeyguardStateController,
                 mShadeController,
-                () -> mShadeViewController,
+                () -> mPanelExpansionInteractor,
                 mRecentsOptional,
                 mDisplayTracker);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 64936862..69cd592 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -476,12 +476,18 @@
             mValueAnimator.end();
         });
 
-        verify(mSpyController).enableWindowMagnificationInternal(
+        // Verify the method is called in
+        // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
+        // {@link Animator.AnimatorListener#onAnimationEnd} once in {@link ValueAnimator#end()}
+        verify(mSpyController, times(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
-        //Animating in reverse, so we only check if the start values are greater than current.
+
+        // The animation is playing forwards, so we only check the animated values are greater than
+        // the current one. (Asserting the first captured scale)
         assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
+        // The last captured scale equalsto the targetScale when we enable window magnification.
         assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
         assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
         assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
@@ -588,10 +594,10 @@
         final float expectedY = (int) (windowBounds.exactCenterY() + expectedOffset
                 - defaultMagnificationWindowSize / 2);
 
-        // This is called 4 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
+        // This is called 5 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
         // created and we place the mirrored content as a child of the SurfaceView
-        // (3) the animation starts (4) the animation updates
-        verify(mTransaction, times(4))
+        // (3) the animation starts (4) the animation updates (5) the animation ends
+        verify(mTransaction, times(5))
                 .setPosition(any(SurfaceControl.class), eq(expectedX), eq(expectedY));
     }
 
@@ -781,16 +787,18 @@
         // wait for animation returns
         waitForIdleSync();
 
-        // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} will only
-        // be triggered once in {@link ValueAnimator#end()}
-        verify(mSpyController).enableWindowMagnificationInternal(
+        // Verify the method is called in
+        // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
+        // {@link Animator.AnimatorListener#onAnimationEnd} once in {@link ValueAnimator#end()}
+        verify(mSpyController, times(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
 
-        //The animation is in verse, so we only check the start values should no be greater than
-        // the current one.
+        // The animation is playing backwards, so we only check the animated values should not be
+        // greater than the current one. (Asserting the first captured scale)
         assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
+        // The last captured scale is 1.0 when we delete window magnification.
         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
         verifyStartValue(mCenterXCaptor, Float.NaN);
         verifyStartValue(mCenterYCaptor, Float.NaN);
@@ -823,10 +831,15 @@
         resetMockObjects();
         deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2);
 
-        verify(mSpyController).enableWindowMagnificationInternal(
+        // Verify the method is called in
+        // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
+        // {@link Animator.AnimatorListener#onAnimationEnd} once when running the animation at
+        // the final duration time.
+        verify(mSpyController, times(2)).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
+        // The last captured scale is 1.0 when we delete window magnification.
         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
         verifyStartValue(mOffsetXCaptor, 0f);
         verifyStartValue(mOffsetYCaptor, 0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index 08b49e7..cbdc696 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -132,7 +132,8 @@
 public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase {
 
     @Rule
-    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
+    // NOTE: pass 'null' to allow this test advances time on the main thread.
+    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(/* test= */ null);
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
index 96ce3ab..b73e4e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
@@ -18,6 +18,8 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.policy.DecorView
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.kosmos.Kosmos
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
@@ -31,7 +33,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.any
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
@@ -43,7 +44,7 @@
     private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
     private val attachedViews = mutableSetOf<View>()
 
-    @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+    val interactionJankMonitor = Kosmos().interactionJankMonitor
     @get:Rule val rule = MockitoJUnit.rule()
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 416b334..072569d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -399,6 +399,7 @@
     @Test
     @Ignore("b/302735104")
     fun testShowCredentialUI_withCustomBp() {
+        mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
         val container = initializeFingerprintContainer(
                 authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
                 isUsingContentView = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
index 21b8aca..c79cbab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
@@ -85,7 +85,9 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onStateChanged(TEST_FOLDED)
+            deviceStateManagerListener.value.onDeviceStateChanged(
+                getDeviceStateForIdentifier(TEST_FOLDED)
+            )
 
             assertThat(state()).isEqualTo(DeviceState.FOLDED)
         }
@@ -95,7 +97,9 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onStateChanged(TEST_HALF_FOLDED)
+            deviceStateManagerListener.value.onDeviceStateChanged(
+                getDeviceStateForIdentifier(TEST_HALF_FOLDED)
+            )
 
             assertThat(state()).isEqualTo(DeviceState.HALF_FOLDED)
         }
@@ -105,7 +109,9 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onStateChanged(TEST_UNFOLDED)
+            deviceStateManagerListener.value.onDeviceStateChanged(
+                getDeviceStateForIdentifier(TEST_UNFOLDED)
+            )
 
             assertThat(state()).isEqualTo(DeviceState.UNFOLDED)
         }
@@ -115,7 +121,9 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onStateChanged(TEST_REAR_DISPLAY)
+            deviceStateManagerListener.value.onDeviceStateChanged(
+                getDeviceStateForIdentifier(TEST_REAR_DISPLAY)
+            )
 
             assertThat(state()).isEqualTo(DeviceState.REAR_DISPLAY)
         }
@@ -125,7 +133,9 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onStateChanged(TEST_CONCURRENT_DISPLAY)
+            deviceStateManagerListener.value.onDeviceStateChanged(
+                getDeviceStateForIdentifier(TEST_CONCURRENT_DISPLAY)
+            )
 
             assertThat(state()).isEqualTo(DeviceState.CONCURRENT_DISPLAY)
         }
@@ -135,7 +145,9 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onStateChanged(123456)
+            deviceStateManagerListener.value.onDeviceStateChanged(
+                getDeviceStateForIdentifier(123456)
+            )
 
             assertThat(state()).isEqualTo(DeviceState.UNKNOWN)
         }
@@ -152,6 +164,13 @@
 
     private fun Int.toIntArray() = listOf(this).toIntArray()
 
+    private fun getDeviceStateForIdentifier(id: Int): android.hardware.devicestate.DeviceState {
+        return android.hardware.devicestate.DeviceState(
+            android.hardware.devicestate.DeviceState.Configuration.Builder(id, /* name= */ "")
+                .build()
+        )
+    }
+
     private companion object {
         // Used to fake the ids in the test. Note that there is no guarantees different devices will
         // have the same ids (that's why the ones in this test start from 41)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1849245..272b488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -75,7 +75,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.foldables.FoldGracePeriodProvider;
-import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.LockPatternUtils;
@@ -181,7 +180,6 @@
     private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
     private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private @Mock ScreenOffAnimationController mScreenOffAnimationController;
-    private @Mock InteractionJankMonitor mInteractionJankMonitor;
     private @Mock ScreenOnCoordinator mScreenOnCoordinator;
     private @Mock KeyguardTransitions mKeyguardTransitions;
     private @Mock ShadeController mShadeController;
@@ -235,8 +233,6 @@
         when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
         when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
         when(mPowerManager.isInteractive()).thenReturn(true);
-        when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
-        when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
         final ViewRootImpl testViewRoot = mock(ViewRootImpl.class);
         when(testViewRoot.getView()).thenReturn(mock(View.class));
@@ -1245,7 +1241,7 @@
                 () -> mNotificationShadeDepthController,
                 mScreenOnCoordinator,
                 mKeyguardTransitions,
-                mInteractionJankMonitor,
+                mKosmos.getInteractionJankMonitor(),
                 mDreamOverlayStateController,
                 mJavaAdapter,
                 mWallpaperRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 7ee8963..1839d8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -37,8 +37,6 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.selectedUserInteractor
 import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertTrue
-import junit.framework.Assert.fail
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -144,73 +142,6 @@
         }
 
     @Test
-    fun testSurfaceBehindModel() =
-        testScope.runTest {
-            val values by collectValues(underTest.surfaceBehindModel)
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.LOCKSCREEN,
-                )
-            )
-            runCurrent()
-
-            assertEquals(
-                listOf(
-                    null, // PRIMARY_BOUNCER -> LOCKSCREEN does not have specific view params.
-                ),
-                values
-            )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GONE,
-                )
-            )
-            runCurrent()
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.RUNNING,
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GONE,
-                    value = 0.01f,
-                )
-            )
-            runCurrent()
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.RUNNING,
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GONE,
-                    value = 0.99f,
-                )
-            )
-            runCurrent()
-
-            assertEquals(3, values.size)
-            val model1percent = values[1]
-            val model99percent = values[2]
-
-            try {
-                // We should initially have an alpha of 0f when unlocking, so the surface is not
-                // visible
-                // while lockscreen UI animates out.
-                assertEquals(0f, model1percent!!.alpha)
-
-                // By the end it should probably be visible.
-                assertTrue(model99percent!!.alpha > 0f)
-            } catch (e: NullPointerException) {
-                fail("surfaceBehindModel was unexpectedly null.")
-            }
-        }
-
-    @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testReturnToLockscreen_whenBouncerHides() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 170d348..b6b4571 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -17,6 +17,8 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
@@ -76,6 +78,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
     fun testAppliesSplitShadeBlueprint() {
         testScope.runTest {
             val blueprint by collectLastValue(underTest.blueprint)
@@ -88,6 +91,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
     fun fingerprintPropertyInitialized_updatesBlueprint() {
         testScope.runTest {
             val blueprint by collectLastValue(underTest.blueprint)
@@ -99,9 +103,9 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
     fun composeLockscreenOff_DoesAppliesSplitShadeWeatherClockBlueprint() {
         testScope.runTest {
-            mSetFlagsRule.disableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
             val blueprint by collectLastValue(underTest.blueprint)
             whenever(clockController.config)
                 .thenReturn(
@@ -121,9 +125,9 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
     fun testDoesAppliesSplitShadeWeatherClockBlueprint() {
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
             val blueprint by collectLastValue(underTest.blueprint)
             whenever(clockController.config)
                 .thenReturn(
@@ -143,9 +147,9 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
     fun testAppliesWeatherClockBlueprint() {
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
             val blueprint by collectLastValue(underTest.blueprint)
             whenever(clockController.config)
                 .thenReturn(
@@ -163,4 +167,18 @@
             assertThat(blueprint?.id).isEqualTo(WEATHER_CLOCK_BLUEPRINT_ID)
         }
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+    fun testDoesNotApplySplitShadeBlueprint() {
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, true)
+            val blueprint by collectLastValue(underTest.blueprint)
+            clockRepository.setCurrentClock(clockController)
+            configurationRepository.onConfigurationChange()
+            runCurrent()
+
+            assertThat(blueprint?.id).isEqualTo(DefaultKeyguardBlueprint.DEFAULT)
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 09c56b0..58273d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -107,6 +107,7 @@
 
     @Test
     fun addViewsConditionally_migrateFlagOff() {
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
         mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val constraintLayout = ConstraintLayout(context, null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index f252163..0322301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -33,6 +33,8 @@
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shared.clocks.ClockRegistry
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.policy.SplitShadeStateController
@@ -42,6 +44,7 @@
 import kotlin.test.Test
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.TestScope
@@ -72,6 +75,7 @@
     @Mock private lateinit var splitShadeStateController: SplitShadeStateController
     @Mock private lateinit var notifsKeyguardInteractor: NotificationsKeyguardInteractor
     @Mock private lateinit var areNotificationsFullyHidden: Flow<Boolean>
+    @Mock private lateinit var shadeInteractor: ShadeInteractor
 
     @Before
     fun setup() {
@@ -96,13 +100,14 @@
         keyguardClockInteractor = KeyguardClockInteractor(keyguardClockRepository)
         whenever(notifsKeyguardInteractor.areNotificationsFullyHidden)
             .thenReturn(areNotificationsFullyHidden)
+        whenever(shadeInteractor.shadeMode).thenReturn(MutableStateFlow(ShadeMode.Single))
         underTest =
             KeyguardClockViewModel(
                 keyguardInteractor,
                 keyguardClockInteractor,
                 scope.backgroundScope,
-                splitShadeStateController,
-                notifsKeyguardInteractor
+                notifsKeyguardInteractor,
+                shadeInteractor,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
new file mode 100644
index 0000000..e53cd11
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardClockViewModelWithKosmosTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val underTest = kosmos.keyguardClockViewModel
+    private val testScope = kosmos.testScope
+
+    @Test
+    fun currentClockLayout_splitShadeOn_clockCentered_largeClock() =
+        testScope.runTest {
+            with(kosmos) {
+                shadeRepository.setShadeMode(ShadeMode.Split)
+                keyguardRepository.setClockShouldBeCentered(true)
+                keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+            }
+            val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+            assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+        }
+
+    @Test
+    fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
+        testScope.runTest {
+            with(kosmos) {
+                shadeRepository.setShadeMode(ShadeMode.Split)
+                keyguardRepository.setClockShouldBeCentered(false)
+                keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+            }
+            val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+            assertThat(currentClockLayout)
+                .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK)
+        }
+
+    @Test
+    fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
+        testScope.runTest {
+            with(kosmos) {
+                shadeRepository.setShadeMode(ShadeMode.Split)
+                keyguardRepository.setClockShouldBeCentered(false)
+                keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+            }
+            val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+            assertThat(currentClockLayout)
+                .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK)
+        }
+
+    @Test
+    fun currentClockLayout_singleShade_smallClock_smallClock() =
+        testScope.runTest {
+            with(kosmos) {
+                shadeRepository.setShadeMode(ShadeMode.Single)
+                keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+            }
+            val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+            assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.SMALL_CLOCK)
+        }
+
+    @Test
+    fun currentClockLayout_singleShade_largeClock_largeClock() =
+        testScope.runTest {
+            with(kosmos) {
+                shadeRepository.setShadeMode(ShadeMode.Single)
+                keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+            }
+            val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+            assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index d75553f..b593def 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -81,7 +81,7 @@
     @Test
     fun loadRecentTasks_singleTask_returnsTaskAsNotForeground() {
         givenRecentTasks(
-            createSingleTask(taskId = 1),
+            createSingleTask(taskId = 1, isVisible = true),
         )
 
         val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
@@ -90,10 +90,10 @@
     }
 
     @Test
-    fun loadRecentTasks_multipleTasks_returnsSecondTaskAsForegroundTask() {
+    fun loadRecentTasks_multipleTasks_returnsSecondVisibleTaskAsForegroundTask() {
         givenRecentTasks(
             createSingleTask(taskId = 1),
-            createSingleTask(taskId = 2),
+            createSingleTask(taskId = 2, isVisible = true),
             createSingleTask(taskId = 3),
         )
 
@@ -103,10 +103,25 @@
     }
 
     @Test
-    fun loadRecentTasks_secondTaskIsGrouped_marksBothGroupedTasksAsForeground() {
+    fun loadRecentTasks_multipleTasks_returnsSecondInvisibleTaskAsNotForegroundTask() {
         givenRecentTasks(
             createSingleTask(taskId = 1),
-            createTaskPair(taskId1 = 2, taskId2 = 3),
+            createSingleTask(taskId = 2, isVisible = false),
+            createSingleTask(taskId = 3),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result.map { it.isForegroundTask })
+            .containsExactly(false, false, false)
+            .inOrder()
+    }
+
+    @Test
+    fun loadRecentTasks_secondTaskIsGroupedAndVisible_marksBothGroupedTasksAsForeground() {
+        givenRecentTasks(
+            createSingleTask(taskId = 1),
+            createTaskPair(taskId1 = 2, taskId2 = 3, isVisible = true),
             createSingleTask(taskId = 4),
         )
 
@@ -117,6 +132,21 @@
             .inOrder()
     }
 
+    @Test
+    fun loadRecentTasks_secondTaskIsGroupedAndInvisible_marksBothGroupedTasksAsNotForeground() {
+        givenRecentTasks(
+            createSingleTask(taskId = 1),
+            createTaskPair(taskId1 = 2, taskId2 = 3, isVisible = false),
+            createSingleTask(taskId = 4),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result.map { it.isForegroundTask })
+            .containsExactly(false, false, false, false)
+            .inOrder()
+    }
+
     @Suppress("UNCHECKED_CAST")
     private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
         whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
@@ -136,11 +166,23 @@
             isForegroundTask = false,
         )
 
-    private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
-        GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
+    private fun createSingleTask(taskId: Int, isVisible: Boolean = false): GroupedRecentTaskInfo =
+        GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId, isVisible))
 
-    private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
-        GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
+    private fun createTaskPair(
+        taskId1: Int,
+        taskId2: Int,
+        isVisible: Boolean = false
+    ): GroupedRecentTaskInfo =
+        GroupedRecentTaskInfo.forSplitTasks(
+            createTaskInfo(taskId1, isVisible),
+            createTaskInfo(taskId2, isVisible),
+            null
+        )
 
-    private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
+    private fun createTaskInfo(taskId: Int, isVisible: Boolean = false) =
+        RecentTaskInfo().apply {
+            this.taskId = taskId
+            this.isVisible = isVisible
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
new file mode 100644
index 0000000..261e8c0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
@@ -0,0 +1,249 @@
+/*
+ * 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.systemui.monet
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.util.Log
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.theme.DynamicColors
+import com.google.ux.material.libmonet.hct.Hct
+import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
+import java.io.File
+import java.io.FileWriter
+import java.io.StringWriter
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.transform.OutputKeys
+import javax.xml.transform.TransformerException
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.dom.DOMSource
+import javax.xml.transform.stream.StreamResult
+import kotlin.math.abs
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.w3c.dom.Document
+import org.w3c.dom.Element
+import org.w3c.dom.Node
+
+private const val fileHeader =
+    """
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+"""
+
+private fun testName(name: String): String {
+    return "Auto generated by: atest ColorSchemeTest#$name"
+}
+
+private const val commentRoles =
+    "Colors used in Android system, from design system. These " +
+        "values can be overlaid at runtime by OverlayManager RROs."
+
+private const val commentOverlay = "This value can be overlaid at runtime by OverlayManager RROs."
+
+private fun commentWhite(paletteName: String): String {
+    return "Lightest shade of the $paletteName color used by the system. White. $commentOverlay"
+}
+
+private fun commentBlack(paletteName: String): String {
+    return "Darkest shade of the $paletteName color used by the system. Black. $commentOverlay"
+}
+
+private fun commentShade(paletteName: String, tone: Int): String {
+    return "Shade of the $paletteName system color at $tone% perceptual luminance (L* in L*a*b* " +
+        "color space). $commentOverlay"
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ColorSchemeTest : SysuiTestCase() {
+    @Test
+    fun generateThemeStyles() {
+        val document = buildDoc<Any>()
+
+        val themes = document.createElement("themes")
+        document.appendWithBreak(themes)
+
+        var hue = 0.0
+        while (hue < 360) {
+            val sourceColor = Hct.from(hue, 50.0, 50.0)
+            val sourceColorHex = sourceColor.toInt().toRGBHex()
+
+            val theme = document.createElement("theme")
+            theme.setAttribute("color", sourceColorHex)
+            themes.appendChild(theme)
+
+            for (styleValue in Style.entries) {
+                if (
+                    styleValue == Style.CLOCK ||
+                        styleValue == Style.CLOCK_VIBRANT ||
+                        styleValue == Style.CONTENT
+                ) {
+                    continue
+                }
+
+                val style = document.createElement(styleValue.name.lowercase())
+                val colorScheme = ColorScheme(sourceColor.toInt(), false, styleValue)
+
+                style.appendChild(
+                    document.createTextNode(
+                        listOf(
+                                colorScheme.accent1,
+                                colorScheme.accent2,
+                                colorScheme.accent3,
+                                colorScheme.neutral1,
+                                colorScheme.neutral2
+                            )
+                            .flatMap { a -> listOf(*a.allShades.toTypedArray()) }
+                            .joinToString(",", transform = Int::toRGBHex)
+                    )
+                )
+                theme.appendChild(style)
+            }
+
+            hue += 60
+        }
+
+        saveFile(document, "current_themes.xml")
+    }
+
+    @Test
+    fun generateDefaultValues() {
+        val document = buildDoc<Any>()
+
+        val resources = document.createElement("resources")
+        document.appendWithBreak(resources)
+
+        // shade colors
+        val colorScheme = ColorScheme(GOOGLE_BLUE, false)
+        arrayOf(
+                Triple("accent1", "Primary", colorScheme.accent1),
+                Triple("accent2", "Secondary", colorScheme.accent2),
+                Triple("accent3", "Tertiary", colorScheme.accent3),
+                Triple("neutral1", "Neutral", colorScheme.neutral1),
+                Triple("neutral2", "Secondary Neutral", colorScheme.neutral2)
+            )
+            .forEach {
+                val (paletteName, readable, palette) = it
+                palette.allShadesMapped.entries.forEachIndexed { index, (shade, colorValue) ->
+                    val comment =
+                        when (index) {
+                            0 -> commentWhite(readable)
+                            palette.allShadesMapped.entries.size - 1 -> commentBlack(readable)
+                            else -> commentShade(readable, abs(shade / 10 - 100))
+                        }
+                    resources.createColorEntry("system_${paletteName}_$shade", colorValue, comment)
+                }
+            }
+
+        resources.appendWithBreak(document.createComment(commentRoles), 2)
+
+        // dynamic colors
+        arrayOf(false, true).forEach { isDark ->
+            val suffix = if (isDark) "_dark" else "_light"
+            val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5)
+            DynamicColors.allDynamicColorsMapped(false).forEach {
+                resources.createColorEntry(
+                    "system_${it.first}$suffix",
+                    it.second.getArgb(dynamicScheme)
+                )
+            }
+        }
+
+        // fixed colors
+        val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5)
+        DynamicColors.getFixedColorsMapped(false).forEach {
+            resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme))
+        }
+
+        saveFile(document, "role_values.xml")
+    }
+
+    // Helper Functions
+
+    private inline fun <reified T> buildDoc(): Document {
+        val functionName = T::class.simpleName + ""
+        val factory = DocumentBuilderFactory.newInstance()
+        val builder = factory.newDocumentBuilder()
+        val document = builder.newDocument()
+
+        document.appendWithBreak(document.createComment(fileHeader))
+        document.appendWithBreak(document.createComment(testName(functionName)))
+
+        return document
+    }
+
+    private fun documentToString(document: Document): String {
+        try {
+            val transformerFactory = TransformerFactory.newInstance()
+            val transformer = transformerFactory.newTransformer()
+            transformer.setOutputProperty(OutputKeys.MEDIA_TYPE, "application/xml")
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml")
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes")
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4")
+
+            val stringWriter = StringWriter()
+            transformer.transform(DOMSource(document), StreamResult(stringWriter))
+            return stringWriter.toString()
+        } catch (e: TransformerException) {
+            throw RuntimeException("Error transforming XML", e)
+        }
+    }
+
+    private fun saveFile(document: Document, fileName: String) {
+        val outPath = context.filesDir.path + "/" + fileName
+        Log.d("ColorSchemeXml", "Artifact $fileName created")
+        val writer = FileWriter(File(outPath))
+        writer.write(documentToString(document))
+        writer.close()
+    }
+}
+
+private fun Element.createColorEntry(name: String, value: Int, comment: String? = null) {
+    val doc = this.ownerDocument
+
+    if (comment != null) {
+        this.appendChild(doc.createComment(comment))
+    }
+
+    val color = doc.createElement("color")
+    this.appendChild(color)
+
+    color.setAttribute("name", name)
+    color.appendChild(doc.createTextNode("#" + value.toRGBHex()))
+}
+
+private fun Node.appendWithBreak(child: Node, lineBreaks: Int = 1): Node {
+    val doc = if (this is Document) this else this.ownerDocument
+    val node = doc.createTextNode("\n".repeat(lineBreaks))
+    this.appendChild(node)
+    return this.appendChild(child)
+}
+
+private fun Int.toRGBHex(): String {
+    return "%06X".format(0xFFFFFF and this)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index dc211303..f88a5a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -19,7 +19,6 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotSame;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.reset;
@@ -28,6 +27,7 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -38,9 +38,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.res.R;
 import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.CommandQueue;
@@ -176,6 +174,6 @@
             DeviceStateManager.DeviceStateCallback {
 
         @Override
-        public void onStateChanged(int state) { }
+        public void onDeviceStateChanged(DeviceState state) { }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
deleted file mode 100644
index 9ea30d6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ /dev/null
@@ -1,129 +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.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class ActionProxyReceiverTest extends SysuiTestCase {
-    @Mock
-    private ActivityManagerWrapper mMockActivityManagerWrapper;
-    @Mock
-    private ScreenshotSmartActions mMockScreenshotSmartActions;
-    @Mock
-    private PendingIntent mMockPendingIntent;
-    @Mock
-    private ActivityStarter mActivityStarter;
-
-    private Intent mIntent;
-    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
-    @Before
-    public void setup() throws InterruptedException, ExecutionException, TimeoutException {
-        MockitoAnnotations.initMocks(this);
-        mIntent = new Intent(mContext, ActionProxyReceiver.class)
-                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
-    }
-
-    @Test
-    public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
-        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
-        // ensure that the pending intent call is passed through
-        doAnswer((Answer<Object>) invocation -> {
-            ((Runnable) invocation.getArgument(0)).run();
-            return null;
-        }).when(mActivityStarter).executeRunnableDismissingKeyguard(
-                any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
-
-        actionProxyReceiver.onReceive(mContext, mIntent);
-
-        verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
-        verify(mActivityStarter).executeRunnableDismissingKeyguard(
-                any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
-        verify(mMockPendingIntent).send(
-                eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
-    }
-
-    @Test
-    public void testSmartActionsNotNotifiedByDefault() {
-        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
-
-        actionProxyReceiver.onReceive(mContext, mIntent);
-
-        verify(mMockScreenshotSmartActions, never())
-                .notifyScreenshotAction(anyString(), anyString(), anyBoolean(),
-                        any(Intent.class));
-    }
-
-    @Test
-    public void testSmartActionsNotifiedIfEnabled() {
-        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
-        mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
-        String testId = "testID";
-        mIntent.putExtra(EXTRA_ID, testId);
-
-        actionProxyReceiver.onReceive(mContext, mIntent);
-
-        verify(mMockScreenshotSmartActions).notifyScreenshotAction(
-                testId, ACTION_TYPE_SHARE, false, null);
-    }
-
-    private ActionProxyReceiver constructActionProxyReceiver() {
-        return new ActionProxyReceiver(
-                mMockActivityManagerWrapper,
-                mMockScreenshotSmartActions,
-                mDisplayTracker,
-                mActivityStarter
-        );
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
deleted file mode 100644
index d58f47a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ /dev/null
@@ -1,145 +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.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Environment;
-import android.provider.MediaStore;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class DeleteScreenshotReceiverTest extends SysuiTestCase {
-
-    @Mock
-    private ScreenshotSmartActions mMockScreenshotSmartActions;
-    @Mock
-    private Executor mMockExecutor;
-
-    private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
-    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mDeleteScreenshotReceiver =
-                new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
-    }
-
-    @Test
-    public void testNoUriProvided() {
-        Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
-
-        mDeleteScreenshotReceiver.onReceive(mContext, intent);
-
-        verify(mMockExecutor, never()).execute(any(Runnable.class));
-        verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
-                any(String.class), any(String.class), anyBoolean(),
-                any(Intent.class));
-    }
-
-    @Test
-    public void testFileDeleted() {
-        DeleteScreenshotReceiver deleteScreenshotReceiver =
-                new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
-        ContentResolver contentResolver = mContext.getContentResolver();
-        final Uri testUri = contentResolver.insert(
-                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
-        assertNotNull(testUri);
-
-        try {
-            Cursor cursor =
-                    contentResolver.query(testUri, null, null, null, null);
-            assertEquals(1, cursor.getCount());
-            Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
-                    .putExtra(SCREENSHOT_URI_ID, testUri.toString());
-
-            deleteScreenshotReceiver.onReceive(mContext, intent);
-            int runCount = mFakeExecutor.runAllReady();
-
-            assertEquals(1, runCount);
-            cursor =
-                    contentResolver.query(testUri, null, null, null, null);
-            assertEquals(0, cursor.getCount());
-        } finally {
-            contentResolver.delete(testUri, null, null);
-        }
-
-        // ensure smart actions not called by default
-        verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
-                any(String.class), any(String.class), anyBoolean(), any(Intent.class));
-    }
-
-    @Test
-    public void testNotifyScreenshotAction() {
-        Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
-        String uriString = "testUri";
-        String testId = "testID";
-        intent.putExtra(SCREENSHOT_URI_ID, uriString);
-        intent.putExtra(EXTRA_ID, testId);
-        intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
-
-        mDeleteScreenshotReceiver.onReceive(mContext, intent);
-
-        verify(mMockExecutor).execute(any(Runnable.class));
-        verify(mMockScreenshotSmartActions).notifyScreenshotAction(testId,
-                ACTION_TYPE_DELETE, false, null);
-    }
-
-    private static ContentValues getFakeContentValues() {
-        final ContentValues values = new ContentValues();
-        values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
-                + File.separator + Environment.DIRECTORY_SCREENSHOTS);
-        values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
-        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
-        values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
-        values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
-        return values;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
index 4c8a4b0..aad46139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import static com.google.common.util.concurrent.Futures.getUnchecked;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
index 670a130..1023260 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 
 import static org.junit.Assert.assertEquals;
@@ -37,8 +37,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
index 6f081c7..f39f543 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import static com.google.common.util.concurrent.Futures.getUnchecked;
 import static com.google.common.util.concurrent.Futures.immediateFuture;
@@ -36,7 +36,7 @@
 
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
index de97bc3..5699cfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
index 4c84df2..04aba11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.app.Activity;
 import android.os.Bundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index c31c625..1ee26db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -196,7 +196,8 @@
                 new ConfigurationInteractor(configurationRepository),
                 shadeRepository,
                 keyguardTransitionInteractor,
-                () -> sceneInteractor);
+                () -> sceneInteractor,
+                () -> mKosmos.getFromGoneTransitionInteractor());
         CommunalInteractor communalInteractor = mKosmos.getCommunalInteractor();
 
         mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index a077164..b9451ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -17,7 +17,6 @@
 package com.android.systemui.shade;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -223,7 +222,8 @@
                 new ConfigurationInteractor(configurationRepository),
                 mShadeRepository,
                 keyguardTransitionInteractor,
-                () -> sceneInteractor);
+                () -> sceneInteractor,
+                () -> mKosmos.getFromGoneTransitionInteractor());
 
         mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
         mFromPrimaryBouncerTransitionInteractor =
@@ -289,10 +289,6 @@
 
         when(mNotificationRemoteInputManager.isRemoteInputActive())
                 .thenReturn(false);
-        when(mInteractionJankMonitor.begin(any(), anyInt()))
-                .thenReturn(true);
-        when(mInteractionJankMonitor.end(anyInt()))
-                .thenReturn(true);
 
         when(mPanelView.getParent()).thenReturn(mPanelViewParent);
         when(mQs.getHeader()).thenReturn(mQsHeader);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 103dcb7..dcd000a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -153,6 +154,7 @@
                 shadeRepository,
                 keyguardTransitionInteractor,
                 { kosmos.sceneInteractor },
+                { kosmos.fromGoneTransitionInteractor },
             )
 
         whenever(deviceEntryUdfpsInteractor.isUdfpsSupported).thenReturn(MutableStateFlow(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index dcbb93a..d5c4053 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -49,9 +49,8 @@
 import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.PanelExpansionInteractorImpl;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -79,7 +78,7 @@
     @Mock private CommandQueue mCommandQueue;
     @Mock private QuickSettingsController mQuickSettingsController;
     @Mock private ShadeViewController mShadeViewController;
-    @Mock private PanelExpansionInteractorImpl mPanelExpansionInteractor;
+    @Mock private PanelExpansionInteractor mPanelExpansionInteractor;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -129,7 +128,6 @@
                 mStatusBarHideIconsForBouncerManager,
                 mPowerManager,
                 Optional.of(mVibrator),
-                new DisableFlagsLogger(),
                 DEFAULT_DISPLAY,
                 mCameraLauncherLazy,
                 mUserTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c8c54dbd..611cf91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -49,6 +49,7 @@
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.IntentFilter;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.fingerprint.FingerprintManager;
@@ -1113,10 +1114,12 @@
     }
 
     private void setDeviceState(int state) {
+        DeviceState deviceState = new DeviceState(
+                new DeviceState.Configuration.Builder(state, "TEST").build());
         ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
                 ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
         verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture());
-        callbackCaptor.getValue().onStateChanged(state);
+        callbackCaptor.getValue().onDeviceStateChanged(deviceState);
     }
 
     private void setGoToSleepStates(int... states) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
index 649dc23..5d42d51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.statusbar.phone
 
+import android.hardware.devicestate.DeviceState
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.internal.R
@@ -40,52 +41,52 @@
     @Before
     fun setUp() {
         initMocks(this)
-        setFoldedStates(DEVICE_STATE_FOLDED)
-        setGoToSleepStates(DEVICE_STATE_FOLDED)
+        setFoldedStates(DEVICE_STATE_FOLDED.identifier)
+        setGoToSleepStates(DEVICE_STATE_FOLDED.identifier)
         sut = FoldStateListener(mContext, listener)
     }
 
     @Test
     fun onStateChanged_stateFolded_notifiesWithFoldedAndGoingToSleep() {
-        sut.onStateChanged(DEVICE_STATE_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_FOLDED)
 
         verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
     }
 
     @Test
     fun onStateChanged_stateHalfFolded_notifiesWithNotFoldedAndNotGoingToSleep() {
-        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
 
         verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
     }
 
     @Test
     fun onStateChanged_stateUnfolded_notifiesWithNotFoldedAndNotGoingToSleep() {
-        sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_UNFOLDED)
 
         verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
     }
 
     @Test
     fun onStateChanged_stateUnfoldedThenHalfFolded_notifiesOnce() {
-        sut.onStateChanged(DEVICE_STATE_UNFOLDED)
-        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_UNFOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
 
         verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
     }
 
     @Test
     fun onStateChanged_stateHalfFoldedThenUnfolded_notifiesOnce() {
-        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
-        sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_UNFOLDED)
 
         verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
     }
 
     @Test
     fun onStateChanged_stateHalfFoldedThenFolded_notifiesTwice() {
-        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
-        sut.onStateChanged(DEVICE_STATE_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_FOLDED)
 
         val inOrder = Mockito.inOrder(listener)
         inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
@@ -94,8 +95,8 @@
 
     @Test
     fun onStateChanged_stateFoldedThenHalfFolded_notifiesTwice() {
-        sut.onStateChanged(DEVICE_STATE_FOLDED)
-        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_FOLDED)
+        sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
 
         val inOrder = Mockito.inOrder(listener)
         inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
@@ -117,9 +118,18 @@
     }
 
     companion object {
-        private const val DEVICE_STATE_FOLDED = 123
-        private const val DEVICE_STATE_HALF_FOLDED = 456
-        private const val DEVICE_STATE_UNFOLDED = 789
+        private val DEVICE_STATE_FOLDED = DeviceState(
+            DeviceState.Configuration.Builder(123 /* id */, "FOLDED" /* name */)
+                    .build()
+        )
+        private val DEVICE_STATE_HALF_FOLDED = DeviceState(
+            DeviceState.Configuration.Builder(456 /* id */, "HALF_FOLDED" /* name */)
+                    .build()
+        )
+        private val DEVICE_STATE_UNFOLDED = DeviceState(
+            DeviceState.Configuration.Builder(789 /* id */, "UNFOLDED" /* name */)
+                    .build()
+        )
 
         private const val FOLDED = true
         private const val NOT_FOLDED = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 1748cff..d9e9c59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -177,7 +177,8 @@
                 new ConfigurationInteractor(new FakeConfigurationRepository()),
                 new FakeShadeRepository(),
                 keyguardTransitionInteractor,
-                () -> mKosmos.getSceneInteractor());
+                () -> mKosmos.getSceneInteractor(),
+                () -> mKosmos.getFromGoneTransitionInteractor());
         mViewModel =
                 new KeyguardStatusBarViewModel(
                         mTestScope.getBackgroundScope(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 57a89b2..de18913 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.StatusBarStateControllerImpl
@@ -67,6 +68,8 @@
     @Mock
     private lateinit var shadeViewController: ShadeViewController
     @Mock
+    private lateinit var panelExpansionInteractor: PanelExpansionInteractor
+    @Mock
     private lateinit var notifShadeWindowController: NotificationShadeWindowController
     @Mock
     private lateinit var lightRevealScrim: LightRevealScrim
@@ -87,17 +90,18 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         controller = UnlockedScreenOffAnimationController(
-                context,
-                wakefulnessLifecycle,
-                statusBarStateController,
-                { keyguardViewMediator },
-                keyguardStateController,
-                { dozeParameters },
-                globalSettings,
-                { notifShadeWindowController },
-                interactionJankMonitor,
-                powerManager,
-                handler = handler
+            context,
+            wakefulnessLifecycle,
+            statusBarStateController,
+            { keyguardViewMediator },
+            keyguardStateController,
+            { dozeParameters },
+            globalSettings,
+            { notifShadeWindowController },
+            interactionJankMonitor,
+            powerManager,
+            { panelExpansionInteractor },
+            handler,
         )
         controller.initialize(centralSurfaces, shadeViewController, lightRevealScrim)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
index ce47170..c606511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy
 
+import android.hardware.devicestate.DeviceState
 import android.hardware.devicestate.DeviceStateManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -30,6 +31,7 @@
 import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -72,6 +74,18 @@
             com.android.internal.R.array.config_device_state_postures,
             deviceStateToPostureMapping
         )
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(
+                listOf(
+                    DEVICE_STATE_CLOSED,
+                    DEVICE_STATE_HALF_FOLDED,
+                    DEVICE_STATE_OPENED,
+                    DEVICE_STATE_FLIPPED,
+                    DEVICE_STATE_UNKNOWN,
+                    DEVICE_STATE_USE_BASE_STATE
+                )
+            )
+
         underTest =
             DevicePostureControllerImpl(
                 context,
@@ -86,20 +100,20 @@
         var posture = -1
         underTest.addCallback { posture = it }
 
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
-        assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
-
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_CLOSED)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
 
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
 
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_OPENED)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
 
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_FLIPPED)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
+
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_UNKNOWN)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
     }
 
     @Test
@@ -107,15 +121,26 @@
         var posture = -1
         underTest.addCallback { posture = it }
 
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
 
-        // base state change doesn't change the posture
-        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+        val physicalProperties =
+            setOf(DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+        val updatedState =
+            DeviceState(
+                DeviceState.Configuration.Builder(
+                        DEVICE_STATE_HALF_FOLDED.identifier,
+                        DEVICE_STATE_HALF_FOLDED.name
+                    )
+                    .setPhysicalProperties(physicalProperties)
+                    .build()
+            )
+        // state change with updated physical properties shouldn't cause a posture change
+        deviceStateCallback.value.onDeviceStateChanged(updatedState)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
 
-        // WHEN the display state maps to using the base state, then posture updates
-        deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
+        // WHEN the display state maps to the physical state, then posture updates
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_USE_BASE_STATE)
         assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
     }
 
@@ -124,20 +149,97 @@
         var numPostureChanges = 0
         underTest.addCallback { numPostureChanges++ }
 
-        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
         assertThat(numPostureChanges).isEqualTo(1)
 
-        // base state changes doesn't send another posture update since the device state isn't
-        // useBaseStateDeviceState
-        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
-        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
-        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
-        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
-        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
+        // update to physical properties doesn't send another posture update since the device state
+        // isn't useBaseStateDeviceState
+        deviceStateCallback.value.onDeviceStateChanged(
+            getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_CLOSED)
+        )
+        deviceStateCallback.value.onDeviceStateChanged(
+            getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_HALF_FOLDED)
+        )
+        deviceStateCallback.value.onDeviceStateChanged(
+            getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_OPENED)
+        )
+        deviceStateCallback.value.onDeviceStateChanged(
+            getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_UNKNOWN)
+        )
         assertThat(numPostureChanges).isEqualTo(1)
     }
 
     private fun verifyRegistersForDeviceStateCallback() {
         verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
     }
+
+    private fun getStateUpdatedPhysicalProperties(
+        currentState: DeviceState,
+        physicalState: DeviceState
+    ): DeviceState {
+        return DeviceState(
+            DeviceState.Configuration.Builder(currentState.identifier, currentState.name)
+                .setSystemProperties(currentState.configuration.systemProperties)
+                .setPhysicalProperties(physicalState.configuration.physicalProperties)
+                .build()
+        )
+    }
+
+    companion object {
+        val DEVICE_STATE_CLOSED =
+            DeviceState(
+                DeviceState.Configuration.Builder(
+                        DEVICE_POSTURE_CLOSED /* id */,
+                        "CLOSED" /* name */
+                    )
+                    .setPhysicalProperties(
+                        setOf(DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+                    )
+                    .build()
+            )
+        val DEVICE_STATE_HALF_FOLDED =
+            DeviceState(
+                DeviceState.Configuration.Builder(
+                        DEVICE_POSTURE_HALF_OPENED /* id */,
+                        "HALF_FOLDED" /* name */
+                    )
+                    .setPhysicalProperties(
+                        setOf(
+                            DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+                        )
+                    )
+                    .build()
+            )
+        val DEVICE_STATE_OPENED =
+            DeviceState(
+                DeviceState.Configuration.Builder(
+                        DEVICE_POSTURE_OPENED /* id */,
+                        "OPENED" /* name */
+                    )
+                    .setPhysicalProperties(
+                        setOf(DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)
+                    )
+                    .build()
+            )
+        val DEVICE_STATE_FLIPPED =
+            DeviceState(
+                DeviceState.Configuration.Builder(
+                        DEVICE_POSTURE_FLIPPED /* id */,
+                        "FLIPPED" /* name */
+                    )
+                    .build()
+            )
+        val DEVICE_STATE_UNKNOWN =
+            DeviceState(
+                DeviceState.Configuration.Builder(
+                        DEVICE_POSTURE_UNKNOWN /* id */,
+                        "UNKNOWN" /* name */
+                    )
+                    .build()
+            )
+        val DEVICE_STATE_USE_BASE_STATE =
+            DeviceState(
+                DeviceState.Configuration.Builder(SUPPORTED_POSTURES_SIZE, "USE_BASE_STATE").build()
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 4ccbd1b..2955162 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -119,11 +120,11 @@
                 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onStateChanged(1);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         // Settings only exist for state 0 and 1
-        mDeviceStateCallback.onStateChanged(2);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
@@ -134,10 +135,10 @@
                 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onStateChanged(0);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
-        mDeviceStateCallback.onStateChanged(1);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
     }
 
@@ -147,7 +148,7 @@
         mFakeRotationPolicy.setRotationLock(true);
 
         // State 2 -> Ignored -> Fall back to state 1 which is unlocked
-        mDeviceStateCallback.onStateChanged(2);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
@@ -161,7 +162,7 @@
         mFakeRotationPolicy.setRotationLock(false);
 
         // State 2 -> Ignored -> Fall back to state 1 which is locked
-        mDeviceStateCallback.onStateChanged(2);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
     }
@@ -173,7 +174,7 @@
         mSettingsManager.onPersistedSettingsChanged();
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onStateChanged(0);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -189,10 +190,10 @@
 
     @Test
     public void whenDeviceStateSwitchedToIgnoredState_useFallbackSetting() {
-        mDeviceStateCallback.onStateChanged(0);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
 
-        mDeviceStateCallback.onStateChanged(2);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
 
@@ -202,10 +203,10 @@
                 8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onStateChanged(1);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
-        mDeviceStateCallback.onStateChanged(8);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8));
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -225,7 +226,7 @@
                 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(false);
-        mDeviceStateCallback.onStateChanged(0);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
@@ -241,7 +242,7 @@
         initializeSettingsWith(
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
-        mDeviceStateCallback.onStateChanged(0);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                 /* rotationLocked= */ false,
@@ -262,7 +263,7 @@
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                 2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
-        mDeviceStateCallback.onStateChanged(2);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                 /* rotationLocked= */ true,
@@ -283,8 +284,8 @@
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                 8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
-        mDeviceStateCallback.onStateChanged(1);
-        mDeviceStateCallback.onStateChanged(8);
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
+        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8));
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                 /* rotationLocked= */ true,
@@ -320,6 +321,10 @@
         mSettingsManager.onPersistedSettingsChanged();
     }
 
+    private DeviceState createDeviceStateForIdentifier(int id) {
+        return new DeviceState(new DeviceState.Configuration.Builder(id, "" /* name */).build());
+    }
+
     private static class FakeRotationPolicy implements RotationPolicyWrapper {
 
         private boolean mRotationLock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 76913e8..e4b9f10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.kosmos.testScope
@@ -63,9 +64,9 @@
             ConfigurationInteractor(FakeConfigurationRepository()),
             FakeShadeRepository(),
             kosmos.keyguardTransitionInteractor,
-        ) {
-            kosmos.sceneInteractor
-        }
+            { kosmos.sceneInteractor },
+            { kosmos.fromGoneTransitionInteractor },
+        )
     private val keyguardStatusBarInteractor =
         KeyguardStatusBarInteractor(
             FakeKeyguardStatusBarRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index ab28a2f..ed7c956 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -985,7 +985,7 @@
         FabricatedOverlay neutrals = overlays[1];
         FabricatedOverlay dynamic = overlays[2];
 
-        final int colorsPerPalette = 12;
+        final int colorsPerPalette = 13;
 
         // Color resources were added for all 3 accent palettes
         verify(accents, times(colorsPerPalette * 3))
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 19f31d5..ec27f48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -441,7 +441,8 @@
                 new ConfigurationInteractor(configurationRepository),
                 shadeRepository,
                 keyguardTransitionInteractor,
-                () -> sceneInteractor);
+                () -> sceneInteractor,
+                () -> mKosmos.getFromGoneTransitionInteractor());
 
         mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
         mFromPrimaryBouncerTransitionInteractor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index cceb3ff..f65c74f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -19,6 +19,7 @@
 import android.platform.test.annotations.EnableFlags
 import com.android.systemui.Flags.FLAG_COMPOSE_LOCKSCREEN
 import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@@ -33,6 +34,7 @@
     FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
     FLAG_COMPOSE_LOCKSCREEN,
     FLAG_MEDIA_IN_SCENE_CONTAINER,
+    FLAG_KEYGUARD_WM_STATE_REFACTOR,
 )
 @Retention(AnnotationRetention.RUNTIME)
 @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
index 5c5016d..e2b5869 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
@@ -16,9 +16,24 @@
 
 package com.android.systemui.jank
 
+import android.os.HandlerThread
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.jank.InteractionJankMonitor.Configuration.Builder
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.spy
 
-val Kosmos.interactionJankMonitor by Fixture<InteractionJankMonitor> { mock() }
+val Kosmos.interactionJankMonitor by
+    Fixture<InteractionJankMonitor> {
+        spy(InteractionJankMonitor(HandlerThread("InteractionJankMonitor-Kosmos"))).apply {
+            doReturn(true).`when`(this).shouldMonitor()
+            doReturn(true).`when`(this).begin(any(), anyInt())
+            doReturn(true).`when`(this).begin(any<Builder>())
+            doReturn(true).`when`(this).end(anyInt())
+            doReturn(true).`when`(this).cancel(anyInt())
+            doReturn(true).`when`(this).cancel(anyInt(), anyInt())
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 3893a9b7..00cdc33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -51,6 +51,7 @@
         configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
         shadeRepository: FakeShadeRepository = FakeShadeRepository(),
         sceneInteractor: SceneInteractor = mock(),
+        fromGoneTransitionInteractor: FromGoneTransitionInteractor = mock(),
         powerInteractor: PowerInteractor = PowerInteractorFactory.create().powerInteractor,
     ): WithDependencies {
         // Mock this until the class is replaced by kosmos
@@ -77,6 +78,7 @@
                 sceneInteractorProvider = { sceneInteractor },
                 keyguardTransitionInteractor = keyguardTransitionInteractor,
                 powerInteractor = powerInteractor,
+                fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
             ),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
index 5140a9f..d61bc9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.statusbar.commandQueue
 
-val Kosmos.keyguardInteractor by
+val Kosmos.keyguardInteractor: KeyguardInteractor by
     Kosmos.Fixture {
         KeyguardInteractor(
             repository = keyguardRepository,
@@ -38,5 +38,6 @@
             shadeRepository = shadeRepository,
             keyguardTransitionInteractor = keyguardTransitionInteractor,
             sceneInteractorProvider = { sceneInteractor },
+            fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index d84988d..29167d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -27,6 +27,7 @@
             surfaceBehindInteractor = keyguardSurfaceBehindInteractor,
             fromLockscreenInteractor = fromLockscreenTransitionInteractor,
             fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
+            fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor,
             notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 4a85909..60dd48a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -20,8 +20,8 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
-import com.android.systemui.statusbar.policy.splitShadeStateController
 
 val Kosmos.keyguardClockViewModel by
     Kosmos.Fixture {
@@ -29,7 +29,7 @@
             keyguardInteractor = keyguardInteractor,
             keyguardClockInteractor = keyguardClockInteractor,
             applicationScope = applicationCoroutineScope,
-            splitShadeStateController = splitShadeStateController,
             notifsKeyguardInteractor = notificationsKeyguardInteractor,
+            shadeInteractor = shadeInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 8da5dd4..f0fedd2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.policy.splitShadeStateController
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 
 val Kosmos.lockscreenContentViewModel by
     Kosmos.Fixture {
@@ -29,6 +29,6 @@
             interactor = keyguardBlueprintInteractor,
             authController = authController,
             longPress = keyguardLongPressViewModel,
-            splitShadeStateController = splitShadeStateController,
+            shadeInteractor = shadeInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 3fc5af1..e861892 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.jank.interactionJankMonitor
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -92,6 +93,7 @@
     val fromPrimaryBouncerTransitionInteractor by lazy {
         kosmos.fromPrimaryBouncerTransitionInteractor
     }
+    val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
     val globalActionsInteractor by lazy { kosmos.globalActionsInteractor }
     val sceneDataSource by lazy { kosmos.sceneDataSource }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
similarity index 97%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
index 63f7c97..ea59c0a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import android.content.pm.ActivityInfo;
 import android.graphics.Canvas;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
similarity index 97%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
index 478658e..3b7b158 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
 
 import static android.util.MathUtils.constrain;
 
@@ -32,6 +32,8 @@
 import android.media.Image;
 import android.util.Log;
 
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient;
+
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractorKosmos.kt
similarity index 77%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractorKosmos.kt
index 9e34fe8..2ed56ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractorKosmos.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.statusbar.domain.interactor
 
+import com.android.systemui.keyguard.domain.interactor.keyguardSurfaceBehindInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.power.domain.interactor.powerInteractor
 
@@ -26,5 +28,7 @@
             keyguardTransitionInteractor = this.keyguardTransitionInteractor,
             keyguardOcclusionInteractor = this.keyguardOcclusionInteractor,
             powerInteractor = this.powerInteractor,
+            wmLockscreenVisibilityInteractor = windowManagerLockscreenVisibilityInteractor,
+            surfaceBehindInteractor = keyguardSurfaceBehindInteractor,
         )
     }
diff --git a/services/Android.bp b/services/Android.bp
index 8709692..98a7979 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -119,6 +119,7 @@
         ":services.companion-sources",
         ":services.contentcapture-sources",
         ":services.contentsuggestions-sources",
+        ":services.contextualsearch-sources",
         ":services.coverage-sources",
         ":services.credentials-sources",
         ":services.devicepolicy-sources",
@@ -208,6 +209,7 @@
         "services.companion",
         "services.contentcapture",
         "services.contentsuggestions",
+        "services.contextualsearch",
         "services.coverage",
         "services.credentials",
         "services.devicepolicy",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2d531e7..4e14dee 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -935,34 +935,11 @@
                     }
                     final AccessibilityUserState userState = getUserStateLocked(userId);
 
-                    if (Flags.disableContinuousShortcutOnForceStop()) {
-                        if (doit && onPackagesForceStoppedLocked(packages, userState)) {
-                            onUserStateChangedLocked(userState);
-                            return false;
-                        } else {
-                            return true;
-                        }
-                    } else {
-                        final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
-                        while (it.hasNext()) {
-                            final ComponentName comp = it.next();
-                            final String compPkg = comp.getPackageName();
-                            for (String pkg : packages) {
-                                if (compPkg.equals(pkg)) {
-                                    if (!doit) {
-                                        return true;
-                                    }
-                                    it.remove();
-                                    userState.getBindingServicesLocked().remove(comp);
-                                    userState.getCrashedServicesLocked().remove(comp);
-                                    persistComponentNamesToSettingLocked(
-                                            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                                            userState.mEnabledServices, userId);
-                                    onUserStateChangedLocked(userState);
-                                }
-                            }
-                        }
+                    if (doit && onPackagesForceStoppedLocked(packages, userState)) {
+                        onUserStateChangedLocked(userState);
                         return false;
+                    } else {
+                        return true;
                     }
                 }
             }
@@ -5225,18 +5202,16 @@
         private int mSystemUiUid = 0;
 
         AccessibilityDisplayListener(Context context, Handler handler) {
-            if (Flags.addWindowTokenWithoutLock()) {
-                // Avoid concerns about one thread adding displays while another thread removes
-                // them by ensuring the looper is the main looper and the DisplayListener
-                // callbacks are always executed on the one main thread.
-                final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
-                final String errorMessage =
-                        "AccessibilityDisplayListener must use the main handler";
-                if (Build.IS_USERDEBUG || Build.IS_ENG) {
-                    Preconditions.checkArgument(isMainHandler, errorMessage);
-                } else if (!isMainHandler) {
-                    Slog.e(LOG_TAG, errorMessage);
-                }
+            // Avoid concerns about one thread adding displays while another thread removes
+            // them by ensuring the looper is the main looper and the DisplayListener
+            // callbacks are always executed on the one main thread.
+            final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
+            final String errorMessage =
+                    "AccessibilityDisplayListener must use the main handler";
+            if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                Preconditions.checkArgument(isMainHandler, errorMessage);
+            } else if (!isMainHandler) {
+                Slog.e(LOG_TAG, errorMessage);
             }
 
             mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
@@ -5280,14 +5255,12 @@
 
         @Override
         public void onDisplayAdded(int displayId) {
-            if (Flags.addWindowTokenWithoutLock()) {
-                final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
-                final String errorMessage = "onDisplayAdded must be called from the main thread";
-                if (Build.IS_USERDEBUG || Build.IS_ENG) {
-                    Preconditions.checkArgument(isMainThread, errorMessage);
-                } else if (!isMainThread) {
-                    Slog.e(LOG_TAG, errorMessage);
-                }
+            final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+            final String errorMessage = "onDisplayAdded must be called from the main thread";
+            if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                Preconditions.checkArgument(isMainThread, errorMessage);
+            } else if (!isMainThread) {
+                Slog.e(LOG_TAG, errorMessage);
             }
             final Display display = mDisplayManager.getDisplay(displayId);
             if (!isValidDisplay(display)) {
@@ -5303,41 +5276,27 @@
                     mInputFilter.onDisplayAdded(display);
                 }
                 AccessibilityUserState userState = getCurrentUserStateLocked();
-                if (Flags.addWindowTokenWithoutLock()) {
-                    services = new ArrayList<>(userState.mBoundServices);
-                } else {
-                    services = userState.mBoundServices;
-                    if (displayId != Display.DEFAULT_DISPLAY) {
-                        for (int i = 0; i < services.size(); i++) {
-                            AccessibilityServiceConnection boundClient = services.get(i);
-                            boundClient.addWindowTokenForDisplay(displayId);
-                        }
-                    }
-                }
+                services = new ArrayList<>(userState.mBoundServices);
                 updateMagnificationLocked(userState);
                 updateWindowsForAccessibilityCallbackLocked(userState);
                 notifyClearAccessibilityCacheLocked();
             }
-            if (Flags.addWindowTokenWithoutLock()) {
-                if (displayId != Display.DEFAULT_DISPLAY) {
-                    for (int i = 0; i < services.size(); i++) {
-                        AccessibilityServiceConnection boundClient = services.get(i);
-                        boundClient.addWindowTokenForDisplay(displayId);
-                    }
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                for (int i = 0; i < services.size(); i++) {
+                    AccessibilityServiceConnection boundClient = services.get(i);
+                    boundClient.addWindowTokenForDisplay(displayId);
                 }
             }
         }
 
         @Override
         public void onDisplayRemoved(int displayId) {
-            if (Flags.addWindowTokenWithoutLock()) {
-                final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
-                final String errorMessage = "onDisplayRemoved must be called from the main thread";
-                if (Build.IS_USERDEBUG || Build.IS_ENG) {
-                    Preconditions.checkArgument(isMainThread, errorMessage);
-                } else if (!isMainThread) {
-                    Slog.e(LOG_TAG, errorMessage);
-                }
+            final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+            final String errorMessage = "onDisplayRemoved must be called from the main thread";
+            if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                Preconditions.checkArgument(isMainThread, errorMessage);
+            } else if (!isMainThread) {
+                Slog.e(LOG_TAG, errorMessage);
             }
             synchronized (mLock) {
                 if (!removeDisplayFromList(displayId)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index b90a66a..fb28055 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -220,7 +220,7 @@
     @Override
     public void onServiceConnected(ComponentName componentName, IBinder service) {
         AccessibilityUserState userState = mUserStateWeakReference.get();
-        if (userState != null && Flags.addWindowTokenWithoutLock()) {
+        if (userState != null) {
             addWindowTokensForAllDisplays();
         }
         synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 4b128f7..9a1d379 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -243,9 +243,6 @@
 
     void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
         if (!mBoundServices.contains(serviceConnection)) {
-            if (!Flags.addWindowTokenWithoutLock()) {
-                serviceConnection.addWindowTokensForAllDisplays();
-            }
             mBoundServices.add(serviceConnection);
             mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
             mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index f69104d..aad9e24 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -136,9 +136,6 @@
             return;
         }
 
-        if (!Flags.addWindowTokenWithoutLock()) {
-            mUiAutomationService.addWindowTokensForAllDisplays();
-        }
         // UiAutomationService#connectServiceUnknownThread posts to a handler
         // so this call should return immediately.
         mUiAutomationService.connectServiceUnknownThread();
@@ -286,9 +283,7 @@
                     // If the serviceInterface is null, the UiAutomation has been shut down on
                     // another thread.
                     if (serviceInterface != null) {
-                        if (Flags.addWindowTokenWithoutLock()) {
-                            mUiAutomationService.addWindowTokensForAllDisplays();
-                        }
+                        mUiAutomationService.addWindowTokensForAllDisplays();
                         if (mTrace.isA11yTracingEnabledForTypes(
                                 AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
                             mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
diff --git a/services/contextualsearch/Android.bp b/services/contextualsearch/Android.bp
new file mode 100644
index 0000000..b4dd20e
--- /dev/null
+++ b/services/contextualsearch/Android.bp
@@ -0,0 +1,22 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "services.contextualsearch-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.contextualsearch",
+    defaults: ["platform_service_defaults"],
+    srcs: [":services.contextualsearch-sources"],
+    libs: ["services.core"],
+}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
new file mode 100644
index 0000000..b28bc1f
--- /dev/null
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -0,0 +1,315 @@
+/*
+ * 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.contextualsearch;
+
+import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH;
+import static android.content.Context.CONTEXTUAL_SEARCH_SERVICE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.ActivityOptions;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.app.contextualsearch.ContextualSearchManager;
+import android.app.contextualsearch.ContextualSearchState;
+import android.app.contextualsearch.IContextualSearchCallback;
+import android.app.contextualsearch.IContextualSearchManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelableException;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Log;
+import android.util.Slog;
+import android.window.ScreenCapture;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityAssistInfo;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class ContextualSearchManagerService extends SystemService {
+
+    private static final int MSG_RESET_TEMPORARY_PACKAGE = 0;
+    private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+    private final Context mContext;
+    private final ActivityTaskManagerInternal mAtmInternal;
+    private final WindowManagerInternal mWmInternal;
+    private final DevicePolicyManagerInternal mDpmInternal;
+
+    private Handler mTemporaryHandler;
+
+    @GuardedBy("this")
+    private String mTemporaryPackage = null;
+    private static final String TAG = ContextualSearchManagerService.class.getSimpleName();
+
+    public ContextualSearchManagerService(@NonNull Context context) {
+        super(context);
+        if (DEBUG_USER) Log.d(TAG, "ContextualSearchManagerService created");
+        mContext = context;
+        mAtmInternal = Objects.requireNonNull(
+                LocalServices.getService(ActivityTaskManagerInternal.class));
+        mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class));
+        mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(CONTEXTUAL_SEARCH_SERVICE, new ContextualSearchManagerStub());
+    }
+
+    void resetTemporaryPackage() {
+        synchronized (this) {
+            enforceOverridingPermission("resetTemporaryPackage");
+            if (mTemporaryHandler != null) {
+                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
+                mTemporaryHandler = null;
+            }
+            if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage reset.");
+            mTemporaryPackage = null;
+        }
+    }
+
+    void setTemporaryPackage(@NonNull String temporaryPackage, int durationMs) {
+        synchronized (this) {
+            enforceOverridingPermission("setTemporaryPackage");
+            final int maxDurationMs = MAX_TEMP_PACKAGE_DURATION_MS;
+            if (durationMs > maxDurationMs) {
+                throw new IllegalArgumentException(
+                        "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+            }
+            if (mTemporaryHandler == null) {
+                mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        if (msg.what == MSG_RESET_TEMPORARY_PACKAGE) {
+                            synchronized (this) {
+                                resetTemporaryPackage();
+                            }
+                        } else {
+                            Slog.wtf(TAG, "invalid handler msg: " + msg);
+                        }
+                    }
+                };
+            } else {
+                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
+            }
+            mTemporaryPackage = temporaryPackage;
+            mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_PACKAGE, durationMs);
+            if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
+        }
+    }
+
+    private Intent getResolvedLaunchIntent() {
+        synchronized (this) {
+            // If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
+            String csPkgName = mTemporaryPackage != null ? mTemporaryPackage : mContext
+                    .getResources().getString(R.string.config_defaultContextualSearchPackageName);
+            if (csPkgName.isEmpty()) {
+                // Return null if csPackageName is not specified.
+                return null;
+            }
+            Intent launchIntent = new Intent(
+                    ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH);
+            launchIntent.setPackage(csPkgName);
+            ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+                    launchIntent, MATCH_FACTORY_ONLY);
+            if (resolveInfo == null) {
+                return null;
+            }
+            ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
+            if (componentName == null) {
+                return null;
+            }
+            launchIntent.setComponent(componentName);
+            return launchIntent;
+        }
+    }
+
+    private Intent getContextualSearchIntent(int entrypoint, IBinder mToken) {
+        final Intent launchIntent = getResolvedLaunchIntent();
+        if (launchIntent == null) {
+            return null;
+        }
+
+        if (DEBUG_USER) Log.d(TAG, "Launch component: " + launchIntent.getComponent());
+        launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION
+                | FLAG_ACTIVITY_NO_USER_ACTION);
+        launchIntent.putExtra(ContextualSearchManager.EXTRA_ENTRYPOINT, entrypoint);
+        launchIntent.putExtra(ContextualSearchManager.EXTRA_TOKEN, mToken);
+        boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
+        final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
+        ArrayList<String> visiblePackageNames = new ArrayList<>();
+        boolean isManagedProfileVisible = false;
+        for (ActivityAssistInfo record : records) {
+            // Add the package name to the list only if assist data is allowed.
+            if (isAssistDataAllowed) {
+                visiblePackageNames.add(record.getComponentName().getPackageName());
+            }
+            if (mDpmInternal != null
+                    && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+                isManagedProfileVisible = true;
+            }
+        }
+        final ScreenCapture.ScreenshotHardwareBuffer shb;
+        if (mWmInternal != null) {
+            shb = mWmInternal.takeAssistScreenshot();
+        } else {
+            shb = null;
+        }
+        final Bitmap bm = shb != null ? shb.asBitmap() : null;
+        // Now that everything is fetched, putting it in the launchIntent.
+        if (bm != null) {
+            launchIntent.putExtra(ContextualSearchManager.EXTRA_FLAG_SECURE_FOUND,
+                    shb.containsSecureLayers());
+            // Only put the screenshot if assist data is allowed
+            if (isAssistDataAllowed) {
+                launchIntent.putExtra(ContextualSearchManager.EXTRA_SCREENSHOT, bm.asShared());
+            }
+        }
+        launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_MANAGED_PROFILE_VISIBLE,
+                isManagedProfileVisible);
+        // Only put the list of visible package names if assist data is allowed
+        if (isAssistDataAllowed) {
+            launchIntent.putExtra(ContextualSearchManager.EXTRA_VISIBLE_PACKAGE_NAMES,
+                    visiblePackageNames);
+        }
+        return launchIntent;
+    }
+
+    @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
+    private int invokeContextualSearchIntent(Intent launchIntent) {
+        // Contextual search starts with a frozen screen - so we launch without
+        // any system animations or starting window.
+        final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext,
+                /* enterResId= */ 0, /* exitResId= */ 0, null, null, null);
+        opts.setDisableStartingWindow(true);
+        return mAtmInternal.startActivityWithScreenshot(launchIntent,
+                mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null,
+                opts.toBundle(), Binder.getCallingUserHandle().getIdentifier());
+    }
+
+    private void enforcePermission(@NonNull final String func) {
+        Context ctx = getContext();
+        if (!(ctx.checkCallingPermission(ACCESS_CONTEXTUAL_SEARCH) == PERMISSION_GRANTED
+                || isCallerTemporary())) {
+            String msg = "Permission Denial: Cannot call " + func + " from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+            throw new SecurityException(msg);
+        }
+    }
+
+    private void enforceOverridingPermission(@NonNull final String func) {
+        if (!(Binder.getCallingUid() == Process.SHELL_UID
+                || Binder.getCallingUid() == Process.ROOT_UID
+                || Binder.getCallingUid() == Process.SYSTEM_UID)) {
+            String msg = "Permission Denial: Cannot override Contextual Search. Called " + func
+                    + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+            throw new SecurityException(msg);
+        }
+    }
+
+    private boolean isCallerTemporary() {
+        synchronized (this) {
+            return mTemporaryPackage != null
+                    && mTemporaryPackage.equals(
+                    getContext().getPackageManager().getNameForUid(Binder.getCallingUid()));
+        }
+    }
+
+    private class ContextualSearchManagerStub extends IContextualSearchManager.Stub {
+        private @Nullable IBinder mToken;
+
+        @Override
+        public void startContextualSearch(int entrypoint) {
+            synchronized (this) {
+                if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
+                enforcePermission("startContextualSearch");
+                mToken = new Binder();
+                // We get the launch intent with the system server's identity because the system
+                // server has READ_FRAME_BUFFER permission to get the screenshot and because only
+                // the system server can invoke non-exported activities.
+                Binder.withCleanCallingIdentity(() -> {
+                    Intent launchIntent = getContextualSearchIntent(entrypoint, mToken);
+                    if (launchIntent != null) {
+                        int result = invokeContextualSearchIntent(launchIntent);
+                        if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void getContextualSearchState(
+                @NonNull IBinder token,
+                @NonNull IContextualSearchCallback callback) {
+            if (DEBUG_USER) {
+                Log.i(TAG, "getContextualSearchState token: " + token + ", callback: " + callback);
+            }
+            if (mToken == null || !mToken.equals(token)) {
+                if (DEBUG_USER) {
+                    Log.e(TAG, "getContextualSearchState: invalid token, returning error");
+                }
+                try {
+                    callback.onError(
+                            new ParcelableException(new IllegalArgumentException("Invalid token")));
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not invoke onError callback", e);
+                }
+                return;
+            }
+            mToken = null;
+            // Process data request
+            try {
+                callback.onResult(new ContextualSearchState(null, null, Bundle.EMPTY));
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not invoke onResult callback", e);
+            }
+        }
+
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err, @NonNull String[] args,
+                @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+            new ContextualSearchManagerShellCommand(ContextualSearchManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+    }
+}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
new file mode 100644
index 0000000..5777e1d
--- /dev/null
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
@@ -0,0 +1,78 @@
+/*
+ * 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.contextualsearch;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+public class ContextualSearchManagerShellCommand extends ShellCommand {
+
+    private final ContextualSearchManagerService mService;
+
+    ContextualSearchManagerShellCommand(@NonNull ContextualSearchManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "set": {
+                final String what = getNextArgRequired();
+                switch (what) {
+                    case "temporary-package": {
+                        String packageName = getNextArg();
+                        if (packageName == null) {
+                            mService.resetTemporaryPackage();
+                            pw.println("ContextualSearchManagerService reset.");
+                            return 0;
+                        }
+                        final int duration = Integer.parseInt(getNextArgRequired());
+                        mService.setTemporaryPackage(packageName, duration);
+                        pw.println("ContextualSearchManagerService temporarily set to "
+                                + packageName + " for " + duration + "ms");
+                        break;
+                    }
+                }
+            }
+            break;
+            default:
+                return handleDefaultCommands(cmd);
+        }
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter()) {
+            pw.println("ContextualSearchService commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  set temporary-package [PACKAGE_NAME DURATION]");
+            pw.println("    Temporarily (for DURATION ms) changes the Contextual Search "
+                    + "implementation.");
+            pw.println("    To reset, call without any arguments.");
+            pw.println("");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0012b3d..133a77d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4658,6 +4658,8 @@
                         INVALID_UID /* sdkSandboxClientAppUid */,
                         null /* sdkSandboxClientAppPackage */,
                         false /* inSharedIsolatedProcess */);
+                r.foregroundId = fgsDelegateOptions.mClientNotificationId;
+                r.foregroundNoti = fgsDelegateOptions.mClientNotification;
                 res.setService(r);
                 smap.mServicesByInstanceName.put(cn, r);
                 smap.mServicesByIntent.put(filter, r);
@@ -8171,7 +8173,7 @@
      * @param targetProcess  the process of the service to start.
      * @return {@link ReasonCode}
      */
-    private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
+    @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
             int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
             BackgroundStartPrivileges backgroundStartPrivileges) {
         int ret = REASON_DENIED;
@@ -9046,6 +9048,10 @@
             });
         }
         signalForegroundServiceObserversLocked(r);
+        if (r.foregroundId != 0 && r.foregroundNoti != null) {
+            r.foregroundNoti.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+            r.postNotification(true);
+        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index ff83797..272e84b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -51,6 +51,7 @@
 import android.util.ArraySet;
 import android.util.KeyValueListParser;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -436,6 +437,18 @@
     private static final String KEY_MAX_SERVICE_CONNECTIONS_PER_PROCESS =
             "max_service_connections_per_process";
 
+    private static final String KEY_PROC_STATE_DEBUG_UIDS = "proc_state_debug_uids";
+
+    /**
+     * UIDs we want to print detailed info in OomAdjuster.
+     * It's only used for debugging, and it's almost never updated, so we just create a new
+     * array when it's changed to avoid synchronization.
+     */
+    volatile SparseBooleanArray mProcStateDebugUids = new SparseBooleanArray(0);
+    volatile boolean mEnableProcStateStacktrace = false;
+    volatile int mProcStateDebugSetProcStateDelay = 0;
+    volatile int mProcStateDebugSetUidStateDelay = 0;
+
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
 
@@ -1339,6 +1352,9 @@
                             case KEY_PSS_TO_RSS_THRESHOLD_MODIFIER:
                                 updatePssToRssThresholdModifier();
                                 break;
+                            case KEY_PROC_STATE_DEBUG_UIDS:
+                                updateProcStateDebugUids();
+                                break;
                             default:
                                 updateFGSPermissionEnforcementFlagsIfNecessary(name);
                                 break;
@@ -2039,6 +2055,76 @@
                 DEFAULT_MAX_PREVIOUS_TIME);
     }
 
+    private void updateProcStateDebugUids() {
+        final String val = DeviceConfig.getString(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_PROC_STATE_DEBUG_UIDS,
+                "").trim();
+
+        // Parse KEY_PROC_STATE_DEBUG_UIDS as comma-separated values. Each values can be:
+        // Number:  Enable debugging on the given UID.
+        // "stack": Enable stack trace when updating proc/uid-states.s
+        // "u" + delay-ms: Enable sleep when updating uid-state
+        // "p" + delay-ms: Enable sleep when updating procstate
+        //
+        // Example:
+        //   device_config put activity_manager proc_state_debug_uids '10177,10202,stack,p500,u100'
+        // means:
+        // - Monitor UID 10177 and 10202
+        // - Also enable stack trace
+        // - Sleep 500 ms when updating the procstate.
+        // - Sleep 100 ms when updating the UID state.
+
+        mEnableProcStateStacktrace = false;
+        mProcStateDebugSetProcStateDelay = 0;
+        mProcStateDebugSetUidStateDelay = 0;
+        if (val.length() == 0) {
+            mProcStateDebugUids = new SparseBooleanArray(0);
+            return;
+        }
+        final String[] uids = val.split(",");
+
+        final SparseBooleanArray newArray = new SparseBooleanArray(0);
+
+        for (String token : uids) {
+            if (token.length() == 0) {
+                continue;
+            }
+            // "stack" -> enable stacktrace.
+            if ("stack".equals(token)) {
+                mEnableProcStateStacktrace = true;
+                continue;
+            }
+            boolean isUid = true;
+            char prefix = token.charAt(0);
+            if ('a' <= prefix && prefix <= 'z') {
+                // If the token starts with an alphabet, it's not a UID.
+                isUid = false;
+                token = token.substring(1);
+            }
+
+            int value = -1;
+            try {
+                value = Integer.parseInt(token.trim());
+            } catch (NumberFormatException e) {
+                Slog.w(TAG, "Invalid number " + token + " in " + val);
+                continue;
+            }
+            if (isUid) {
+                newArray.put(value, true);
+            } else if (prefix == 'p') {
+                // Enable delay in set-proc-state
+                mProcStateDebugSetProcStateDelay = value;
+            } else if (prefix == 'u') {
+                // Enable delay in set-uid-state
+                mProcStateDebugSetUidStateDelay = value;
+            } else {
+                Slog.w(TAG, "Invalid prefix " + prefix + " in " + val);
+            }
+        }
+        mProcStateDebugUids = newArray;
+    }
+
     private void updateMinAssocLogDuration() {
         MIN_ASSOC_LOG_DURATION = DeviceConfig.getLong(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MIN_ASSOC_LOG_DURATION,
@@ -2178,6 +2264,28 @@
                 mDefaultPssToRssThresholdModifier);
     }
 
+    boolean shouldDebugUidForProcState(int uid) {
+        SparseBooleanArray ar = mProcStateDebugUids;
+        final var size = ar.size();
+        if (size == 0) { // Most common case.
+            return false;
+        }
+        // If the array is small (also common), avoid the binary search.
+        if (size <= 8) {
+            for (int i = 0; i < size; i++) {
+                if (ar.keyAt(i) == uid) {
+                    return ar.valueAt(i);
+                }
+            }
+            return false;
+        }
+        return ar.get(uid, false);
+    }
+
+    boolean shouldEnableProcStateDebug() {
+        return mProcStateDebugUids.size() > 0;
+    }
+
     @NeverCompile // Avoid size overhead of debugging code.
     void dump(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
@@ -2393,5 +2501,12 @@
         pw.print("  OOMADJ_UPDATE_QUICK="); pw.println(OOMADJ_UPDATE_QUICK);
         pw.print("  ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
         pw.println(mEnableWaitForFinishAttachApplication);
+
+        synchronized (mProcStateDebugUids) {
+            pw.print("  "); pw.print(KEY_PROC_STATE_DEBUG_UIDS);
+            pw.print("="); pw.println(mProcStateDebugUids);
+            pw.print("    uid-state-delay="); pw.println(mProcStateDebugSetUidStateDelay);
+            pw.print("    proc-state-delay="); pw.println(mProcStateDebugSetProcStateDelay);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 3e633cc..ddf1d5f 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -211,11 +211,17 @@
             if (!mEnabled) {
                 return;
             }
-            if (!mInProgRecords.containsKey(id)) {
+            int index = mInProgRecords.indexOfKey(id);
+            if (index < 0) {
                 return;
             }
-            mInProgRecords.get(id).setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
-            mInProgRecords.remove(id);
+            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            if (info == null) {
+                mInProgRecords.removeAt(index);
+                return;
+            }
+            info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
+            mInProgRecords.removeAt(index);
         }
     }
 
@@ -224,16 +230,24 @@
             if (!mEnabled) {
                 return;
             }
-            if (!mInProgRecords.containsKey(id)) {
+            int index = mInProgRecords.indexOfKey(id);
+            if (index < 0) {
                 return;
             }
-            if (app != null) {
-                ApplicationStartInfo info = mInProgRecords.get(id);
-                info.setStartType((int) temperature);
-                addBaseFieldsFromProcessRecord(info, app);
-                mInProgRecords.put(id, addStartInfoLocked(info));
+            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            if (info == null || app == null) {
+                mInProgRecords.removeAt(index);
+                return;
+            }
+            info.setStartType((int) temperature);
+            addBaseFieldsFromProcessRecord(info, app);
+            ApplicationStartInfo newInfo = addStartInfoLocked(info);
+            if (newInfo == null) {
+                // newInfo can be null if records are added before load from storage is
+                // complete. In this case the newly added record will be lost.
+                mInProgRecords.removeAt(index);
             } else {
-                mInProgRecords.remove(id);
+                mInProgRecords.setValueAt(index, newInfo);
             }
         }
     }
@@ -243,12 +257,17 @@
             if (!mEnabled) {
                 return;
             }
-            if (!mInProgRecords.containsKey(id)) {
+            int index = mInProgRecords.indexOfKey(id);
+            if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.get(id);
+            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            if (info == null) {
+                mInProgRecords.removeAt(index);
+                return;
+            }
             info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
-            mInProgRecords.remove(id);
+            mInProgRecords.removeAt(index);
         }
     }
 
@@ -258,10 +277,15 @@
             if (!mEnabled) {
                 return;
             }
-            if (!mInProgRecords.containsKey(id)) {
+            int index = mInProgRecords.indexOfKey(id);
+            if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.get(id);
+            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            if (info == null) {
+                mInProgRecords.removeAt(index);
+                return;
+            }
             info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN);
             info.setLaunchMode(launchMode);
             checkCompletenessAndCallback(info);
@@ -273,13 +297,18 @@
             if (!mEnabled) {
                 return;
             }
-            if (!mInProgRecords.containsKey(id)) {
+            int index = mInProgRecords.indexOfKey(id);
+            if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.get(id);
+            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            if (info == null) {
+                mInProgRecords.removeAt(index);
+                return;
+            }
             info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
                     timestampNanos);
-            mInProgRecords.remove(id);
+            mInProgRecords.removeAt(index);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 8038732..b142781 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -135,3 +135,9 @@
 
 # Caller information to clear application data
 30120 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
+
+30111 am_uid_state_changed (UID|1|5),(Seq|1|5),(UidState|1|5),(OldUidState|1|5),(Capability|1|5),(OldCapability|1|5),(Flags|1|5),(reason|3)
+30112 am_proc_state_changed (UID|1|5),(PID|1|5),(Seq|1|5),(ProcState|1|5),(OldProcState|1|5),(OomAdj|1|5),(OldOomAdj|1|5),(reason|3)
+
+# "Misc" events. See OomAdjusterDebugLogger.java
+30113 am_oom_adj_misc (Event|1|5),(UID|1|5),(PID|1|5),(Seq|1|5),(Arg1|1|5),(Arg2|1|5),(reason|3)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7f6d62c..1a7629f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -401,6 +401,14 @@
     @GuardedBy("mService")
     private boolean mPendingFullOomAdjUpdate = false;
 
+    /**
+     * Most recent reason string. We update it in sync with the trace.
+     */
+    @OomAdjReason
+    protected int mLastReason;
+
+    private final OomAdjusterDebugLogger mLogger;
+
     /** Overrideable by a test */
     @VisibleForTesting
     protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -433,6 +441,8 @@
         mCachedAppOptimizer = new CachedAppOptimizer(mService);
         mCacheOomRanker = new CacheOomRanker(service);
 
+        mLogger = new OomAdjusterDebugLogger(this, mService.mConstants);
+
         mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
             final int pid = msg.arg1;
             final int group = msg.arg2;
@@ -636,6 +646,7 @@
     protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
 
+        mLastReason = oomAdjReason;
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
         mAdjSeq++;
@@ -916,6 +927,7 @@
     protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
 
+        mLastReason = oomAdjReason;
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
 
@@ -958,6 +970,7 @@
             }
         }
 
+        mLastReason = oomAdjReason;
         if (startProfiling) {
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
             mService.mOomAdjProfiler.oomAdjStarted();
@@ -1491,6 +1504,7 @@
                         || uidRec.isSetAllowListed() != uidRec.isCurAllowListed()
                         || uidRec.getProcAdjChanged()) {
                     int uidChange = 0;
+                    final boolean shouldLog = mLogger.shouldLog(uidRec.getUid());
                     if (DEBUG_UID_OBSERVERS) {
                         Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
                                 + ": proc state from " + uidRec.getSetProcState() + " to "
@@ -1511,14 +1525,21 @@
                                 || uidRec.isSetAllowListed()
                                 || uidRec.getLastBackgroundTime() == 0) {
                             uidRec.setLastBackgroundTime(nowElapsed);
+                            if (shouldLog) {
+                                mLogger.logSetLastBackgroundTime(uidRec.getUid(), nowElapsed);
+                            }
                             if (mService.mDeterministicUidIdle
                                     || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
                                 // Note: the background settle time is in elapsed realtime, while
                                 // the handler time base is uptime.  All this means is that we may
                                 // stop background uids later than we had intended, but that only
                                 // happens because the device was sleeping so we are okay anyway.
+                                if (shouldLog) {
+                                    mLogger.logScheduleUidIdle1(uidRec.getUid(),
+                                            mConstants.BACKGROUND_SETTLE_TIME);
+                                }
                                 mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                                        mConstants.BACKGROUND_SETTLE_TIME);
+                                        mConstants.BACKGROUND_SETTLE_TIME); // XXX
                             }
                         }
                         if (uidRec.isIdle() && !uidRec.isSetIdle()) {
@@ -1536,6 +1557,9 @@
                         }
                         uidRec.setLastBackgroundTime(0);
                         uidRec.setLastIdleTime(0);
+                        if (shouldLog) {
+                            mLogger.logClearLastBackgroundTime(uidRec.getUid());
+                        }
                     }
                     final boolean wasCached = uidRec.getSetProcState()
                             > ActivityManager.PROCESS_STATE_RECEIVER;
@@ -1555,11 +1579,25 @@
                     if (uidRec.getProcAdjChanged()) {
                         uidChange |= UidRecord.CHANGE_PROCADJ;
                     }
+                    int oldProcState = uidRec.getSetProcState();
+                    int oldCapability = uidRec.getSetCapability();
                     uidRec.setSetProcState(uidRec.getCurProcState());
                     uidRec.setSetCapability(uidRec.getCurCapability());
                     uidRec.setSetAllowListed(uidRec.isCurAllowListed());
                     uidRec.setSetIdle(uidRec.isIdle());
                     uidRec.clearProcAdjChanged();
+                    if (shouldLog
+                            && ((uidRec.getSetProcState() != oldProcState)
+                            || (uidRec.getSetCapability() != oldCapability))) {
+                        int flags = 0;
+                        if (uidRec.isSetAllowListed()) {
+                            flags |= 1;
+                        }
+                        mLogger.logUidStateChanged(uidRec.getUid(),
+                                uidRec.getSetProcState(), oldProcState,
+                                uidRec.getSetCapability(), oldCapability,
+                                flags);
+                    }
                     if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0
                             || (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) {
                         mService.mAtmInternal.onUidProcStateChanged(
@@ -3300,6 +3338,7 @@
             mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
         }
 
+        final int oldOomAdj = state.getSetAdj();
         if (state.getCurAdj() != state.getSetAdj()) {
             ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
@@ -3457,6 +3496,7 @@
             mService.mAppProfiler.updateNextPssTimeLPf(
                     state.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
         }
+        int oldProcState = state.getSetProcState();
         if (state.getSetProcState() != state.getCurProcState()) {
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
                 String msg = "Proc state change of " + app.processName
@@ -3556,6 +3596,11 @@
                 // Kick off the delayed checkup message if needed.
                 if (mService.mDeterministicUidIdle
                         || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+                    if (mLogger.shouldLog(app.uid)) {
+                        mLogger.logScheduleUidIdle2(
+                                uidRec.getUid(), app.getPid(),
+                                mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
+                    }
                     mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
                             mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
                 }
@@ -3563,6 +3608,12 @@
         }
         state.setSetCached(state.isCached());
         state.setSetNoKillOnBgRestrictedAndIdle(state.shouldNotKillOnBgRestrictedAndIdle());
+        if (((oldProcState != state.getSetProcState()) || (oldOomAdj != state.getSetAdj()))
+                && mLogger.shouldLog(app.uid)) {
+            mLogger.logProcStateChanged(app.uid, app.getPid(),
+                    state.getSetProcState(), oldProcState,
+                    state.getSetAdj(), oldOomAdj);
+        }
 
         return success;
     }
@@ -3704,6 +3755,7 @@
         if (mService.mLocalPowerManager != null) {
             mService.mLocalPowerManager.startUidChanges();
         }
+        boolean shouldLogMisc = false;
         for (int i = N - 1; i >= 0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
             final long bgTime = uidRec.getLastBackgroundTime();
@@ -3721,6 +3773,9 @@
                     if (nextTime == 0 || nextTime > bgTime) {
                         nextTime = bgTime;
                     }
+                    if (mLogger.shouldLog(uidRec.getUid())) {
+                        shouldLogMisc = true;
+                    }
                 }
             }
         }
@@ -3742,8 +3797,11 @@
             }
         }
         if (nextTime > 0) {
-            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                    nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
+            long delay = nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed;
+            if (shouldLogMisc) {
+                mLogger.logScheduleUidIdle3(delay);
+            }
+            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, delay);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjusterDebugLogger.java b/services/core/java/com/android/server/am/OomAdjusterDebugLogger.java
new file mode 100644
index 0000000..1294a4d
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjusterDebugLogger.java
@@ -0,0 +1,110 @@
+/*
+ * 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.am;
+
+import android.app.StackTrace;
+import android.util.Slog;
+
+/**
+ * Helper for writing debug log about proc/uid state changes.
+ */
+class OomAdjusterDebugLogger {
+    // Use the "am_" tag to make it similar to  event logs.
+    private static final String STACK_TRACE_TAG = "am_stack";
+
+    private final OomAdjuster mOomAdjuster;
+    private final ActivityManagerConstants mConstants;
+
+    private static final int MISC_SCHEDULE_IDLE_UIDS_MSG_1 = 1;
+    private static final int MISC_SCHEDULE_IDLE_UIDS_MSG_2 = 2;
+    private static final int MISC_SCHEDULE_IDLE_UIDS_MSG_3 = 3;
+
+    private static final int MISC_SET_LAST_BG_TIME = 10;
+    private static final int MISC_CLEAR_LAST_BG_TIME = 11;
+
+    OomAdjusterDebugLogger(OomAdjuster oomAdjuster, ActivityManagerConstants constants) {
+        mOomAdjuster = oomAdjuster;
+        mConstants = constants;
+    }
+
+    boolean shouldLog(int uid) {
+        return mConstants.shouldDebugUidForProcState(uid);
+    }
+
+    private void maybeLogStacktrace(String msg) {
+        if (!mConstants.mEnableProcStateStacktrace) {
+            return;
+        }
+        Slog.i(STACK_TRACE_TAG,
+                msg + ": " + OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason),
+                new StackTrace("Called here"));
+    }
+
+    private void maybeSleep(int millis) {
+        if (millis == 0) {
+            return;
+        }
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    void logUidStateChanged(int uid, int uidstate, int olduidstate,
+            int capability, int oldcapability, int flags) {
+        EventLogTags.writeAmUidStateChanged(
+                uid, mOomAdjuster.mAdjSeq, uidstate, olduidstate, capability, oldcapability, flags,
+                OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+        maybeLogStacktrace("uidStateChanged");
+        maybeSleep(mConstants.mProcStateDebugSetUidStateDelay);
+    }
+
+    void logProcStateChanged(int uid, int pid, int procstate, int oldprocstate,
+            int oomadj, int oldoomadj) {
+        EventLogTags.writeAmProcStateChanged(
+                uid, pid, mOomAdjuster.mAdjSeq, procstate, oldprocstate, oomadj, oldoomadj,
+                OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+        maybeLogStacktrace("procStateChanged");
+        maybeSleep(mConstants.mProcStateDebugSetProcStateDelay);
+    }
+
+    void logScheduleUidIdle1(int uid, long delay) {
+        EventLogTags.writeAmOomAdjMisc(MISC_SCHEDULE_IDLE_UIDS_MSG_1,
+                uid, 0, mOomAdjuster.mAdjSeq, (int) delay, 0, "");
+    }
+
+    void logScheduleUidIdle2(int uid, int pid, long delay) {
+        EventLogTags.writeAmOomAdjMisc(MISC_SCHEDULE_IDLE_UIDS_MSG_2,
+                uid, pid, mOomAdjuster.mAdjSeq, (int) delay, 0, "");
+    }
+
+    void logScheduleUidIdle3(long delay) {
+        EventLogTags.writeAmOomAdjMisc(MISC_SCHEDULE_IDLE_UIDS_MSG_3,
+                0, 0, mOomAdjuster.mAdjSeq, (int) delay, 0, "");
+    }
+
+    void logSetLastBackgroundTime(int uid, long time) {
+        EventLogTags.writeAmOomAdjMisc(MISC_SET_LAST_BG_TIME,
+                uid, 0, mOomAdjuster.mAdjSeq, (int) time, 0,
+                OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+    }
+
+    void logClearLastBackgroundTime(int uid) {
+        EventLogTags.writeAmOomAdjMisc(MISC_CLEAR_LAST_BG_TIME,
+                uid, 0, mOomAdjuster.mAdjSeq, 0, 0,
+                OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+    }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 46bdfe8..5feac1f 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -741,6 +741,7 @@
         mPendingProcessSet.clear();
         mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
 
+        mLastReason = oomAdjReason;
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
 
@@ -761,6 +762,7 @@
     @GuardedBy("mService")
     @Override
     protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
+        mLastReason = oomAdjReason;
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
 
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index b1823b4..e955b00 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -60,10 +60,3 @@
         purpose: PURPOSE_BUGFIX
     }
 }
-
-flag {
-    namespace: "backstage_power"
-    name: "defer_outgoing_bcasts"
-    description: "Defer outgoing broadcasts from processes in freezable state"
-    bug: "327496592"
-}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index e915688..b7e11a7 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -20,7 +20,7 @@
 // TODO(b/141025588): Create separate internal and external permissions for AuthService.
 // TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
 
-import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
 import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -1030,8 +1030,8 @@
     }
 
     private void checkManageBiometricPermission() {
-        getContext().enforceCallingOrSelfPermission(SET_BIOMETRIC_DIALOG_LOGO,
-                "Must have SET_BIOMETRIC_DIALOG_LOGO permission");
+        getContext().enforceCallingOrSelfPermission(SET_BIOMETRIC_DIALOG_ADVANCED,
+                "Must have SET_BIOMETRIC_DIALOG_ADVANCED permission");
     }
 
     private void checkPermission() {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 177c345..e2ae3de 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -76,7 +76,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
@@ -397,11 +396,10 @@
     @NonNull
     private DeviceStateInfo getDeviceStateInfoLocked() {
         final List<DeviceState> supportedStates = getSupportedStatesLocked();
-        final DeviceState baseState = mBaseState.orElse(null);
-        final DeviceState currentState = mCommittedState.orElse(null);
+        final DeviceState baseState = mBaseState.orElse(INVALID_DEVICE_STATE);
+        final DeviceState currentState = mCommittedState.orElse(INVALID_DEVICE_STATE);
 
-        return new DeviceStateInfo(supportedStates,
-                baseState != null ? baseState : INVALID_DEVICE_STATE,
+        return new DeviceStateInfo(supportedStates, baseState,
                 createMergedDeviceState(currentState, baseState));
     }
 
@@ -412,7 +410,7 @@
      */
     private DeviceState createMergedDeviceState(@Nullable DeviceState committedState,
             @Nullable DeviceState baseState) {
-        if (committedState == null) {
+        if (committedState.equals(INVALID_DEVICE_STATE)) {
             return INVALID_DEVICE_STATE;
         }
 
@@ -420,8 +418,7 @@
                 committedState.getConfiguration().getSystemProperties();
 
         Set<@DeviceState.DeviceStateProperties Integer> physicalProperties =
-                baseState != null ? baseState.getConfiguration().getPhysicalProperties()
-                        : Collections.emptySet();
+                baseState.getConfiguration().getPhysicalProperties();
 
         DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder(
                 committedState.getIdentifier(), committedState.getName())
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 9b2dcc5..4116669 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -57,6 +57,9 @@
 import com.android.server.display.config.HbmTiming;
 import com.android.server.display.config.HdrBrightnessData;
 import com.android.server.display.config.HighBrightnessMode;
+import com.android.server.display.config.IdleScreenRefreshRateTimeout;
+import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
+import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds;
 import com.android.server.display.config.IntegerArray;
 import com.android.server.display.config.LuxThrottling;
 import com.android.server.display.config.NitsMap;
@@ -553,6 +556,18 @@
  *         <minorVersion>0</minorVersion>
  *     </usiVersion>
  *     <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
+ *     <idleScreenRefreshRateTimeout>
+ *          <luxThresholds>
+ *              <point>
+ *                  <lux>6</lux>
+ *                  <timeout>1000</timeout>
+ *              </point>
+ *              <point>
+ *                  <lux>10</lux>
+ *                  <timeout>800</timeout>
+ *              </point>
+ *          </luxThresholds>
+ *     </idleScreenRefreshRateTimeout>
  *    </displayConfiguration>
  *  }
  *  </pre>
@@ -843,6 +858,14 @@
     private final Map<BrightnessLimitMapType, Map<Float, Float>>
             mLuxThrottlingData = new HashMap<>();
 
+    /**
+     * The idle screen timeout configuration for switching to lower refresh rate
+     */
+    @NonNull
+    private List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+            mIdleScreenRefreshRateTimeoutLuxThresholds = new ArrayList<>();
+
+
     @Nullable
     private HostUsiVersion mHostUsiVersion;
 
@@ -1999,6 +2022,7 @@
                 loadUsiVersion(config);
                 mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
                 loadBrightnessCapForWearBedtimeMode(config);
+                loadIdleScreenRefreshRateTimeoutConfigs(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
@@ -2024,6 +2048,7 @@
         loadAutoBrightnessAvailableFromConfigXml();
         loadRefreshRateSetting(null);
         loadBrightnessCapForWearBedtimeModeFromConfigXml();
+        loadIdleScreenRefreshRateTimeoutConfigs(null);
         mLoadedFrom = "<config.xml>";
     }
 
@@ -3326,6 +3351,47 @@
         }
     }
 
+    private void loadIdleScreenRefreshRateTimeoutConfigs(@Nullable DisplayConfiguration config) {
+        if (mFlags.isIdleScreenRefreshRateTimeoutEnabled()
+                && config != null && config.getIdleScreenRefreshRateTimeout() != null) {
+            validateIdleScreenRefreshRateTimeoutConfig(
+                    config.getIdleScreenRefreshRateTimeout());
+            mIdleScreenRefreshRateTimeoutLuxThresholds = config
+                    .getIdleScreenRefreshRateTimeout().getLuxThresholds().getPoint();
+        }
+    }
+
+    private void validateIdleScreenRefreshRateTimeoutConfig(
+            IdleScreenRefreshRateTimeout idleScreenRefreshRateTimeoutConfig) {
+        IdleScreenRefreshRateTimeoutLuxThresholds idleScreenRefreshRateTimeoutLuxThresholds =
+                idleScreenRefreshRateTimeoutConfig.getLuxThresholds();
+
+        if (idleScreenRefreshRateTimeoutLuxThresholds != null) {
+            int previousLux = -1;
+            // Validate that the lux values are in the increasing order
+            for (IdleScreenRefreshRateTimeoutLuxThresholdPoint point :
+                    idleScreenRefreshRateTimeoutLuxThresholds.getPoint()) {
+                int newLux = point.getLux().intValue();
+                if (previousLux >= newLux) {
+                    throw new RuntimeException("Lux values should be in ascending order in the"
+                            + " idle screen refresh rate timeout config");
+                }
+                previousLux = newLux;
+            }
+        }
+    }
+
+    /**
+     * Gets the idle screen refresh rate timeout(in ms) configuration list. For each entry, the lux
+     * value represent the lower bound of the lux range, and the value of the lux in the next
+     * point(INF if not present) represents the upper bound for the corresponding timeout(in ms)
+     */
+    @NonNull
+    public List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+            getIdleScreenRefreshRateTimeoutLuxThresholdPoint() {
+        return mIdleScreenRefreshRateTimeoutLuxThresholds;
+    }
+
     /**
      * Extracts a float array from the specified {@link TypedArray}.
      *
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ce7c224..84eebe8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_DISPLAYS;
+import static android.Manifest.permission.RESTRICT_DISPLAY_MODES;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
 import static android.hardware.display.DisplayManager.EventsMask;
@@ -75,6 +76,7 @@
 import android.hardware.OverlayProperties;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManagerInternal;
 import android.hardware.display.AmbientBrightnessDayStats;
@@ -4530,6 +4532,14 @@
             disableConnectedDisplay_enforcePermission();
             DisplayManagerService.this.enableConnectedDisplay(displayId, false);
         }
+
+        @EnforcePermission(RESTRICT_DISPLAY_MODES)
+        @Override // Binder call
+        public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) {
+            requestDisplayModes_enforcePermission();
+            DisplayManagerService.this.mDisplayModeDirector.requestDisplayModes(
+                    token, displayId, modeIds);
+        }
     }
 
     private static boolean isValidBrightness(float brightness) {
@@ -5034,30 +5044,22 @@
      * Listens to changes in device state and reports the state to LogicalDisplayMapper.
      */
     class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
-        // Base state corresponds to the physical state of the device
-        private int mBaseState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
 
         @Override
-        public void onStateChanged(int deviceState) {
-            boolean isDeviceStateOverrideActive = deviceState != mBaseState;
+        public void onDeviceStateChanged(DeviceState deviceState) {
             synchronized (mSyncRoot) {
                 // Notify WindowManager that we are about to handle new device state, this should
                 // be sent before any work related to the device state in DisplayManager, so
                 // WindowManager could do implement that depends on the device state and display
                 // changes (serializes device state update and display change events)
                 Message msg = mHandler.obtainMessage(MSG_RECEIVED_DEVICE_STATE);
-                msg.arg1 = deviceState;
+                msg.arg1 = deviceState.getIdentifier();
                 mHandler.sendMessage(msg);
 
                 mLogicalDisplayMapper
-                        .setDeviceStateLocked(deviceState, isDeviceStateOverrideActive);
+                        .setDeviceStateLocked(deviceState.getIdentifier());
             }
         }
-
-        @Override
-        public void onBaseStateChanged(int state) {
-            mBaseState = state;
-        }
     };
 
     private class BrightnessPair {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 21f90d4..f727eac 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -441,7 +441,7 @@
         mVirtualDeviceDisplayMapping.put(displayDevice.getUniqueId(), virtualDeviceUniqueId);
     }
 
-    void setDeviceStateLocked(int state, boolean isOverrideActive) {
+    void setDeviceStateLocked(int state) {
         if (!mBootCompleted) {
             // The boot animation might still be in progress, we do not want to switch states now
             // as the boot animation would end up with an incorrect size.
@@ -465,7 +465,7 @@
         final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
                 mInteractive, mBootCompleted);
         final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
-                isOverrideActive, mInteractive, mBootCompleted);
+                mInteractive, mBootCompleted);
 
         // If all displays are off already, we can just transition here, unless we are trying to
         // wake or sleep the device as part of this transition. In that case defer the final
@@ -513,8 +513,7 @@
             mBootCompleted = true;
             if (mDeviceStateToBeAppliedAfterBoot
                     != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER) {
-                setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot,
-                        /* isOverrideActive= */ false);
+                setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot);
             }
         }
     }
@@ -560,7 +559,6 @@
      *
      * @param pendingState device state we are moving to
      * @param currentState device state we are currently in
-     * @param isOverrideActive if a device state override is currently active or not
      * @param isInteractive if the device is in an interactive state
      * @param isBootCompleted is the device fully booted
      *
@@ -568,13 +566,13 @@
      * @see #setDeviceStateLocked
      */
     @VisibleForTesting
-    boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isOverrideActive,
-            boolean isInteractive, boolean isBootCompleted) {
+    boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isInteractive,
+            boolean isBootCompleted) {
         return currentState != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER
                 && mDeviceStatesOnWhichToSleep.get(pendingState)
                 && !mDeviceStatesOnWhichToSleep.get(currentState)
-                && !isOverrideActive
-                && isInteractive && isBootCompleted
+                && isInteractive
+                && isBootCompleted
                 && !mFoldSettingProvider.shouldStayAwakeOnFold();
     }
 
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 5f455db..e1a166e 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -92,9 +92,9 @@
             Flags.FLAG_BRIGHTNESS_INT_RANGE_USER_PERCEPTION,
             Flags::brightnessIntRangeUserPerception);
 
-    private final FlagState mVsyncProximityVote = new FlagState(
-            Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE,
-            Flags::enableExternalVsyncProximityVote);
+    private final FlagState mRestrictDisplayModes = new FlagState(
+            Flags.FLAG_ENABLE_RESTRICT_DISPLAY_MODES,
+            Flags::enableRestrictDisplayModes);
 
     private final FlagState mVsyncLowPowerVote = new FlagState(
             Flags.FLAG_ENABLE_VSYNC_LOW_POWER_VOTE,
@@ -135,6 +135,11 @@
             Flags::sensorBasedBrightnessThrottling
     );
 
+    private final FlagState mIdleScreenRefreshRateTimeout = new FlagState(
+            Flags.FLAG_IDLE_SCREEN_REFRESH_RATE_TIMEOUT,
+            Flags::idleScreenRefreshRateTimeout
+    );
+
 
     private final FlagState mRefactorDisplayPowerController = new FlagState(
             Flags.FLAG_REFACTOR_DISPLAY_POWER_CONTROLLER,
@@ -237,8 +242,8 @@
         return mBrightnessIntRangeUserPerceptionFlagState.isEnabled();
     }
 
-    public boolean isVsyncProximityVoteEnabled() {
-        return mVsyncProximityVote.isEnabled();
+    public boolean isRestrictDisplayModesEnabled() {
+        return mRestrictDisplayModes.isEnabled();
     }
 
     public boolean isVsyncLowPowerVoteEnabled() {
@@ -280,6 +285,10 @@
         return mSensorBasedBrightnessThrottling.isEnabled();
     }
 
+    public boolean isIdleScreenRefreshRateTimeoutEnabled() {
+        return mIdleScreenRefreshRateTimeout.isEnabled();
+    }
+
     public boolean isRefactorDisplayPowerControllerEnabled() {
         return mRefactorDisplayPowerController.isEnabled();
     }
@@ -302,7 +311,7 @@
         pw.println(" " + mPowerThrottlingClamperFlagState);
         pw.println(" " + mSmallAreaDetectionFlagState);
         pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
-        pw.println(" " + mVsyncProximityVote);
+        pw.println(" " + mRestrictDisplayModes);
         pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
         pw.println(" " + mAutoBrightnessModesFlagState);
         pw.println(" " + mFastHdrTransitions);
@@ -310,6 +319,7 @@
         pw.println(" " + mRefreshRateVotingTelemetry);
         pw.println(" " + mPixelAnisotropyCorrectionEnabled);
         pw.println(" " + mSensorBasedBrightnessThrottling);
+        pw.println(" " + mIdleScreenRefreshRateTimeout);
         pw.println(" " + mRefactorDisplayPowerController);
     }
 
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index d2909b8..a5f241f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -130,9 +130,9 @@
 }
 
 flag {
-    name: "enable_external_vsync_proximity_vote"
+    name: "enable_restrict_display_modes"
     namespace: "display_manager"
-    description: "Feature flag for external vsync proximity vote"
+    description: "Feature flag for restriction display modes api"
     bug: "284866750"
     is_fixed_read_only: true
 }
@@ -219,3 +219,11 @@
     bug: "294444204"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "idle_screen_refresh_rate_timeout"
+    namespace: "display_manager"
+    description: "Feature flag for reducing the refresh rate when the screen is idle after a timeout"
+    bug: "310026579"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
index c538231..6d750c0 100644
--- a/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
+++ b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.NonNull;
+
 import java.util.Objects;
 
 class BaseModeRefreshRateVote implements Vote {
@@ -31,7 +33,7 @@
     }
 
     @Override
-    public void updateSummary(VoteSummary summary) {
+    public void updateSummary(@NonNull VoteSummary summary) {
         if (summary.appRequestBaseModeRefreshRate == 0f
                 && mAppRequestBaseModeRefreshRate > 0f) {
             summary.appRequestBaseModeRefreshRate = mAppRequestBaseModeRefreshRate;
diff --git a/services/core/java/com/android/server/display/mode/CombinedVote.java b/services/core/java/com/android/server/display/mode/CombinedVote.java
index 4b68791..3cd16bf 100644
--- a/services/core/java/com/android/server/display/mode/CombinedVote.java
+++ b/services/core/java/com/android/server/display/mode/CombinedVote.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.NonNull;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -28,7 +30,7 @@
     }
 
     @Override
-    public void updateSummary(VoteSummary summary) {
+    public void updateSummary(@NonNull VoteSummary summary) {
         mVotes.forEach(vote -> vote.updateSummary(summary));
     }
 
diff --git a/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
index 7f57406..7abb518 100644
--- a/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
+++ b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.NonNull;
+
 import java.util.Objects;
 
 class DisableRefreshRateSwitchingVote implements Vote {
@@ -31,7 +33,7 @@
     }
 
     @Override
-    public void updateSummary(VoteSummary summary) {
+    public void updateSummary(@NonNull VoteSummary summary) {
         summary.disableRefreshRateSwitching =
                 summary.disableRefreshRateSwitching || mDisableRefreshRateSwitching;
     }
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 64cbd54..495ae87 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -41,6 +41,7 @@
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
 import android.os.Looper;
@@ -80,7 +81,6 @@
 import com.android.server.display.utils.DeviceConfigParsingUtils;
 import com.android.server.display.utils.SensorUtils;
 import com.android.server.sensors.SensorManagerInternal;
-import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
@@ -128,9 +128,12 @@
     private final SettingsObserver mSettingsObserver;
     private final DisplayObserver mDisplayObserver;
     private final UdfpsObserver mUdfpsObserver;
-    private final SensorObserver mSensorObserver;
+    private final ProximitySensorObserver mSensorObserver;
     private final HbmObserver mHbmObserver;
     private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+
+    @Nullable
+    private final SystemRequestObserver mSystemRequestObserver;
     private final DeviceConfigParameterProvider mConfigParameterProvider;
     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
 
@@ -203,6 +206,7 @@
             .isDisplaysRefreshRatesSynchronizationEnabled();
         mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
                 .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
+
         mContext = context;
         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
         mInjector = injector;
@@ -222,10 +226,15 @@
         mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
                 mVotesStatsReporter);
         mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
-        mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
+        mSensorObserver = new ProximitySensorObserver(mVotesStorage, injector);
         mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
         mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
                 mDeviceConfigDisplaySettings);
+        if (mDvrrSupported && displayManagerFlags.isRestrictDisplayModesEnabled()) {
+            mSystemRequestObserver = new SystemRequestObserver(mVotesStorage);
+        } else {
+            mSystemRequestObserver = null;
+        }
         mAlwaysRespectAppRequest = false;
         mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
     }
@@ -520,6 +529,15 @@
     }
 
     /**
+     * Delegates requestDisplayModes call to SystemRequestObserver
+     */
+    public void requestDisplayModes(IBinder token, int displayId, int[] modeIds) {
+        if (mSystemRequestObserver != null) {
+            mSystemRequestObserver.requestDisplayModes(token, displayId, modeIds);
+        }
+    }
+
+    /**
      * Print the object's state and debug information into the given stream.
      *
      * @param pw The stream to dump information to.
@@ -970,10 +988,10 @@
                     Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
             final Vote vote;
             if (inLowPowerMode && mVsynLowPowerVoteEnabled) {
-                vote = Vote.forSupportedModes(List.of(
-                        new SupportedModesVote.SupportedMode(/* peakRefreshRate= */ 60f,
+                vote = Vote.forSupportedRefreshRates(List.of(
+                        new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
                                 /* vsyncRate= */ 240f),
-                        new SupportedModesVote.SupportedMode(/* peakRefreshRate= */ 60f,
+                        new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
                                 /* vsyncRate= */ 60f)
                 ));
             } else if (inLowPowerMode) {
@@ -2158,11 +2176,11 @@
                 }
 
                 if (mVsyncLowLightBlockingVoteEnabled) {
-                    refreshRateSwitchingVote = Vote.forSupportedModesAndDisableRefreshRateSwitching(
+                    refreshRateSwitchingVote = Vote.forSupportedRefreshRatesAndDisableSwitching(
                             List.of(
-                                    new SupportedModesVote.SupportedMode(
+                                    new SupportedRefreshRatesVote.RefreshRates(
                                             /* peakRefreshRate= */ 60f, /* vsyncRate= */ 60f),
-                                    new SupportedModesVote.SupportedMode(
+                                    new SupportedRefreshRatesVote.RefreshRates(
                                             /* peakRefreshRate= */120f, /* vsyncRate= */ 120f)));
                 } else {
                     refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
@@ -2498,116 +2516,6 @@
         }
     }
 
-    protected static final class SensorObserver implements ProximityActiveListener,
-            DisplayManager.DisplayListener {
-        private final String mProximitySensorName = null;
-        private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
-
-        private final VotesStorage mVotesStorage;
-        private final Context mContext;
-        private final Injector mInjector;
-        @GuardedBy("mSensorObserverLock")
-        private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
-        private final Object mSensorObserverLock = new Object();
-
-        private DisplayManager mDisplayManager;
-        private DisplayManagerInternal mDisplayManagerInternal;
-        @GuardedBy("mSensorObserverLock")
-        private boolean mIsProxActive = false;
-
-        SensorObserver(Context context, VotesStorage votesStorage, Injector injector) {
-            mContext = context;
-            mVotesStorage = votesStorage;
-            mInjector = injector;
-        }
-
-        @Override
-        public void onProximityActive(boolean isActive) {
-            synchronized (mSensorObserverLock) {
-                if (mIsProxActive != isActive) {
-                    mIsProxActive = isActive;
-                    recalculateVotesLocked();
-                }
-            }
-        }
-
-        public void observe() {
-            mDisplayManager = mContext.getSystemService(DisplayManager.class);
-            mDisplayManagerInternal = mInjector.getDisplayManagerInternal();
-
-            final SensorManagerInternal sensorManager = mInjector.getSensorManagerInternal();
-            sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
-
-            synchronized (mSensorObserverLock) {
-                for (Display d : mInjector.getDisplays()) {
-                    mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
-                }
-            }
-            mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
-                    DisplayManager.EVENT_FLAG_DISPLAY_ADDED
-                            | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
-                            | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
-        }
-
-        private void recalculateVotesLocked() {
-            final Display[] displays = mInjector.getDisplays();
-            for (Display d : displays) {
-                int displayId = d.getDisplayId();
-                Vote vote = null;
-                if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
-                    final RefreshRateRange rate =
-                            mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
-                                    displayId, mProximitySensorName, mProximitySensorType);
-                    if (rate != null) {
-                        vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
-                    }
-                }
-                mVotesStorage.updateVote(displayId, Vote.PRIORITY_PROXIMITY, vote);
-            }
-        }
-
-        void dump(PrintWriter pw) {
-            pw.println("  SensorObserver");
-            synchronized (mSensorObserverLock) {
-                pw.println("    mIsProxActive=" + mIsProxActive);
-                pw.println("    mDozeStateByDisplay:");
-                for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
-                    final int id = mDozeStateByDisplay.keyAt(i);
-                    final boolean dozed = mDozeStateByDisplay.valueAt(i);
-                    pw.println("      " + id + " -> " + dozed);
-                }
-            }
-        }
-
-        @Override
-        public void onDisplayAdded(int displayId) {
-            boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
-            synchronized (mSensorObserverLock) {
-                mDozeStateByDisplay.put(displayId, isDozeState);
-                recalculateVotesLocked();
-            }
-        }
-
-        @Override
-        public void onDisplayChanged(int displayId) {
-            boolean wasDozeState = mDozeStateByDisplay.get(displayId);
-            synchronized (mSensorObserverLock) {
-                mDozeStateByDisplay.put(displayId,
-                        mInjector.isDozeState(mInjector.getDisplay(displayId)));
-                if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
-                    recalculateVotesLocked();
-                }
-            }
-        }
-
-        @Override
-        public void onDisplayRemoved(int displayId) {
-            synchronized (mSensorObserverLock) {
-                mDozeStateByDisplay.delete(displayId);
-                recalculateVotesLocked();
-            }
-        }
-    }
 
     /**
      * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
diff --git a/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java b/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java
new file mode 100644
index 0000000..11418c1
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java
@@ -0,0 +1,138 @@
+/*
+ * 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.display.mode;
+
+import android.hardware.Sensor;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.util.SparseBooleanArray;
+import android.view.Display;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.sensors.SensorManagerInternal;
+
+import java.io.PrintWriter;
+
+class ProximitySensorObserver implements
+        SensorManagerInternal.ProximityActiveListener,
+        DisplayManager.DisplayListener {
+    private final String mProximitySensorName = null;
+    private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
+
+    private final VotesStorage mVotesStorage;
+    private final DisplayModeDirector.Injector mInjector;
+    @GuardedBy("mSensorObserverLock")
+    private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
+    private final Object mSensorObserverLock = new Object();
+    private DisplayManagerInternal mDisplayManagerInternal;
+    @GuardedBy("mSensorObserverLock")
+    private boolean mIsProxActive = false;
+
+    ProximitySensorObserver(VotesStorage votesStorage, DisplayModeDirector.Injector injector) {
+        mVotesStorage = votesStorage;
+        mInjector = injector;
+    }
+
+    @Override
+    public void onProximityActive(boolean isActive) {
+        synchronized (mSensorObserverLock) {
+            if (mIsProxActive != isActive) {
+                mIsProxActive = isActive;
+                recalculateVotesLocked();
+            }
+        }
+    }
+
+    void observe() {
+        mDisplayManagerInternal = mInjector.getDisplayManagerInternal();
+
+        final SensorManagerInternal sensorManager = mInjector.getSensorManagerInternal();
+        sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
+
+        synchronized (mSensorObserverLock) {
+            for (Display d : mInjector.getDisplays()) {
+                mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
+            }
+        }
+        mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
+                DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+    }
+
+    @GuardedBy("mSensorObserverLock")
+    private void recalculateVotesLocked() {
+        final Display[] displays = mInjector.getDisplays();
+        for (Display d : displays) {
+            int displayId = d.getDisplayId();
+            Vote vote = null;
+            if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
+                final SurfaceControl.RefreshRateRange rate =
+                        mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
+                                displayId, mProximitySensorName, mProximitySensorType);
+                if (rate != null) {
+                    vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
+                }
+            }
+            mVotesStorage.updateVote(displayId, Vote.PRIORITY_PROXIMITY, vote);
+        }
+    }
+
+    void dump(PrintWriter pw) {
+        pw.println("  SensorObserver");
+        synchronized (mSensorObserverLock) {
+            pw.println("    mIsProxActive=" + mIsProxActive);
+            pw.println("    mDozeStateByDisplay:");
+            for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
+                final int id = mDozeStateByDisplay.keyAt(i);
+                final boolean dozed = mDozeStateByDisplay.valueAt(i);
+                pw.println("      " + id + " -> " + dozed);
+            }
+        }
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
+        synchronized (mSensorObserverLock) {
+            mDozeStateByDisplay.put(displayId, isDozeState);
+            recalculateVotesLocked();
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        synchronized (mSensorObserverLock) {
+            boolean wasDozeState = mDozeStateByDisplay.get(displayId);
+            mDozeStateByDisplay.put(displayId,
+                    mInjector.isDozeState(mInjector.getDisplay(displayId)));
+            if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
+                recalculateVotesLocked();
+            }
+        }
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        synchronized (mSensorObserverLock) {
+            mDozeStateByDisplay.delete(displayId);
+            recalculateVotesLocked();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/RefreshRateVote.java b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
index 670b8a1..b96ab3b 100644
--- a/services/core/java/com/android/server/display/mode/RefreshRateVote.java
+++ b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.NonNull;
+
 import java.util.Objects;
 
 
@@ -64,7 +66,7 @@
          *  Vote: min(ignored)     min(applied)  min(applied+physical)  max(applied)  max(ignored)
          */
         @Override
-        public void updateSummary(VoteSummary summary) {
+        public void updateSummary(@NonNull VoteSummary summary) {
             summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate, mMinRefreshRate);
             summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, mMaxRefreshRate);
             // Physical refresh rate cannot be lower than the minimal render frame rate.
@@ -97,7 +99,7 @@
          *  Vote: min(ignored) min(applied)  max(applied+render)     max(applied)  max(ignored)
          */
         @Override
-        public void updateSummary(VoteSummary summary) {
+        public void updateSummary(@NonNull VoteSummary summary) {
             summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
                     mMinRefreshRate);
             summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
diff --git a/services/core/java/com/android/server/display/mode/SizeVote.java b/services/core/java/com/android/server/display/mode/SizeVote.java
index f2f8dc4..f5a5abe 100644
--- a/services/core/java/com/android/server/display/mode/SizeVote.java
+++ b/services/core/java/com/android/server/display/mode/SizeVote.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.NonNull;
+
 import java.util.Objects;
 
 class SizeVote implements Vote {
@@ -48,7 +50,7 @@
     }
 
     @Override
-    public void updateSummary(VoteSummary summary) {
+    public void updateSummary(@NonNull VoteSummary summary) {
         if (mHeight > 0 && mWidth > 0) {
             // For display size, disable refresh rate switching and base mode refresh rate use
             // only the first vote we come across (i.e. the highest priority vote that includes
diff --git a/services/core/java/com/android/server/display/mode/SupportedModesVote.java b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
index 7eebcc0..0cf8311 100644
--- a/services/core/java/com/android/server/display/mode/SupportedModesVote.java
+++ b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
@@ -16,77 +16,42 @@
 
 package com.android.server.display.mode;
 
-import java.util.ArrayList;
+import android.annotation.NonNull;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
-class SupportedModesVote implements Vote {
+public class SupportedModesVote implements Vote {
 
-    final List<SupportedMode> mSupportedModes;
+    final List<Integer> mModeIds;
 
-    SupportedModesVote(List<SupportedMode> supportedModes) {
-        mSupportedModes = Collections.unmodifiableList(supportedModes);
+    SupportedModesVote(List<Integer> modeIds) {
+        mModeIds = Collections.unmodifiableList(modeIds);
+    }
+    @Override
+    public void updateSummary(@NonNull VoteSummary summary) {
+        if (summary.supportedModeIds == null) {
+            summary.supportedModeIds = mModeIds;
+        } else {
+            summary.supportedModeIds.retainAll(mModeIds);
+        }
     }
 
-    /**
-     * Summary should have subset of supported modes.
-     * If Vote1.supportedModes=(A,B), Vote2.supportedModes=(B,C) then summary.supportedModes=(B)
-     * If summary.supportedModes==null then there is no restriction on supportedModes
-     */
     @Override
-    public void updateSummary(VoteSummary summary) {
-        if (summary.supportedModes == null) {
-            summary.supportedModes = new ArrayList<>(mSupportedModes);
-        } else {
-            summary.supportedModes.retainAll(mSupportedModes);
-        }
+    public String toString() {
+        return "SupportedModesVote{ mModeIds=" + mModeIds + " }";
     }
 
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof SupportedModesVote that)) return false;
-        return mSupportedModes.equals(that.mSupportedModes);
+        return mModeIds.equals(that.mModeIds);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSupportedModes);
-    }
-
-    @Override
-    public String toString() {
-        return "SupportedModesVote{ mSupportedModes=" + mSupportedModes + " }";
-    }
-
-    static class SupportedMode {
-        final float mPeakRefreshRate;
-        final float mVsyncRate;
-
-
-        SupportedMode(float peakRefreshRate, float vsyncRate) {
-            mPeakRefreshRate = peakRefreshRate;
-            mVsyncRate = vsyncRate;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof SupportedMode that)) return false;
-            return Float.compare(that.mPeakRefreshRate, mPeakRefreshRate) == 0
-                    && Float.compare(that.mVsyncRate, mVsyncRate) == 0;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mPeakRefreshRate, mVsyncRate);
-        }
-
-        @Override
-        public String toString() {
-            return "SupportedMode{ mPeakRefreshRate=" + mPeakRefreshRate
-                    + ", mVsyncRate=" + mVsyncRate + " }";
-        }
+        return Objects.hash(mModeIds);
     }
 }
diff --git a/services/core/java/com/android/server/display/mode/SupportedRefreshRatesVote.java b/services/core/java/com/android/server/display/mode/SupportedRefreshRatesVote.java
new file mode 100644
index 0000000..5305487
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SupportedRefreshRatesVote.java
@@ -0,0 +1,94 @@
+/*
+ * 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.display.mode;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+class SupportedRefreshRatesVote implements Vote {
+
+    final List<RefreshRates> mRefreshRates;
+
+    SupportedRefreshRatesVote(List<RefreshRates> refreshRates) {
+        mRefreshRates = Collections.unmodifiableList(refreshRates);
+    }
+
+    /**
+     * Summary should have subset of supported modes.
+     * If Vote1.refreshRates=(A,B), Vote2.refreshRates=(B,C)
+     *  then summary.supportedRefreshRates=(B)
+     * If summary.supportedRefreshRates==null then there is no restriction on supportedRefreshRates
+     */
+    @Override
+    public void updateSummary(@NonNull VoteSummary summary) {
+        if (summary.supportedRefreshRates == null) {
+            summary.supportedRefreshRates = new ArrayList<>(mRefreshRates);
+        } else {
+            summary.supportedRefreshRates.retainAll(mRefreshRates);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SupportedRefreshRatesVote that)) return false;
+        return mRefreshRates.equals(that.mRefreshRates);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRefreshRates);
+    }
+
+    @Override
+    public String toString() {
+        return "SupportedRefreshRatesVote{ mSupportedModes=" + mRefreshRates + " }";
+    }
+
+    static class RefreshRates {
+        final float mPeakRefreshRate;
+        final float mVsyncRate;
+
+        RefreshRates(float peakRefreshRate, float vsyncRate) {
+            mPeakRefreshRate = peakRefreshRate;
+            mVsyncRate = vsyncRate;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof RefreshRates that)) return false;
+            return Float.compare(that.mPeakRefreshRate, mPeakRefreshRate) == 0
+                    && Float.compare(that.mVsyncRate, mVsyncRate) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPeakRefreshRate, mVsyncRate);
+        }
+
+        @Override
+        public String toString() {
+            return "RefreshRates{ mPeakRefreshRate=" + mPeakRefreshRate
+                    + ", mVsyncRate=" + mVsyncRate + " }";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/SystemRequestObserver.java b/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
new file mode 100644
index 0000000..15f19cc
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
@@ -0,0 +1,139 @@
+/*
+ * 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.display.mode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SystemRequestObserver responsible for handling system requests to filter allowable display
+ * modes
+ */
+class SystemRequestObserver {
+    private final VotesStorage mVotesStorage;
+
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            // noop, binderDied(@NonNull IBinder who) is overridden
+        }
+        @Override
+        public void binderDied(@NonNull IBinder who) {
+            removeSystemRequestedVotes(who);
+            who.unlinkToDeath(mDeathRecipient, 0);
+        }
+    };
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final Map<IBinder, SparseArray<List<Integer>>> mDisplaysRestrictions = new HashMap<>();
+
+    SystemRequestObserver(VotesStorage storage) {
+        mVotesStorage = storage;
+    }
+
+    void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) {
+        if (modeIds == null) {
+            removeSystemRequestedVote(token, displayId);
+        } else {
+            addSystemRequestedVote(token, displayId, modeIds);
+        }
+    }
+
+    private void addSystemRequestedVote(IBinder token, int displayId, @NonNull int[] modeIds) {
+        try {
+            boolean needLinkToDeath = false;
+            List<Integer> modeIdsList = new ArrayList<>();
+            for (int mode: modeIds) {
+                modeIdsList.add(mode);
+            }
+            synchronized (mLock) {
+                SparseArray<List<Integer>> modesByDisplay = mDisplaysRestrictions.get(token);
+                if (modesByDisplay == null) {
+                    needLinkToDeath = true;
+                    modesByDisplay = new SparseArray<>();
+                    mDisplaysRestrictions.put(token, modesByDisplay);
+                }
+
+                modesByDisplay.put(displayId, modeIdsList);
+                updateStorageLocked(displayId);
+            }
+            if (needLinkToDeath) {
+                token.linkToDeath(mDeathRecipient, 0);
+            }
+        } catch (RemoteException re) {
+            removeSystemRequestedVotes(token);
+        }
+    }
+
+    private void removeSystemRequestedVote(IBinder token, int displayId) {
+        boolean needToUnlink = false;
+        synchronized (mLock) {
+            SparseArray<List<Integer>> modesByDisplay = mDisplaysRestrictions.get(token);
+            if (modesByDisplay != null) {
+                modesByDisplay.remove(displayId);
+                needToUnlink = modesByDisplay.size() == 0;
+                updateStorageLocked(displayId);
+            }
+        }
+        if (needToUnlink) {
+            token.unlinkToDeath(mDeathRecipient, 0);
+        }
+    }
+
+    private void removeSystemRequestedVotes(IBinder token) {
+        synchronized (mLock) {
+            SparseArray<List<Integer>> removed = mDisplaysRestrictions.remove(token);
+            if (removed != null) {
+                for (int i = 0; i < removed.size(); i++) {
+                    updateStorageLocked(removed.keyAt(i));
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void updateStorageLocked(int displayId) {
+        List<Integer> modeIds = new ArrayList<>();
+        boolean[] modesFound = new boolean[1];
+
+        mDisplaysRestrictions.forEach((key, value) -> {
+            List<Integer> modesForDisplay = value.get(displayId);
+            if (modesForDisplay != null) {
+                if (!modesFound[0]) {
+                    modeIds.addAll(modesForDisplay);
+                    modesFound[0] = true;
+                } else {
+                    modeIds.retainAll(modesForDisplay);
+                }
+            }
+        });
+
+        mVotesStorage.updateVote(displayId, Vote.PRIORITY_SYSTEM_REQUESTED_MODES,
+                modesFound[0] ? Vote.forSupportedModes(modeIds) : null);
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index e8d5a19..5b987f4 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.NonNull;
+
 import java.util.List;
 
 interface Vote {
@@ -91,26 +93,29 @@
     // For concurrent displays we want to limit refresh rate on all displays
     int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
 
+    // For internal application to limit display modes to specific ids
+    int PRIORITY_SYSTEM_REQUESTED_MODES = 13;
+
     // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
     // Settings.Global.LOW_POWER_MODE is on.
-    int PRIORITY_LOW_POWER_MODE = 13;
+    int PRIORITY_LOW_POWER_MODE = 14;
 
     // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
     // higher priority voters' result is a range, it will fix the rate to a single choice.
     // It's used to avoid refresh rate switches in certain conditions which may result in the
     // user seeing the display flickering when the switches occur.
-    int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
+    int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 15;
 
     // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
-    int PRIORITY_SKIN_TEMPERATURE = 15;
+    int PRIORITY_SKIN_TEMPERATURE = 16;
 
     // The proximity sensor needs the refresh rate to be locked in order to function, so this is
     // set to a high priority.
-    int PRIORITY_PROXIMITY = 16;
+    int PRIORITY_PROXIMITY = 17;
 
     // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
     // to function, so this needs to be the highest priority of all votes.
-    int PRIORITY_UDFPS = 17;
+    int PRIORITY_UDFPS = 18;
 
     // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
     // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -128,7 +133,7 @@
      */
     int INVALID_SIZE = -1;
 
-    void updateSummary(VoteSummary summary);
+    void updateSummary(@NonNull VoteSummary summary);
 
     static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
         return new CombinedVote(
@@ -166,15 +171,22 @@
         return new BaseModeRefreshRateVote(baseModeRefreshRate);
     }
 
-    static Vote forSupportedModes(List<SupportedModesVote.SupportedMode> supportedModes) {
-        return new SupportedModesVote(supportedModes);
+    static Vote forSupportedRefreshRates(
+            List<SupportedRefreshRatesVote.RefreshRates> refreshRates) {
+        return new SupportedRefreshRatesVote(refreshRates);
+    }
+
+    static Vote forSupportedModes(List<Integer> modeIds) {
+        return new SupportedModesVote(modeIds);
     }
 
 
-    static Vote forSupportedModesAndDisableRefreshRateSwitching(
-            List<SupportedModesVote.SupportedMode> supportedModes) {
+
+    static Vote forSupportedRefreshRatesAndDisableSwitching(
+            List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
         return new CombinedVote(
-                List.of(forDisableRefreshRateSwitching(), forSupportedModes(supportedModes)));
+                List.of(forDisableRefreshRateSwitching(),
+                        forSupportedRefreshRates(supportedRefreshRates)));
     }
 
     static String priorityToString(int priority) {
diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java
index 5fc36b5..d4ce892 100644
--- a/services/core/java/com/android/server/display/mode/VoteSummary.java
+++ b/services/core/java/com/android/server/display/mode/VoteSummary.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.Nullable;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -39,7 +40,11 @@
     public boolean disableRefreshRateSwitching;
     public float appRequestBaseModeRefreshRate;
 
-    public List<SupportedModesVote.SupportedMode> supportedModes;
+    @Nullable
+    public List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates;
+
+    @Nullable
+    public List<Integer> supportedModeIds;
 
     final boolean mIsDisplayResolutionRangeVotingEnabled;
 
@@ -112,6 +117,9 @@
         boolean missingBaseModeRefreshRate = appRequestBaseModeRefreshRate > 0f;
 
         for (Display.Mode mode : modes) {
+            if (!validateRefreshRatesSupported(mode)) {
+                continue;
+            }
             if (!validateModeSupported(mode)) {
                 continue;
             }
@@ -253,21 +261,37 @@
     }
 
     private boolean validateModeSupported(Display.Mode mode) {
-        if (supportedModes == null || !mSupportedModesVoteEnabled) {
+        if (supportedModeIds == null || !mSupportedModesVoteEnabled) {
             return true;
         }
-        for (SupportedModesVote.SupportedMode supportedMode : supportedModes) {
-            if (equalsWithinFloatTolerance(mode.getRefreshRate(), supportedMode.mPeakRefreshRate)
-                    && equalsWithinFloatTolerance(mode.getVsyncRate(), supportedMode.mVsyncRate)) {
+        if (supportedModeIds.contains(mode.getModeId())) {
+            return true;
+        }
+        if (mLoggingEnabled) {
+            Slog.w(TAG, "Discarding mode " + mode.getModeId()
+                    + ", supportedMode not found"
+                    + ": mode.modeId=" + mode.getModeId()
+                    + ", supportedModeIds=" + supportedModeIds);
+        }
+        return false;
+    }
+
+    private boolean validateRefreshRatesSupported(Display.Mode mode) {
+        if (supportedRefreshRates == null || !mSupportedModesVoteEnabled) {
+            return true;
+        }
+        for (SupportedRefreshRatesVote.RefreshRates refreshRates : this.supportedRefreshRates) {
+            if (equalsWithinFloatTolerance(mode.getRefreshRate(), refreshRates.mPeakRefreshRate)
+                    && equalsWithinFloatTolerance(mode.getVsyncRate(), refreshRates.mVsyncRate)) {
                 return true;
             }
         }
         if (mLoggingEnabled) {
             Slog.w(TAG, "Discarding mode " + mode.getModeId()
-                    + ", supportedMode not found"
+                    + ", supportedRefreshRates not found"
                     + ": mode.refreshRate=" + mode.getRefreshRate()
                     + ", mode.vsyncRate=" + mode.getVsyncRate()
-                    + ", supportedModes=" + supportedModes);
+                    + ", supportedRefreshRates=" + supportedRefreshRates);
         }
         return false;
     }
@@ -298,7 +322,8 @@
             return false;
         }
 
-        if (supportedModes != null && mSupportedModesVoteEnabled && supportedModes.isEmpty()) {
+        if (supportedRefreshRates != null && mSupportedModesVoteEnabled
+                && supportedRefreshRates.isEmpty()) {
             if (mLoggingEnabled) {
                 Slog.w(TAG, "Vote summary resulted in empty set (empty supportedModes)");
             }
@@ -345,7 +370,8 @@
         minHeight = 0;
         disableRefreshRateSwitching = false;
         appRequestBaseModeRefreshRate = 0f;
-        supportedModes = null;
+        supportedRefreshRates = null;
+        supportedModeIds = null;
         if (mLoggingEnabled) {
             Slog.i(TAG, "Summary reset: " + this);
         }
@@ -367,7 +393,8 @@
                 + ", minHeight=" + minHeight
                 + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
                 + ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate
-                + ", supportedModes=" + supportedModes
+                + ", supportedRefreshRates=" + supportedRefreshRates
+                + ", supportedModeIds=" + supportedModeIds
                 + ", mIsDisplayResolutionRangeVotingEnabled="
                 + mIsDisplayResolutionRangeVotingEnabled
                 + ", mSupportedModesVoteEnabled=" + mSupportedModesVoteEnabled
diff --git a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
index e80b9451..7562a52 100644
--- a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
+++ b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
@@ -117,11 +117,11 @@
             maxRefreshRate = (int) physicalVote.mMaxRefreshRate;
         } else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) {
             maxRefreshRate = (int)  renderVote.mMaxRefreshRate;
-        } else if (vote instanceof SupportedModesVote supportedModesVote) {
-            // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed
+        } else if (vote instanceof SupportedRefreshRatesVote refreshRatesVote) {
+            // SupportedRefreshRatesVote limits mode by refreshRates, so highest rr is allowed
             maxRefreshRate = 0;
-            for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) {
-                maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate);
+            for (SupportedRefreshRatesVote.RefreshRates rr : refreshRatesVote.mRefreshRates) {
+                maxRefreshRate = Math.max(maxRefreshRate, (int) rr.mPeakRefreshRate);
             }
         } else if (vote instanceof CombinedVote combinedVote) {
             for (Vote subVote: combinedVote.mVotes) {
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 56c7c18..6becf1c 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -124,6 +125,44 @@
         }
     }
 
+    /** removes all votes with certain priority from vote storage */
+    void removeAllVotesForPriority(int priority) {
+        if (mLoggingEnabled) {
+            Slog.i(TAG, "removeAllVotesForPriority(priority="
+                    + Vote.priorityToString(priority) + ")");
+        }
+        if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
+            Slog.w(TAG, "Received an invalid priority, ignoring:"
+                    + " priority=" + Vote.priorityToString(priority));
+            return;
+        }
+        IntArray removedVotesDisplayIds = new IntArray();
+        synchronized (mStorageLock) {
+            int size = mVotesByDisplay.size();
+            for (int i = 0; i < size; i++) {
+                SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
+                if (votes.get(priority) != null) {
+                    votes.remove(priority);
+                    removedVotesDisplayIds.add(mVotesByDisplay.keyAt(i));
+                }
+            }
+        }
+        if (mLoggingEnabled) {
+            Slog.i(TAG, "Removed votes with priority=" + priority
+                    + " for displays=" + removedVotesDisplayIds);
+        }
+        int removedVotesSize = removedVotesDisplayIds.size();
+        if (removedVotesSize > 0) {
+            if (mVotesStatsReporter != null) {
+                for (int i = 0; i < removedVotesSize; i++) {
+                    mVotesStatsReporter.reportVoteChanged(
+                            removedVotesDisplayIds.get(i), priority, null);
+                }
+            }
+            mListener.onChanged();
+        }
+    }
+
     /** dump class values, for debugging */
     void dump(@NonNull PrintWriter pw) {
         SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index b8ae737..f21fd41 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -146,6 +146,10 @@
         SPLIT_SCREEN_NAVIGATION(
                 FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION,
                 "SPLIT_SCREEN_NAVIGATION"),
+
+        CHANGE_SPLITSCREEN_FOCUS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS,
+                "CHANGE_SPLITSCREEN_FOCUS"),
         TRIGGER_BUG_REPORT(
                 FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT,
                 "TRIGGER_BUG_REPORT"),
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index a100fe0..29cccdf 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -74,7 +74,6 @@
     @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
     @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
     @GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
-    @GuardedBy("ImfLock.class") private int mCurSeq;
     @GuardedBy("ImfLock.class") private boolean mVisibleBound;
     @GuardedBy("ImfLock.class") private boolean mSupportsStylusHw;
     @GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw;
@@ -195,27 +194,6 @@
     }
 
     /**
-     * The current binding sequence number, incremented every time there is
-     * a new bind performed.
-     */
-    @GuardedBy("ImfLock.class")
-    int getSequenceNumber() {
-        return mCurSeq;
-    }
-
-    /**
-     * Increase the current binding sequence number by one.
-     * Reset to 1 on overflow.
-     */
-    @GuardedBy("ImfLock.class")
-    void advanceSequenceNumber() {
-        mCurSeq += 1;
-        if (mCurSeq <= 0) {
-            mCurSeq = 1;
-        }
-    }
-
-    /**
      * If non-null, this is the input method service we are currently connected
      * to.
      */
@@ -435,9 +413,11 @@
             mLastBindTime = SystemClock.uptimeMillis();
 
             addFreshWindowToken();
+            final UserData monitor = UserData.getOrCreate(
+                    mService.getCurrentImeUserIdLocked());
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                    null, null, null, mCurId, mCurSeq, false);
+                    null, null, null, mCurId, monitor.mSequence.getSequenceNumber(), false);
         }
 
         Slog.w(InputMethodManagerService.TAG,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 50d2c63..d0a83a6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -479,7 +479,8 @@
      */
     @GuardedBy("ImfLock.class")
     private int getSequenceNumberLocked() {
-        return mBindingController.getSequenceNumber();
+        final UserData monitor = UserData.getOrCreate(mCurrentUserId);
+        return monitor.mSequence.getSequenceNumber();
     }
 
     /**
@@ -488,7 +489,8 @@
      */
     @GuardedBy("ImfLock.class")
     private void advanceSequenceNumberLocked() {
-        mBindingController.advanceSequenceNumber();
+        final UserData monitor = UserData.getOrCreate(mCurrentUserId);
+        monitor.mSequence.advanceSequenceNumber();
     }
 
     @GuardedBy("ImfLock.class")
@@ -558,10 +560,13 @@
     private InputMethodSubtype mCurrentSubtype;
 
     /**
-     * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
+     * Map of window perceptible states indexed by their associated window tokens.
+     *
+     * The value {@code true} indicates that IME has not been mostly hidden via
+     * {@link android.view.InsetsController} for the given window.
      */
-    @MultiUserUnawareField
-    private boolean mCurPerceptible;
+    @GuardedBy("ImfLock.class")
+    private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>();
 
     /**
      * Set to true if our ServiceConnection is currently actively bound to
@@ -1366,6 +1371,7 @@
         // InputMethodSettingsRepository should be initialized before buildInputMethodListLocked
         InputMethodSettingsRepository.initialize(mHandler, mContext);
         AdditionalSubtypeMapRepository.initialize(mHandler, mContext);
+        UserData.initialize(mHandler);
 
         mCurrentUserId = mActivityManagerInternal.getCurrentUserId();
 
@@ -2841,12 +2847,16 @@
                     + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
                     + " displayId: " + mCurTokenDisplayId);
         }
+        final IBinder focusedWindowToken = mImeBindingState != null
+                ? mImeBindingState.mFocusedWindow : null;
+        final Boolean windowPerceptible = focusedWindowToken != null
+                ? mFocusedWindowPerceptible.get(focusedWindowToken) : null;
 
         // TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
         // all updateSystemUi happens on system privilege.
         final long ident = Binder.clearCallingIdentity();
         try {
-            if (!mCurPerceptible) {
+            if (windowPerceptible != null && !windowPerceptible) {
                 if ((vis & InputMethodService.IME_VISIBLE) != 0) {
                     vis &= ~InputMethodService.IME_VISIBLE;
                     vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
@@ -3313,11 +3323,12 @@
         Binder.withCleanCallingIdentity(() -> {
             Objects.requireNonNull(windowToken, "windowToken must not be null");
             synchronized (ImfLock.class) {
+                Boolean windowPerceptible = mFocusedWindowPerceptible.get(windowToken);
                 if (mImeBindingState.mFocusedWindow != windowToken
-                        || mCurPerceptible == perceptible) {
+                        || (windowPerceptible != null && windowPerceptible == perceptible)) {
                     return;
                 }
-                mCurPerceptible = perceptible;
+                mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
                 updateSystemUiLocked();
             }
         });
@@ -3695,7 +3706,7 @@
         }
 
         mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
-        mCurPerceptible = true;
+        mFocusedWindowPerceptible.put(windowToken, true);
 
         // We want to start input before showing the IME, but after closing
         // it.  We want to do this after closing it to help the IME disappear
@@ -5546,9 +5557,11 @@
         public void reportImeControl(@Nullable IBinder windowToken) {
             synchronized (ImfLock.class) {
                 if (mImeBindingState.mFocusedWindow != windowToken) {
-                    // mCurPerceptible was set by the focused window, but it is no longer in
-                    // control, so we reset mCurPerceptible.
-                    mCurPerceptible = true;
+                    // A perceptible value was set for the focused window, but it is no longer in
+                    // control, so we reset the perceptible for the window passed as argument.
+                    // TODO(b/314149476): Investigate whether this logic is still relevant, if not
+                    //     then consider removing using concurrent_input_methods feature flag.
+                    mFocusedWindowPerceptible.put(windowToken, true);
                 }
             }
         }
@@ -5849,11 +5862,11 @@
                 p.println("    curSession=" + c.mCurSession);
             };
             mClientController.forAllClients(clientControllerDump);
-
+            p.println("  mCurrentUserId=" + mCurrentUserId);
             p.println("  mCurMethodId=" + getSelectedMethodIdLocked());
             client = mCurClient;
             p.println("  mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
-            p.println("  mCurPerceptible=" + mCurPerceptible);
+            p.println("  mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
             mImeBindingState.dump("  ", p);
             p.println("  mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked()
                     + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
@@ -5875,8 +5888,6 @@
                     ? Arrays.toString(mStylusIds.toArray()) : ""));
             p.println("  mSwitchingController:");
             mSwitchingController.dump(p);
-            p.println("  mSettings:");
-            settings.dump(p, "    ");
 
             p.println("  mStartInputHistory:");
             mStartInputHistory.dump(pw, "    ");
diff --git a/services/core/java/com/android/server/inputmethod/Sequence.java b/services/core/java/com/android/server/inputmethod/Sequence.java
new file mode 100644
index 0000000..05e31ce
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/Sequence.java
@@ -0,0 +1,45 @@
+/*
+ * 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.inputmethod;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A sequence number utility class that only generate positive numbers.
+ */
+final class Sequence {
+
+    private final Object mLock = new Object();
+
+    private int mSequence;
+
+    int getSequenceNumber() {
+        synchronized (mLock) {
+            return mSequence;
+        }
+    }
+
+    @GuardedBy("ImfLock.class")
+    void advanceSequenceNumber() {
+        synchronized (mLock) {
+            mSequence++;
+            if (mSequence <= 0) {
+                mSequence = 1;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/UserData.java b/services/core/java/com/android/server/inputmethod/UserData.java
new file mode 100644
index 0000000..fc2a422
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/UserData.java
@@ -0,0 +1,88 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+final class UserData {
+
+    @NonNull
+    private static final SparseArray<UserData> sPerUserMonitor = new SparseArray<>();
+
+    @UserIdInt
+    final int mUserId;
+
+    @GuardedBy("ImfLock.class")
+    final Sequence mSequence = new Sequence();
+
+    /**
+     * Not intended to be instantiated.
+     */
+    private UserData(int userId) {
+        mUserId = userId;
+    }
+
+    @GuardedBy("ImfLock.class")
+    static UserData getOrCreate(@UserIdInt int userId) {
+        UserData monitor = sPerUserMonitor.get(userId);
+        if (monitor == null) {
+            monitor = new UserData(userId);
+            sPerUserMonitor.put(userId, monitor);
+        }
+        return monitor;
+    }
+
+    static void initialize(Handler handler) {
+        final UserManagerInternal userManagerInternal =
+                LocalServices.getService(UserManagerInternal.class);
+        userManagerInternal.addUserLifecycleListener(
+                new UserManagerInternal.UserLifecycleListener() {
+                    @Override
+                    public void onUserRemoved(UserInfo user) {
+                        final int userId = user.id;
+                        handler.post(() -> {
+                            synchronized (ImfLock.class) {
+                                sPerUserMonitor.remove(userId);
+                            }
+                        });
+                    }
+
+                    @Override
+                    public void onUserCreated(UserInfo user, Object unusedToken) {
+                        final int userId = user.id;
+                        handler.post(() -> {
+                            synchronized (ImfLock.class) {
+                                getOrCreate(userId);
+                            }
+                        });
+                    }
+                });
+        synchronized (ImfLock.class) {
+            for (int userId : userManagerInternal.getUserIds()) {
+                getOrCreate(userId);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 97ce77c..18b495b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -340,8 +340,6 @@
     static final String TAG = NetworkPolicyLogger.TAG;
     private static final boolean LOGD = NetworkPolicyLogger.LOGD;
     private static final boolean LOGV = NetworkPolicyLogger.LOGV;
-    // TODO: b/304347838 - Remove once the feature is in staging.
-    private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false;
 
     /**
      * No opportunistic quota could be calculated from user data plan or data settings.
@@ -1070,8 +1068,7 @@
                     }
 
                     // The flag is boot-stable.
-                    mBackgroundNetworkRestricted = ALWAYS_RESTRICT_BACKGROUND_NETWORK
-                            && Flags.networkBlockedForTopSleepingAndAbove();
+                    mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove();
                     if (mBackgroundNetworkRestricted) {
                         // Firewall rules and UidBlockedState will get updated in
                         // updateRulesForGlobalChangeAL below.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9e4ea6a..3eeeae7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -148,6 +148,7 @@
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings.Global;
+import android.service.persistentdata.PersistentDataBlockManager;
 import android.stats.devicepolicy.DevicePolicyEnums;
 import android.system.ErrnoException;
 import android.system.Int64Ref;
@@ -2364,8 +2365,21 @@
             assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
             assertNoWriteFileTransfersOpenLocked();
 
-            final boolean isSecureFrpEnabled =
-                    Global.getInt(mContext.getContentResolver(), Global.SECURE_FRP_MODE, 0) == 1;
+            boolean isSecureFrpEnabled;
+            if (android.security.Flags.frpEnforcement()) {
+                PersistentDataBlockManager pdbManager =
+                        mContext.getSystemService(PersistentDataBlockManager.class);
+                if (pdbManager == null) {
+                    // Some devices may not support FRP. In that case, we can't block the install
+                    // accordingly.
+                    isSecureFrpEnabled = false;
+                } else {
+                    isSecureFrpEnabled = pdbManager.isFactoryResetProtectionActive();
+                }
+            } else {
+                isSecureFrpEnabled = Global.getInt(mContext.getContentResolver(),
+                        Global.SECURE_FRP_MODE, 0) == 1;
+            }
 
             if (isSecureFrpEnabled
                     && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c9fd261..1919137 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -217,8 +217,7 @@
     private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
             UserManager.DISALLOW_RECORD_AUDIO,
             UserManager.DISALLOW_WALLPAPER,
-            UserManager.DISALLOW_OEM_UNLOCK,
-            UserManager.DISALLOW_ADD_PRIVATE_PROFILE
+            UserManager.DISALLOW_OEM_UNLOCK
     );
 
     /**
@@ -293,7 +292,8 @@
                     UserManager.DISALLOW_USB_FILE_TRANSFER,
                     UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
                     UserManager.DISALLOW_UNMUTE_MICROPHONE,
-                    UserManager.DISALLOW_CONFIG_DEFAULT_APPS
+                    UserManager.DISALLOW_CONFIG_DEFAULT_APPS,
+                    UserManager.DISALLOW_ADD_PRIVATE_PROFILE
     );
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 266418f..9e31748 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1506,9 +1506,7 @@
     }
 
     private void stemPrimaryPress(int count) {
-        if (DEBUG_INPUT) {
-            Slog.d(TAG, "stemPrimaryPress: " + count);
-        }
+        Slog.d(TAG, "stemPrimaryPress: " + count);
         if (count == 3) {
             stemPrimaryTriplePressAction(mTriplePressOnStemPrimaryBehavior);
         } else if (count == 2) {
@@ -1519,22 +1517,18 @@
     }
 
     private void stemPrimarySinglePressAction(int behavior) {
-        if (DEBUG_INPUT) {
-            Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
-        }
+        Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
         if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;
 
         final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
         if (keyguardActive) {
             // If keyguarded then notify the keyguard.
             mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+            Slog.d(TAG, "stemPrimarySinglePressAction: skip due to keyguard");
             return;
         }
         switch (behavior) {
             case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
-                if (DEBUG_INPUT) {
-                    Slog.d(TAG, "Executing stem primary short press action behavior.");
-                }
                 Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
                 allAppsIntent.addFlags(
                         Intent.FLAG_ACTIVITY_NEW_TASK
@@ -1542,12 +1536,6 @@
                 startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
                 break;
             case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
-                if (DEBUG_INPUT) {
-                    Slog.d(
-                            TAG,
-                            "Executing stem primary short press action behavior for launching "
-                                    + "target activity.");
-                }
                 if (mPrimaryShortPressTargetActivity != null) {
                     Intent targetActivityIntent = new Intent();
                     targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
@@ -1578,13 +1566,11 @@
     }
 
     private void stemPrimaryDoublePressAction(int behavior) {
+        Slog.d(TAG, "stemPrimaryDoublePressAction: " + behavior);
         switch (behavior) {
             case DOUBLE_PRESS_PRIMARY_NOTHING:
                 break;
             case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
-                if (DEBUG_INPUT) {
-                    Slog.d(TAG, "Executing stem primary double press action behavior.");
-                }
                 final boolean keyguardActive = mKeyguardDelegate == null
                         ? false
                         : mKeyguardDelegate.isShowing();
@@ -1596,13 +1582,11 @@
     }
 
     private void stemPrimaryTriplePressAction(int behavior) {
+        Slog.d(TAG, "stemPrimaryTriplePressAction: " + behavior);
         switch (behavior) {
             case TRIPLE_PRESS_PRIMARY_NOTHING:
                 break;
             case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
-                if (DEBUG_INPUT) {
-                    Slog.d(TAG, "Executing stem primary triple press action behavior.");
-                }
                 mTalkbackShortcutController.toggleTalkback(mCurrentUserId);
                 if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
                     performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */
@@ -1614,9 +1598,7 @@
     }
 
     private void stemPrimaryLongPress(long eventTime) {
-        if (DEBUG_INPUT) {
-            Slog.d(TAG, "Executing stem primary long press action behavior.");
-        }
+        Slog.d(TAG, "stemPrimaryLongPress: "  + mLongPressOnStemPrimaryBehavior);
 
         switch (mLongPressOnStemPrimaryBehavior) {
             case LONG_PRESS_PRIMARY_NOTHING:
@@ -1696,10 +1678,19 @@
         return mLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING;
     }
 
+    /** Determine whether the device has any stem primary behaviors. */
     private boolean hasStemPrimaryBehavior() {
+        // Read the default stem behaviors from the XML config to determine whether stem primary
+        // behaviors are supported in this build. If they are supported, then the behaviors may be
+        // overridden at runtime through their respective Settings overrides. If they are not
+        // supported, the Settings overrides will not apply.
+        final int defaultShortPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior);
+        final int defaultLongPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior);
         return getMaxMultiPressStemPrimaryCount() > 1
-                || hasLongPressOnStemPrimaryBehavior()
-                || mShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
+                || defaultLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING
+                || defaultShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
     }
 
     private void interceptScreenshotChord(int source, long pressDelay) {
@@ -2787,6 +2778,7 @@
                             KeyEvent.KEYCODE_STEM_PRIMARY,
                             eventTime,
                             () -> {
+                                Slog.d(TAG, "StemPrimaryKeyRule: executing deferred onKeyUp");
                                 // Save the info of the focused task on screen. This may be used
                                 // later to bring the current focused task back to top. For
                                 // example, stem primary triple press enables the A11y interface
@@ -3526,6 +3518,9 @@
                         moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
                                 true /* leftOrTop */);
                         logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
+                    } else if (event.isAltPressed()) {
+                        setSplitscreenFocus(true /* leftOrTop */);
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.CHANGE_SPLITSCREEN_FOCUS);
                     } else {
                         logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
                         injectBackGesture(event.getDownTime());
@@ -3534,11 +3529,17 @@
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
-                    moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
-                            false /* leftOrTop */);
-                    logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
-                    return true;
+                if (firstDown && event.isMetaPressed()) {
+                    if (event.isCtrlPressed()) {
+                        moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
+                                false /* leftOrTop */);
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
+                        return true;
+                    } else if (event.isAltPressed()) {
+                        setSplitscreenFocus(false /* leftOrTop */);
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.CHANGE_SPLITSCREEN_FOCUS);
+                        return true;
+                    }
                 }
                 break;
             case KeyEvent.KEYCODE_SLASH:
@@ -4398,6 +4399,13 @@
         }
     }
 
+    private void setSplitscreenFocus(boolean leftOrTop) {
+        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+        if (statusbar != null) {
+            statusbar.setSplitscreenFocus(leftOrTop);
+        }
+    }
+
     void launchHomeFromHotKey(int displayId) {
         launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b50e2bf..6ff8cf3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -56,6 +56,7 @@
 import android.database.ContentObserver;
 import android.hardware.SensorManager;
 import android.hardware.SystemSensorManager;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.DisplayManagerInternal;
@@ -7152,9 +7153,10 @@
         private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
 
         @Override
-        public void onStateChanged(int deviceState) {
-            if (mDeviceState != deviceState) {
-                mDeviceState = deviceState;
+        public void onDeviceStateChanged(@NonNull DeviceState deviceState) {
+            int stateIdentifier = deviceState.getIdentifier();
+            if (mDeviceState != stateIdentifier) {
+                mDeviceState = stateIdentifier;
                 // Device-state interactions are applied to the default display so that they
                 // are reflected only with the default power group.
                 userActivityInternal(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(),
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index c73f89c..f7c236a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -238,6 +238,14 @@
     void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop);
 
     /**
+     * Change the split screen focus to the left / top app or the right / bottom app based on
+     * {@param leftOrTop}.
+     *
+     * @see com.android.internal.statusbar.IStatusBar#setSplitscreenFocus
+     */
+    void setSplitscreenFocus(boolean leftOrTop);
+
+    /**
      * Shows the media output switcher dialog.
      *
      * @param packageName of the session for which the output switcher is shown.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 214dbe0..7b3e237 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -830,6 +830,15 @@
         }
 
         @Override
+        public void setSplitscreenFocus(boolean leftOrTop) {
+            IStatusBar bar = mBar;
+            if (bar != null) {
+                try {
+                    bar.setSplitscreenFocus(leftOrTop);
+                } catch (RemoteException ex) { }
+            }
+        }
+        @Override
         public void enterDesktop(int displayId) {
             IStatusBar bar = mBar;
             if (bar != null) {
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 7206c03..ecd140e 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -89,7 +89,7 @@
 
     @NonNull private final TelephonyManager mTelephonyManager;
     @NonNull private final SubscriptionManager mSubscriptionManager;
-    @NonNull private final CarrierConfigManager mCarrierConfigManager;
+    @Nullable private final CarrierConfigManager mCarrierConfigManager;
 
     @NonNull private final ActiveDataSubscriptionIdListener mActiveDataSubIdListener;
 
@@ -158,8 +158,10 @@
         mSubscriptionManager.addOnSubscriptionsChangedListener(
                 executor, mSubscriptionChangedListener);
         mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
-        mCarrierConfigManager.registerCarrierConfigChangeListener(executor,
-                mCarrierConfigChangeListener);
+        if (mCarrierConfigManager != null) {
+            mCarrierConfigManager.registerCarrierConfigChangeListener(executor,
+                    mCarrierConfigChangeListener);
+        }
 
         registerCarrierPrivilegesCallbacks();
     }
@@ -200,7 +202,10 @@
         mContext.unregisterReceiver(this);
         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
         mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
-        mCarrierConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+        if (mCarrierConfigManager != null) {
+            mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+                    mCarrierConfigChangeListener);
+        }
 
         unregisterCarrierPrivilegesCallbacks();
     }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 1a63f14..5184e49 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -228,11 +228,8 @@
     private int mAppTransitionState = APP_STATE_IDLE;
 
     private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
-    private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
     private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
 
-    private final boolean mGridLayoutRecentsEnabled;
-
     private final int mDefaultWindowAnimationStyleResId;
     private boolean mOverrideTaskTransition;
 
@@ -249,8 +246,6 @@
         mTransitionAnimation = new TransitionAnimation(
                 context, ProtoLog.isEnabled(WM_DEBUG_ANIM), TAG);
 
-        mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
-
         final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
                 com.android.internal.R.styleable.Window);
         mDefaultWindowAnimationStyleResId = windowStyle.getResourceId(
@@ -493,11 +488,6 @@
         mListeners.remove(listener);
     }
 
-    void registerKeygaurdExitAnimationStartListener(
-            KeyguardExitAnimationStartListener listener) {
-        mKeyguardExitAnimationStartListener = listener;
-    }
-
     public void notifyAppTransitionFinishedLocked(IBinder token) {
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -1595,14 +1585,6 @@
         return mNextAppTransitionRequests.contains(transit);
     }
 
-    /**
-     * @return whether the transition should show the thumbnail being scaled down.
-     */
-    private boolean shouldScaleDownThumbnailTransition(int uiMode, int orientation) {
-        return mGridLayoutRecentsEnabled
-                || orientation == Configuration.ORIENTATION_PORTRAIT;
-    }
-
     private void handleAppTransitionTimeout() {
         synchronized (mService.mGlobalLock) {
             final DisplayContent dc = mDisplayContent;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 56e6107..1dcfde4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3521,6 +3521,9 @@
     }
 
     void enableHighPerfTransition(boolean enable) {
+        if (!mWmService.mSupportsHighPerfTransitions) {
+            return;
+        }
         if (!explicitRefreshRateHints()) {
             if (enable) {
                 getPendingTransaction().setEarlyWakeupStart();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e789fec..16f7373 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -117,6 +117,7 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowLayout;
@@ -969,19 +970,30 @@
                 break;
 
             case TYPE_BASE_APPLICATION:
-
-                // A non-translucent main app window isn't allowed to fit insets or display cutouts,
-                // as it would create a hole on the display!
                 if (attrs.isFullscreen() && win.mActivityRecord != null
                         && win.mActivityRecord.fillsParent()
-                        && (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
-                        && (attrs.getFitInsetsTypes() != 0
-                                || (attrs.privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
-                                        && attrs.layoutInDisplayCutoutMode
-                                                != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)) {
-                    throw new IllegalArgumentException("Illegal attributes: Main activity window"
-                            + " that isn't translucent trying to fit insets or display cutouts."
-                            + " attrs=" + attrs);
+                        && (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0) {
+                    if (attrs.getFitInsetsTypes() != 0) {
+                        // A non-translucent main app window isn't allowed to fit insets,
+                        // as it would create a hole on the display!
+                        throw new IllegalArgumentException("Illegal attributes: Main window of "
+                                + win.mActivityRecord.getName() + " that isn't translucent trying"
+                                + " to fit insets. fitInsetsTypes=" + WindowInsets.Type.toString(
+                                        attrs.getFitInsetsTypes()));
+                    }
+                    if ((attrs.privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
+                            && attrs.layoutInDisplayCutoutMode
+                                    != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+                        // A non-translucent main window of the app enforced to go edge-to-edge
+                        // isn't allowed to fit display cutout, or it will cause software bezels.
+                        throw new IllegalArgumentException("Illegal attributes: Main window of "
+                                + win.mActivityRecord.getName() + " that isn't translucent and"
+                                + " targets SDK level " + win.mActivityRecord.mTargetSdk
+                                + " (>= 35) trying to specify layoutInDisplayCutoutMode as '"
+                                + WindowManager.LayoutParams.layoutInDisplayCutoutModeToString(
+                                        attrs.layoutInDisplayCutoutMode)
+                                + "' instead of 'always'");
+                    }
                 }
                 break;
         }
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 34ea4ac..8116f68 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.View.DRAG_FLAG_GLOBAL;
 import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION;
 import static android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;
@@ -36,6 +37,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DragEvent;
@@ -52,6 +54,7 @@
 import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
 
 import java.util.Objects;
+import java.util.Random;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -386,6 +389,9 @@
                     + "(listener=" + mGlobalDragListener + ", flags=" + mDragState.mFlags + ")");
             return false;
         }
+        final int traceCookie = new Random().nextInt();
+        Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#notifyUnhandledDrop",
+                traceCookie);
         if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to unhandled listener (" + reason + ")");
         try {
             // Schedule timeout for the unhandled drag listener to call back
@@ -396,6 +402,8 @@
                     if (DEBUG_DRAG) Slog.d(TAG_WM, "Unhandled listener finished handling DROP");
                     synchronized (mService.mGlobalLock) {
                         onUnhandledDropCallback(consumedByListener);
+                        Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER,
+                                "DragDropController#notifyUnhandledDrop", traceCookie);
                     }
                 }
             });
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index b266caa..5ed343a 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -20,6 +20,7 @@
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -49,6 +50,7 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
@@ -258,6 +260,7 @@
             }
             mNotifiedWindows.clear();
             mDragInProgress = false;
+            Trace.instant(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DRAG_ENDED");
         }
 
         // Take the cursor back if it has been changed.
@@ -343,6 +346,18 @@
         if (mAnimator != null) {
             return false;
         }
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DROP");
+            return reportDropWindowLockInner(token, x, y);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
+    private boolean reportDropWindowLockInner(IBinder token, float x, float y) {
+        if (mAnimator != null) {
+            return false;
+        }
 
         final WindowState touchedWin = mService.mInputToWindowMap.get(token);
         final DragEvent unhandledDropEvent = createDropEvent(x, y, null /* touchedWin */,
@@ -355,10 +370,12 @@
                 return true;
             }
 
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#noWindow");
             // "drop" outside a valid window -- no recipient to apply a timeout to, and we can send
             // the drag-ended message immediately.
             endDragLocked(false /* consumed */, false /* relinquishDragSurfaceToDropTarget */);
             if (DEBUG_DRAG) Slog.d(TAG_WM, "Drop outside a valid window " + touchedWin);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             return false;
         }
 
@@ -367,6 +384,7 @@
         final IBinder clientToken = touchedWin.mClient.asBinder();
         final DragEvent event = createDropEvent(x, y, touchedWin, false /* includePrivateInfo */);
         try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#dispatchDrop");
             touchedWin.mClient.dispatchDragEvent(event);
 
             // 5 second timeout for this window to respond to the drop
@@ -380,6 +398,7 @@
             if (MY_PID != touchedWin.mSession.mPid) {
                 event.recycle();
             }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         mToken = clientToken;
         mUnhandledDropEvent = unhandledDropEvent;
@@ -471,6 +490,7 @@
     /* call out to each visible window/session informing it about the drag
      */
     void broadcastDragStartedLocked(final float touchX, final float touchY) {
+        Trace.instant(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DRAG_STARTED");
         mOriginalX = mCurrentX = touchX;
         mOriginalY = mCurrentY = touchY;
 
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index a24c02a..5d613cf 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -22,7 +22,6 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
@@ -132,7 +131,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.LetterboxDetails;
 import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
-import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -196,8 +194,7 @@
     private final boolean mIsOverrideCameraCompatDisableRefreshEnabled;
     // Corresponds to OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE
     private final boolean mIsOverrideCameraCompatEnableRefreshViaPauseEnabled;
-    // Corresponds to OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT
-    private final boolean mIsOverrideCameraCompatDisableFreeformWindowingTreatmentEnabled;
+
     // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION
     private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled;
     // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED
@@ -324,15 +321,15 @@
                         PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
         mBooleanPropertyCameraCompatAllowForceRotation =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
-                        mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+                        () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(),
                         PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION);
         mBooleanPropertyCameraCompatAllowRefresh =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
-                        mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+                        () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(),
                         PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH);
         mBooleanPropertyCameraCompatEnableRefreshViaPause =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
-                        mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+                        () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(),
                         PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
 
         mBooleanPropertyAllowOrientationOverride =
@@ -354,11 +351,11 @@
 
         mBooleanPropertyAllowUserAspectRatioOverride =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
-                        mLetterboxConfiguration::isUserAppAspectRatioSettingsEnabled,
+                        () -> mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled(),
                         PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
         mBooleanPropertyAllowUserAspectRatioFullscreenOverride =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
-                        mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled,
+                        () -> mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled(),
                         PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
 
         mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
@@ -383,8 +380,6 @@
                 isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH);
         mIsOverrideCameraCompatEnableRefreshViaPauseEnabled =
                 isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
-        mIsOverrideCameraCompatDisableFreeformWindowingTreatmentEnabled =
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
 
         mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled =
                 isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION);
@@ -764,7 +759,8 @@
      */
     boolean shouldRefreshActivityForCameraCompat() {
         return shouldEnableWithOptOutOverrideAndProperty(
-                /* gatingCondition */ mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+                /* gatingCondition */ () -> mLetterboxConfiguration
+                        .isCameraCompatTreatmentEnabled(),
                 mIsOverrideCameraCompatDisableRefreshEnabled,
                 mBooleanPropertyCameraCompatAllowRefresh);
     }
@@ -785,7 +781,8 @@
      */
     boolean shouldRefreshActivityViaPauseForCameraCompat() {
         return shouldEnableWithOverrideAndProperty(
-                /* gatingCondition */ mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+                /* gatingCondition */ () -> mLetterboxConfiguration
+                        .isCameraCompatTreatmentEnabled(),
                 mIsOverrideCameraCompatEnableRefreshViaPauseEnabled,
                 mBooleanPropertyCameraCompatEnableRefreshViaPause);
     }
@@ -803,34 +800,12 @@
      */
     boolean shouldForceRotateForCameraCompat() {
         return shouldEnableWithOptOutOverrideAndProperty(
-                /* gatingCondition */ mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+                /* gatingCondition */ () -> mLetterboxConfiguration
+                        .isCameraCompatTreatmentEnabled(),
                 mIsOverrideCameraCompatDisableForceRotationEnabled,
                 mBooleanPropertyCameraCompatAllowForceRotation);
     }
 
-    /**
-     * Whether activity is eligible for camera compatibility free-form treatment.
-     *
-     * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
-     * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
-     * provides changes to the camera and display orientation signals to match those expected on a
-     * portrait device in that orientation (for example, on a standard phone).
-     *
-     * <p>The treatment is enabled when the following conditions are met:
-     * <ul>
-     * <li>Property gating the camera compatibility free-form treatment is enabled.
-     * <li>Activity isn't opted out by the device manufacturer with override or by the app
-     * developers with the component property.
-     * </ul>
-     */
-    boolean shouldApplyFreeformTreatmentForCameraCompat() {
-        return shouldEnableWithOptOutOverrideAndProperty(
-                /* gatingCondition */ ()-> Flags.cameraCompatForFreeform(),
-                mIsOverrideCameraCompatDisableFreeformWindowingTreatmentEnabled,
-                // TODO(b/328616176): add a manifest override for developers.
-                null);
-    }
-
     private boolean isCameraCompatTreatmentActive() {
         DisplayContent displayContent = mActivityRecord.mDisplayContent;
         if (displayContent == null) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index dd14642..33588a0 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -364,11 +364,6 @@
                     com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
             mMaxNumVisibleTasks = res.getInteger(
                     com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
-        } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
-            mMinNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
-            mMaxNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
         } else {
             mMinNumVisibleTasks = res.getInteger(
                     com.android.internal.R.integer.config_minNumVisibleRecentTasks);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 77319cc..acc6330 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -620,14 +620,6 @@
     public abstract void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener);
 
     /**
-     * Registers a listener to be notified to start the keyguard exit animation.
-     *
-     * @param listener The listener to register.
-     */
-    public abstract void registerKeyguardExitAnimationStartListener(
-            KeyguardExitAnimationStartListener listener);
-
-    /**
      * Reports that the password for the given user has changed.
      */
     public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 40b1b20..207b1bb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -538,7 +538,7 @@
     final boolean mHasPermanentDpad;
     final long mDrawLockTimeoutMillis;
     final boolean mAllowAnimationsInLowPowerMode;
-
+    final boolean mSupportsHighPerfTransitions;
     final boolean mAllowBootMessages;
 
     // Indicates whether the Assistant should show on top of the Dream (respectively, above
@@ -1181,6 +1181,8 @@
                 com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
         mMaxUiWidth = context.getResources().getInteger(
                 com.android.internal.R.integer.config_maxUiWidth);
+        mSupportsHighPerfTransitions = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_deviceSupportsHighPerfTransitions);
         mDisableTransitionAnimation = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_disableTransitionAnimation);
         mPerDisplayFocusEnabled = context.getResources().getBoolean(
@@ -1192,6 +1194,7 @@
         final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources()
                 .getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize)
                 && mFlags.mAllowsScreenSizeDecoupledFromStatusBarAndCutout;
+
         if (mFlags.mInsetsDecoupledConfiguration) {
             mDecorTypes = 0;
             mConfigTypes = 0;
@@ -8094,15 +8097,6 @@
         }
 
         @Override
-        public void registerKeyguardExitAnimationStartListener(
-                KeyguardExitAnimationStartListener listener) {
-            synchronized (mGlobalLock) {
-                getDefaultDisplayContentLocked().mAppTransition
-                        .registerKeygaurdExitAnimationStartListener(listener);
-            }
-        }
-
-        @Override
         public void reportPasswordChanged(int userId) {
             mKeyguardDisableHandler.updateKeyguardEnabled(userId);
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 46bac16..2b337ae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5700,7 +5700,8 @@
             // window becomes visible while the sync group is still active.
             return true;
         }
-        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mLastConfigReportedToClient && isDrawn()) {
+        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mLastConfigReportedToClient && isDrawn()
+                && mPrepareSyncSeqId <= 0) {
             // Complete the sync state immediately for a drawn window that doesn't need to redraw.
             onSyncFinishedDrawing();
         }
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index b38a2f9..d0df2b2 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -168,6 +168,10 @@
                 <xs:element type="nonNegativeDecimal" name="screenBrightnessCapForWearBedtimeMode">
                     <xs:annotation name="final"/>
                 </xs:element>
+                <!-- Timeout after which we reduce the refresh rate if the screen has been idle, in order to save power. -->
+                <xs:element type="idleScreenRefreshRateTimeout" name="idleScreenRefreshRateTimeout" minOccurs="0">
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
@@ -772,6 +776,30 @@
         </xs:sequence>
     </xs:complexType>
 
+    <xs:complexType name="idleScreenRefreshRateTimeout">
+        <xs:element name="luxThresholds" type="idleScreenRefreshRateTimeoutLuxThresholds" minOccurs="0">
+            <xs:annotation name="final"/>
+        </xs:element>
+    </xs:complexType>
+
+    <!-- Lux based timeout after which we reduce the refresh rate if the screen has been idle, in order to save power. -->
+    <xs:complexType name="idleScreenRefreshRateTimeoutLuxThresholds">
+        <xs:sequence>
+            <xs:element name="point" type="idleScreenRefreshRateTimeoutLuxThresholdPoint" maxOccurs="unbounded" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <!-- Represents a tuple of lux and timeout(in ms), which represents the timeout value for the lux in
+    the [luxValue, nextLuxValue (INF if missing))  -->
+    <xs:complexType name="idleScreenRefreshRateTimeoutLuxThresholdPoint">
+        <xs:element name="lux" type="xs:nonNegativeInteger">
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element name="timeout" type="xs:nonNegativeInteger">
+            <xs:annotation name="final"/>
+        </xs:element>
+    </xs:complexType>
+
     <!-- Predefined type names as defined by
     AutomaticBrightnessController.AutomaticBrightnessMode -->
     <xs:simpleType  name="AutoBrightnessModeName">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index b329db4..00dc908 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -111,6 +111,7 @@
     method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
     method @Nullable public final com.android.server.display.config.HdrBrightnessConfig getHdrBrightnessConfig();
     method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
+    method public final com.android.server.display.config.IdleScreenRefreshRateTimeout getIdleScreenRefreshRateTimeout();
     method public final com.android.server.display.config.SensorDetails getLightSensor();
     method public com.android.server.display.config.LuxThrottling getLuxThrottling();
     method @Nullable public final String getName();
@@ -146,6 +147,7 @@
     method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
     method public final void setHdrBrightnessConfig(@Nullable com.android.server.display.config.HdrBrightnessConfig);
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
+    method public final void setIdleScreenRefreshRateTimeout(com.android.server.display.config.IdleScreenRefreshRateTimeout);
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
     method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
     method public final void setName(@Nullable String);
@@ -222,6 +224,25 @@
     method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
   }
 
+  public class IdleScreenRefreshRateTimeout {
+    ctor public IdleScreenRefreshRateTimeout();
+    method public final com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds getLuxThresholds();
+    method public final void setLuxThresholds(com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds);
+  }
+
+  public class IdleScreenRefreshRateTimeoutLuxThresholdPoint {
+    ctor public IdleScreenRefreshRateTimeoutLuxThresholdPoint();
+    method public final java.math.BigInteger getLux();
+    method public final java.math.BigInteger getTimeout();
+    method public final void setLux(java.math.BigInteger);
+    method public final void setTimeout(java.math.BigInteger);
+  }
+
+  public class IdleScreenRefreshRateTimeoutLuxThresholds {
+    ctor public IdleScreenRefreshRateTimeoutLuxThresholds();
+    method public java.util.List<com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint> getPoint();
+  }
+
   public class IntegerArray {
     ctor public IntegerArray();
     method public java.util.List<java.math.BigInteger> getItem();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 38ab765..cb87f7e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -79,7 +79,6 @@
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
@@ -93,6 +92,7 @@
 import static android.Manifest.permission.MASTER_CLEAR;
 import static android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE;
 import static android.Manifest.permission.QUERY_ADMIN_POLICY;
+import static android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE;
 import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
 import static android.Manifest.permission.SET_TIME;
 import static android.Manifest.permission.SET_TIME_ZONE;
@@ -18460,7 +18460,7 @@
             return;
         }
         new CalculateHasIncompatibleAccountsTask().executeOnExecutor(
-                calculateHasIncompatibleAccountsExecutor, null);
+                calculateHasIncompatibleAccountsExecutor);
     }
 
     @Nullable
@@ -22128,12 +22128,12 @@
     }
 
     @Override
-    public boolean isTheftDetectionTriggered(String callerPackageName) {
+    public boolean isDevicePotentiallyStolen(String callerPackageName) {
         final CallerIdentity caller = getCallerIdentity(callerPackageName);
         if (!android.app.admin.flags.Flags.deviceTheftImplEnabled()) {
             return false;
         }
-        enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(),
+        enforcePermission(QUERY_DEVICE_STOLEN_STATE, caller.getPackageName(),
                 caller.getUserId());
 
         return mInjector.binderWithCleanCallingIdentity(() ->
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c6189ed..3b2a3dd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -408,6 +408,8 @@
             "com.android.server.searchui.SearchUiManagerService";
     private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
             "com.android.server.smartspace.SmartspaceManagerService";
+    private static final String CONTEXTUAL_SEARCH_MANAGER_SERVICE_CLASS =
+            "com.android.server.contextualsearch.ContextualSearchManagerService";
     private static final String DEVICE_IDLE_CONTROLLER_CLASS =
             "com.android.server.DeviceIdleController";
     private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -2016,6 +2018,16 @@
                 Slog.d(TAG, "SmartspaceManagerService not defined by OEM or disabled by flag");
             }
 
+            // Contextual search manager service
+            if (deviceHasConfigString(context,
+                    R.string.config_defaultContextualSearchPackageName)) {
+                t.traceBegin("StartContextualSearchService");
+                mSystemServiceManager.startService(CONTEXTUAL_SEARCH_MANAGER_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG, "ContextualSearchManagerService not defined or disabled by flag");
+            }
+
             t.traceBegin("InitConnectivityModuleConnector");
             try {
                 ConnectivityModuleConnector.getInstance().init(context);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 2867041..35b69f8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -54,6 +54,7 @@
 
 import com.android.internal.R;
 import com.android.server.display.config.HdrBrightnessData;
+import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
 import com.android.server.display.config.ThermalStatus;
 import com.android.server.display.feature.DisplayManagerFlags;
 
@@ -108,6 +109,7 @@
         when(mContext.getResources()).thenReturn(mResources);
         when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
         when(mFlags.isSensorBasedBrightnessThrottlingEnabled()).thenReturn(true);
+        when(mFlags.isIdleScreenRefreshRateTimeoutEnabled()).thenReturn(true);
         mockDeviceConfigs();
     }
 
@@ -146,6 +148,8 @@
         assertNull(mDisplayDeviceConfig.getProximitySensor().type);
         assertNull(mDisplayDeviceConfig.getProximitySensor().name);
         assertEquals(TEMPERATURE_TYPE_SKIN, mDisplayDeviceConfig.getTempSensor().type);
+        assertEquals(List.of(), mDisplayDeviceConfig
+                .getIdleScreenRefreshRateTimeoutLuxThresholdPoint());
         assertNull(mDisplayDeviceConfig.getTempSensor().name);
         assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
     }
@@ -226,6 +230,19 @@
         assertNotNull(mDisplayDeviceConfig.getHostUsiVersion());
         assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMajorVersion(), 2);
         assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMinorVersion(), 0);
+
+        List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+                idleScreenRefreshRateTimeoutLuxThresholdPoints =
+                mDisplayDeviceConfig.getIdleScreenRefreshRateTimeoutLuxThresholdPoint();
+        assertEquals(2, idleScreenRefreshRateTimeoutLuxThresholdPoints.size());
+        assertEquals(6, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(0).getLux()
+                .intValue());
+        assertEquals(1000, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(0)
+                .getTimeout().intValue());
+        assertEquals(10, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(1)
+                .getLux().intValue());
+        assertEquals(800, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(1)
+                .getTimeout().intValue());
     }
 
     @Test
@@ -734,6 +751,8 @@
 
         assertEquals(brightnessIntToFloat(35),
                 mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
+        assertEquals(List.of(), mDisplayDeviceConfig
+                .getIdleScreenRefreshRateTimeoutLuxThresholdPoint());
     }
 
     @Test
@@ -1587,6 +1606,18 @@
                 +   "<screenBrightnessCapForWearBedtimeMode>"
                 +       "0.1"
                 +   "</screenBrightnessCapForWearBedtimeMode>"
+                +   "<idleScreenRefreshRateTimeout>"
+                +       "<luxThresholds>"
+                +           "<point>"
+                +               "<lux>6</lux>"
+                +               "<timeout>1000</timeout>"
+                +           "</point>"
+                +           "<point>"
+                +               "<lux>10</lux>"
+                +               "<timeout>800</timeout>"
+                +           "</point>"
+                +       "</luxThresholds>"
+                +   "</idleScreenRefreshRateTimeout>"
                 + "</displayConfiguration>\n";
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 48fc407..869cec8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -78,6 +78,7 @@
 import android.graphics.Rect;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.Curve;
@@ -721,7 +722,9 @@
         IDisplayManagerCallback displayChangesCallback = registerDisplayChangeCallback(
                 displayManager);
 
-        listener.onStateChanged(123);
+        listener.onDeviceStateChanged(new DeviceState(
+                new DeviceState.Configuration.Builder(123 /* identifier */,
+                        "TEST" /* name */).build()));
         waitForIdleHandler(handler);
 
         InOrder inOrder = inOrder(mMockWindowManagerInternal, displayChangesCallback);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 2939192..d0c7077 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -650,7 +650,6 @@
     public void testDeviceShouldBePutToSleep() {
         assertTrue(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
                 DEVICE_STATE_OPEN,
-                /* isOverrideActive= */false,
                 /* isInteractive= */true,
                 /* isBootCompleted= */true));
     }
@@ -661,7 +660,6 @@
 
         assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
                 DEVICE_STATE_OPEN,
-                /* isOverrideActive= */false,
                 /* isInteractive= */true,
                 /* isBootCompleted= */true));
     }
@@ -670,21 +668,10 @@
     public void testDeviceShouldNotBePutToSleep() {
         assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_OPEN,
                 DEVICE_STATE_CLOSED,
-                /* isOverrideActive= */false,
                 /* isInteractive= */true,
                 /* isBootCompleted= */true));
         assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
                 INVALID_DEVICE_STATE_IDENTIFIER,
-                /* isOverrideActive= */false,
-                /* isInteractive= */true,
-                /* isBootCompleted= */true));
-    }
-
-    @Test
-    public void testDeviceShouldNotBePutToSleepDifferentBaseState() {
-        assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
-                DEVICE_STATE_OPEN,
-                /* isOverrideActive= */true,
                 /* isInteractive= */true,
                 /* isBootCompleted= */true));
     }
@@ -750,7 +737,7 @@
         // We can only have one default display
         assertEquals(DEFAULT_DISPLAY, id(display1));
 
-        mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(0);
         advanceTime(1000);
         // The new state is not applied until the boot is completed
         assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
@@ -771,7 +758,7 @@
         assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device2)
                 .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
 
-        mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(1);
         advanceTime(1000);
         assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
         assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
@@ -784,7 +771,7 @@
                 mLogicalDisplayMapper.getDisplayLocked(device2)
                         .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
 
-        mLogicalDisplayMapper.setDeviceStateLocked(2, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(2);
         advanceTime(1000);
         assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
         assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
@@ -861,7 +848,7 @@
         // 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state
         // 4) Dispatch handler events.
         mLogicalDisplayMapper.onBootCompleted();
-        mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(0);
         mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
         advanceTime(1000);
         final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
@@ -891,7 +878,7 @@
                 /* includeDisabled= */ false));
 
         // Now do it again to go back to state 1
-        mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(1);
         mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
         advanceTime(1000);
         final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
@@ -945,7 +932,7 @@
         // We can only have one default display
         assertEquals(DEFAULT_DISPLAY, id(display1));
 
-        mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(0);
         advanceTime(1000);
         mLogicalDisplayMapper.onBootCompleted();
         advanceTime(1000);
@@ -964,11 +951,11 @@
     /////////////////
 
     private void finishBootAndFoldDevice() {
-        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_OPEN, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_OPEN);
         advanceTime(1000);
         mLogicalDisplayMapper.onBootCompleted();
         advanceTime(1000);
-        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED, false);
+        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED);
         advanceTime(1000);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
index 638924e..b182cce 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
@@ -95,9 +95,9 @@
     ) {
         ALL_ENABLED(true, true, CombinedVote(
                 listOf(DisableRefreshRateSwitchingVote(true),
-                        SupportedModesVote(
-                                listOf(SupportedModesVote.SupportedMode(60f, 60f),
-                                        SupportedModesVote.SupportedMode(120f, 120f)))))),
+                        SupportedRefreshRatesVote(
+                                listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f),
+                                        SupportedRefreshRatesVote.RefreshRates(120f, 120f)))))),
         VRR_NOT_SUPPORTED(false, true, DisableRefreshRateSwitchingVote(true)),
         VSYNC_VOTE_DISABLED(true, false, DisableRefreshRateSwitchingVote(true))
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/ProximitySensorObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/ProximitySensorObserverTest.java
new file mode 100644
index 0000000..e93e5bc
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/ProximitySensorObserverTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.display.mode;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.sensors.SensorManagerInternal;
+
+import junitparams.JUnitParamsRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class ProximitySensorObserverTest {
+
+    private static final float FLOAT_TOLERANCE = 0.01f;
+    private static final int DISPLAY_ID = 1;
+    private static final SurfaceControl.RefreshRateRange REFRESH_RATE_RANGE =
+            new SurfaceControl.RefreshRateRange(60, 90);
+
+    private final VotesStorage mStorage = new VotesStorage(() -> { }, null);
+    private final FakesInjector mInjector = new FakesInjector();
+    private ProximitySensorObserver mSensorObserver;
+
+    @Mock
+    DisplayManagerInternal mMockDisplayManagerInternal;
+    @Mock
+    SensorManagerInternal mMockSensorManagerInternal;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockDisplayManagerInternal.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID),
+                any(), any())).thenReturn(REFRESH_RATE_RANGE);
+        mSensorObserver = new ProximitySensorObserver(mStorage, mInjector);
+        mSensorObserver.observe();
+    }
+
+    @Test
+    public void testAddsProximityVoteIfSensorManagerProximityActive() {
+        mSensorObserver.onProximityActive(true);
+
+        SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
+        assertThat(displayVotes.size()).isEqualTo(1);
+        Vote vote = displayVotes.get(Vote.PRIORITY_PROXIMITY);
+        assertThat(vote).isNotNull();
+        assertThat(vote).isInstanceOf(CombinedVote.class);
+        CombinedVote combinedVote = (CombinedVote) vote;
+        RefreshRateVote.PhysicalVote physicalVote =
+                (RefreshRateVote.PhysicalVote) combinedVote.mVotes.get(0);
+        assertThat(physicalVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
+        assertThat(physicalVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+    }
+
+    @Test
+    public void testDoesNotAddProximityVoteIfSensorManagerProximityNotActive() {
+        mSensorObserver.onProximityActive(false);
+
+        SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
+        assertThat(displayVotes.size()).isEqualTo(0);
+    }
+
+    @Test
+    public void testDoesNotAddProximityVoteIfDoze() {
+        mInjector.mDozeState = true;
+        mSensorObserver.onDisplayChanged(DISPLAY_ID);
+        mSensorObserver.onProximityActive(true);
+
+        SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
+        assertThat(displayVotes.size()).isEqualTo(0);
+    }
+
+    private class FakesInjector extends DisplayModeDirectorTest.FakesInjector {
+
+        private boolean mDozeState = false;
+
+        @Override
+        public Display[] getDisplays() {
+            return new Display[] { createDisplay(DISPLAY_ID) };
+        }
+
+        @Override
+        public DisplayManagerInternal getDisplayManagerInternal() {
+            return mMockDisplayManagerInternal;
+        }
+
+        @Override
+        public SensorManagerInternal getSensorManagerInternal() {
+            return mMockSensorManagerInternal;
+        }
+
+        @Override
+        public boolean isDozeState(Display d) {
+            return mDozeState;
+        }
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index ebb4f18..230317b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -85,9 +85,9 @@
             internal val expectedVote: Vote?
     ) {
         ALL_ENABLED(true, true, true,
-                SupportedModesVote(listOf(
-                        SupportedModesVote.SupportedMode(60f, 240f),
-                        SupportedModesVote.SupportedMode(60f, 60f)
+                SupportedRefreshRatesVote(listOf(
+                        SupportedRefreshRatesVote.RefreshRates(60f, 240f),
+                        SupportedRefreshRatesVote.RefreshRates(60f, 60f)
                 ))),
         LOW_POWER_OFF(true, true, false, null),
         DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
index 04e6265..6ce49b8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -27,12 +27,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SupportedModesVoteTest {
-    private val supportedModes = listOf(
-            SupportedModesVote.SupportedMode(60f, 90f ),
-            SupportedModesVote.SupportedMode(120f, 240f )
-    )
+    private val supportedModes = listOf(1, 2, 4)
 
-    private val otherMode = SupportedModesVote.SupportedMode(120f, 120f )
+    private val otherMode = 5
 
     private lateinit var supportedModesVote: SupportedModesVote
 
@@ -42,31 +39,31 @@
     }
 
     @Test
-    fun `adds supported modes if supportedModes in summary is null`() {
+    fun `adds supported mode ids if supportedModeIds in summary is null`() {
         val summary = createVotesSummary()
 
         supportedModesVote.updateSummary(summary)
 
-        assertThat(summary.supportedModes).containsExactlyElementsIn(supportedModes)
+        assertThat(summary.supportedModeIds).containsExactlyElementsIn(supportedModes)
     }
 
     @Test
-    fun `does not add supported modes if summary has empty list of modes`() {
+    fun `does not add supported mode ids if summary has empty list of modeIds`() {
         val summary = createVotesSummary()
-        summary.supportedModes = ArrayList()
+        summary.supportedModeIds = ArrayList()
 
         supportedModesVote.updateSummary(summary)
 
-        assertThat(summary.supportedModes).isEmpty()
+        assertThat(summary.supportedModeIds).isEmpty()
     }
 
     @Test
     fun `filters out modes that does not match vote`() {
         val summary = createVotesSummary()
-        summary.supportedModes = ArrayList(listOf(otherMode, supportedModes[0]))
+        summary.supportedModeIds = ArrayList(listOf(otherMode, supportedModes[0]))
 
         supportedModesVote.updateSummary(summary)
 
-        assertThat(summary.supportedModes).containsExactly(supportedModes[0])
+        assertThat(summary.supportedModeIds).containsExactly(supportedModes[0])
     }
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
new file mode 100644
index 0000000..d0c112b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SupportedRefreshRatesVoteTest {
+    private val refreshRates = listOf(
+            SupportedRefreshRatesVote.RefreshRates(60f, 90f),
+            SupportedRefreshRatesVote.RefreshRates(120f, 240f)
+    )
+
+    private val otherMode = SupportedRefreshRatesVote.RefreshRates(120f, 120f)
+
+    private lateinit var supportedRefreshRatesVote: SupportedRefreshRatesVote
+
+    @Before
+    fun setUp() {
+        supportedRefreshRatesVote = SupportedRefreshRatesVote(refreshRates)
+    }
+
+    @Test
+    fun `adds supported refresh rates if supportedModes in summary is null`() {
+        val summary = createVotesSummary()
+
+        supportedRefreshRatesVote.updateSummary(summary)
+
+        assertThat(summary.supportedRefreshRates).containsExactlyElementsIn(refreshRates)
+    }
+
+    @Test
+    fun `does not add supported refresh rates if summary has empty list of refresh rates`() {
+        val summary = createVotesSummary()
+        summary.supportedRefreshRates = ArrayList()
+
+        supportedRefreshRatesVote.updateSummary(summary)
+
+        assertThat(summary.supportedRefreshRates).isEmpty()
+    }
+
+    @Test
+    fun `filters out supported refresh rates that does not match vote`() {
+        val summary = createVotesSummary()
+        summary.supportedRefreshRates = ArrayList(listOf(otherMode, refreshRates[0]))
+
+        supportedRefreshRatesVote.updateSummary(summary)
+
+        assertThat(summary.supportedRefreshRates).containsExactly(refreshRates[0])
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
new file mode 100644
index 0000000..c49205b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import android.os.IBinder
+import android.os.RemoteException
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+private const val DISPLAY_ID = 1
+private const val DISPLAY_ID_OTHER = 2
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class SystemRequestObserverTest {
+
+
+    @get:Rule
+    val mockitoRule = MockitoJUnit.rule()
+
+    private val mockToken = mock<IBinder>()
+    private val mockOtherToken = mock<IBinder>()
+
+    private val storage = VotesStorage({}, null)
+
+    @Test
+    fun `requestDisplayModes adds vote to storage`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(1)
+        val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+        assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+        val supportedModesVote = vote as SupportedModesVote
+        assertThat(supportedModesVote.mModeIds.size).isEqualTo(requestedModes.size)
+        for (mode in requestedModes) {
+            assertThat(supportedModesVote.mModeIds).contains(mode)
+        }
+    }
+
+    @Test
+    fun `requestDisplayModes overrides votes in storage`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, intArrayOf(1, 2, 3))
+
+        val overrideModes = intArrayOf(10, 20, 30)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, overrideModes)
+
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(1)
+        val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+        assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+        val supportedModesVote = vote as SupportedModesVote
+        assertThat(supportedModesVote.mModeIds.size).isEqualTo(overrideModes.size)
+        for (mode in overrideModes) {
+            assertThat(supportedModesVote.mModeIds).contains(mode)
+        }
+    }
+
+    @Test
+    fun `requestDisplayModes removes vote to storage`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(0)
+    }
+
+    @Test
+    fun `requestDisplayModes calls linkToDeath to token`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+        verify(mockToken).linkToDeath(any(), eq(0))
+    }
+
+    @Test
+    fun `does not add votes to storage if binder died when requestDisplayModes called`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        doThrow(RemoteException()).whenever(mockOtherToken).linkToDeath(any(), eq(0))
+        systemRequestObserver.requestDisplayModes(mockOtherToken, DISPLAY_ID, requestedModes)
+
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(0)
+    }
+
+    @Test
+    fun `removes all votes from storage when binder dies`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+        val deathRecipientCaptor = argumentCaptor<IBinder.DeathRecipient>()
+        verify(mockToken).linkToDeath(deathRecipientCaptor.capture(), eq(0))
+
+        deathRecipientCaptor.lastValue.binderDied(mockToken)
+
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(0)
+    }
+
+    @Test
+    fun `calls unlinkToDeath on token when no votes remaining`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+        verify(mockToken).unlinkToDeath(any(), eq(0))
+    }
+
+    @Test
+    fun `does not call unlinkToDeath on token when votes for other display in storage`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID_OTHER, requestedModes)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+        verify(mockToken, never()).unlinkToDeath(any(), eq(0))
+    }
+
+    @Test
+    fun `requestDisplayModes subset modes from different tokens`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+        val requestedOtherModes = intArrayOf(2, 3, 4)
+        systemRequestObserver.requestDisplayModes(mockOtherToken, DISPLAY_ID, requestedOtherModes)
+
+        verify(mockToken).linkToDeath(any(), eq(0))
+        verify(mockOtherToken).linkToDeath(any(), eq(0))
+        verify(mockToken, never()).unlinkToDeath(any(), eq(0))
+        verify(mockOtherToken, never()).unlinkToDeath(any(), eq(0))
+
+        val expectedModes = intArrayOf(2, 3)
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(1)
+        val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+        assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+        val supportedModesVote = vote as SupportedModesVote
+        assertThat(supportedModesVote.mModeIds.size).isEqualTo(expectedModes.size)
+        for (mode in expectedModes) {
+            assertThat(supportedModesVote.mModeIds).contains(mode)
+        }
+    }
+
+    @Test
+    fun `recalculates vote if one binder dies`() {
+        val systemRequestObserver = SystemRequestObserver(storage)
+        val requestedModes = intArrayOf(1, 2, 3)
+        systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+        val requestedOtherModes = intArrayOf(2, 3, 4)
+        systemRequestObserver.requestDisplayModes(mockOtherToken, DISPLAY_ID, requestedOtherModes)
+
+        val deathRecipientCaptor = argumentCaptor<IBinder.DeathRecipient>()
+        verify(mockOtherToken).linkToDeath(deathRecipientCaptor.capture(), eq(0))
+        deathRecipientCaptor.lastValue.binderDied(mockOtherToken)
+
+        val votes = storage.getVotes(DISPLAY_ID)
+        assertThat(votes.size()).isEqualTo(1)
+        val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+        assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+        val supportedModesVote = vote as SupportedModesVote
+        assertThat(supportedModesVote.mModeIds.size).isEqualTo(requestedModes.size)
+        for (mode in requestedModes) {
+            assertThat(supportedModesVote.mModeIds).contains(mode)
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
index 910e03c..6b90bde 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
@@ -18,10 +18,10 @@
 
 internal fun createVotesSummary(
         isDisplayResolutionRangeVotingEnabled: Boolean = true,
-        vsyncProximityVoteEnabled: Boolean = true,
+        supportedModesVoteEnabled: Boolean = true,
         loggingEnabled: Boolean = true,
         supportsFrameRateOverride: Boolean = true
 ): VoteSummary {
-    return VoteSummary(isDisplayResolutionRangeVotingEnabled, vsyncProximityVoteEnabled,
+    return VoteSummary(isDisplayResolutionRangeVotingEnabled, supportedModesVoteEnabled,
             loggingEnabled, supportsFrameRateOverride)
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index d6c8469..04b35f1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -28,29 +28,29 @@
 @RunWith(TestParameterInjector::class)
 class VoteSummaryTest {
 
-    enum class SupportedModesVoteTestCase(
-            val vsyncProximityVoteEnabled: Boolean,
-            internal val summarySupportedModes: List<SupportedModesVote.SupportedMode>?,
+    enum class SupportedRefreshRatesTestCase(
+            val supportedModesVoteEnabled: Boolean,
+            internal val summaryRefreshRates: List<SupportedRefreshRatesVote.RefreshRates>?,
             val modesToFilter: Array<Display.Mode>,
             val expectedModeIds: List<Int>
     ) {
         HAS_NO_MATCHING_VOTE(true,
-                listOf(SupportedModesVote.SupportedMode(60f, 60f)),
+                listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f)),
                 arrayOf(createMode(1, 90f, 90f),
                         createMode(2, 90f, 60f),
                         createMode(3, 60f, 90f)),
                 listOf()
         ),
         HAS_SINGLE_MATCHING_VOTE(true,
-                listOf(SupportedModesVote.SupportedMode(60f, 90f)),
+                listOf(SupportedRefreshRatesVote.RefreshRates(60f, 90f)),
                 arrayOf(createMode(1, 90f, 90f),
                         createMode(2, 90f, 60f),
                         createMode(3, 60f, 90f)),
                 listOf(3)
         ),
         HAS_MULTIPLE_MATCHING_VOTES(true,
-                listOf(SupportedModesVote.SupportedMode(60f, 90f),
-                        SupportedModesVote.SupportedMode(90f, 90f)),
+                listOf(SupportedRefreshRatesVote.RefreshRates(60f, 90f),
+                        SupportedRefreshRatesVote.RefreshRates(90f, 90f)),
                 arrayOf(createMode(1, 90f, 90f),
                         createMode(2, 90f, 60f),
                         createMode(3, 60f, 90f)),
@@ -70,7 +70,69 @@
                         createMode(3, 60f, 90f)),
                 listOf(1, 2, 3)
         ),
-        HAS_VSYNC_PROXIMITY_DISABLED(false,
+        HAS_SUPPORTED_MODES_VOTE_DISABLED(false,
+                listOf(),
+                arrayOf(createMode(1, 90f, 90f),
+                        createMode(2, 90f, 60f),
+                        createMode(3, 60f, 90f)),
+                listOf(1, 2, 3)
+        ),
+    }
+
+    @Test
+    fun `filters modes for summary supportedRefreshRates`(
+            @TestParameter testCase: SupportedRefreshRatesTestCase
+    ) {
+        val summary = createSummary(testCase.supportedModesVoteEnabled)
+        summary.supportedRefreshRates = testCase.summaryRefreshRates
+
+        val result = summary.filterModes(testCase.modesToFilter)
+
+        assertThat(result.map { it.modeId }).containsExactlyElementsIn(testCase.expectedModeIds)
+    }
+
+    enum class SupportedModesTestCase(
+            val supportedModesVoteEnabled: Boolean,
+            internal val summarySupportedModes: List<Int>?,
+            val modesToFilter: Array<Display.Mode>,
+            val expectedModeIds: List<Int>
+    ) {
+        HAS_NO_MATCHING_VOTE(true,
+                listOf(4, 5),
+                arrayOf(createMode(1, 90f, 90f),
+                        createMode(2, 90f, 60f),
+                        createMode(3, 60f, 90f)),
+                listOf()
+        ),
+        HAS_SINGLE_MATCHING_VOTE(true,
+                listOf(3),
+                arrayOf(createMode(1, 90f, 90f),
+                        createMode(2, 90f, 60f),
+                        createMode(3, 60f, 90f)),
+                listOf(3)
+        ),
+        HAS_MULTIPLE_MATCHING_VOTES(true,
+                listOf(1, 3),
+                arrayOf(createMode(1, 90f, 90f),
+                        createMode(2, 90f, 60f),
+                        createMode(3, 60f, 90f)),
+                listOf(1, 3)
+        ),
+        HAS_NO_SUPPORTED_MODES(true,
+                listOf(),
+                arrayOf(createMode(1, 90f, 90f),
+                        createMode(2, 90f, 60f),
+                        createMode(3, 60f, 90f)),
+                listOf()
+        ),
+        HAS_NULL_SUPPORTED_MODES(true,
+                null,
+                arrayOf(createMode(1, 90f, 90f),
+                        createMode(2, 90f, 60f),
+                        createMode(3, 60f, 90f)),
+                listOf(1, 2, 3)
+        ),
+        HAS_SUPPORTED_MODES_VOTE_DISABLED(false,
                 listOf(),
                 arrayOf(createMode(1, 90f, 90f),
                         createMode(2, 90f, 60f),
@@ -81,10 +143,10 @@
 
     @Test
     fun `filters modes for summary supportedModes`(
-            @TestParameter testCase: SupportedModesVoteTestCase
+            @TestParameter testCase: SupportedModesTestCase
     ) {
-        val summary = createSummary(testCase.vsyncProximityVoteEnabled)
-        summary.supportedModes = testCase.summarySupportedModes
+        val summary = createSummary(testCase.supportedModesVoteEnabled)
+        summary.supportedModeIds = testCase.summarySupportedModes
 
         val result = summary.filterModes(testCase.modesToFilter)
 
@@ -96,8 +158,8 @@
             FloatArray(0), IntArray(0))
 }
 
-private fun createSummary(vsyncVoteEnabled: Boolean): VoteSummary {
-    val summary = createVotesSummary(vsyncProximityVoteEnabled = vsyncVoteEnabled)
+private fun createSummary(supportedModesVoteEnabled: Boolean): VoteSummary {
+    val summary = createVotesSummary(supportedModesVoteEnabled = supportedModesVoteEnabled)
     summary.width = 600
     summary.height = 800
     summary.maxPhysicalRefreshRate = Float.POSITIVE_INFINITY
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 1f6f1a4..a248d6de 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -153,4 +154,51 @@
         assertThat(mVotesStorage.getVotes(DISPLAY_ID).size()).isEqualTo(0);
         verify(mVotesListener, never()).onChanged();
     }
+
+
+    @Test
+    public void removesAllVotesForPriority() {
+        // GIVEN vote storage with votes
+        mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+        mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
+        mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY, VOTE);
+        mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY_OTHER, VOTE_OTHER);
+        // WHEN removeAllVotesForPriority is called
+        mVotesStorage.removeAllVotesForPriority(PRIORITY);
+        // THEN votes with priority are removed from the storage
+        SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
+        assertThat(votes.size()).isEqualTo(1);
+        assertThat(votes.get(PRIORITY)).isNull();
+        votes = mVotesStorage.getVotes(DISPLAY_ID_OTHER);
+        assertThat(votes.size()).isEqualTo(1);
+        assertThat(votes.get(PRIORITY)).isNull();
+    }
+
+    @Test
+    public void removesAllVotesForPriority_notifiesListenerOnce() {
+        // GIVEN vote storage with votes
+        mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+        mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
+        mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY, VOTE);
+        mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY_OTHER, VOTE_OTHER);
+        clearInvocations(mVotesListener);
+        // WHEN removeAllVotesForPriority is called
+        mVotesStorage.removeAllVotesForPriority(PRIORITY);
+        // THEN listener notified once
+        verify(mVotesListener).onChanged();
+    }
+
+    @Test
+    public void removesAllVotesForPriority_noChangesIfNothingRemoved() {
+        // GIVEN vote storage with votes
+        mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+        clearInvocations(mVotesListener);
+        // WHEN removeAllVotesForPriority is called for missing priority
+        mVotesStorage.removeAllVotesForPriority(PRIORITY_OTHER);
+        // THEN no changes to votes storage
+        SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
+        assertThat(votes.size()).isEqualTo(1);
+        assertThat(votes.get(PRIORITY)).isEqualTo(VOTE);
+        verify(mVotesListener, never()).onChanged();
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index fcb3caa..dc5b00a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -28,10 +28,15 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static android.os.PowerExemptionManager.REASON_DENIED;
 import static android.util.DebugUtils.valueToString;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
 import static com.android.server.am.ActivityManagerService.Injector;
 import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
@@ -52,28 +57,40 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
 
 import android.Manifest;
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
 import android.app.BroadcastOptions;
+import android.app.ForegroundServiceDelegationOptions;
 import android.app.IApplicationThread;
 import android.app.IUidObserver;
+import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.SyncNotedAppOp;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -84,6 +101,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.permission.IPermissionManager;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -105,18 +123,20 @@
 import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
 import com.android.server.am.UidObserverController.ChangeRecord;
 import com.android.server.appop.AppOpsService;
+import com.android.server.notification.NotificationManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.verification.VerificationMode;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -127,13 +147,15 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 
 /**
  * Test class for {@link ActivityManagerService}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:ActivityManagerServiceTest
+ *  atest FrameworksMockingServicesTests:ActivityManagerServiceTest
  */
 @Presubmit
 @SmallTest
@@ -148,6 +170,9 @@
 
     private static final String TEST_EXTRA_KEY1 = "com.android.server.am.TEST_EXTRA_KEY1";
     private static final String TEST_EXTRA_VALUE1 = "com.android.server.am.TEST_EXTRA_VALUE1";
+
+    private static final String TEST_PACKAGE_NAME = "com.android.server.am.testpackage";
+
     private static final String PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS =
             "apply_sdk_sandbox_audit_restrictions";
     private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
@@ -155,6 +180,7 @@
     private static final String APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS = ":isSdkSandboxAudit";
     private static final String APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = ":isSdkSandboxNext";
     private static final int TEST_UID = 11111;
+    private static final int TEST_PID = 22222;
     private static final int USER_ID = 666;
 
     private static final long TEST_PROC_STATE_SEQ1 = 555;
@@ -169,22 +195,8 @@
         UidRecord.CHANGE_CAPABILITY,
     };
 
-    private static PackageManagerInternal sPackageManagerInternal;
     private static ProcessList.ProcessListSettingsListener sProcessListSettingsListener;
 
-    @BeforeClass
-    public static void setUpOnce() {
-        sPackageManagerInternal = mock(PackageManagerInternal.class);
-        doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
-                .getSystemUiServiceComponent();
-        LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
-    }
-
-    @AfterClass
-    public static void tearDownOnce() {
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-    }
-
     @Rule
     public final ApplicationExitInfoTest.ServiceThreadRule
             mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
@@ -196,15 +208,41 @@
 
     @Mock private AppOpsService mAppOpsService;
     @Mock private UserController mUserController;
+    @Mock private IPackageManager mPackageManager;
+    @Mock private IPermissionManager mPermissionManager;
+    @Mock private BatteryStatsService mBatteryStatsService;
+    @Mock private PackageManagerInternal mPackageManagerInternal;
+    @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+    @Mock private NotificationManagerInternal mNotificationManagerInternal;
+
 
     private TestInjector mInjector;
     private ActivityManagerService mAms;
+    private ActiveServices mActiveServices;
     private HandlerThread mHandlerThread;
     private TestHandler mHandler;
+    private MockitoSession mMockingSession;
+
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(AppGlobals.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
         MockitoAnnotations.initMocks(this);
 
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+        LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
+        LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal);
+
+        doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
+                .getSystemUiServiceComponent();
+
+        doReturn(mPackageManager).when(AppGlobals::getPackageManager);
+        doReturn(mPermissionManager).when(AppGlobals::getPermissionManager);
+        doReturn(new String[]{""}).when(mPackageManager).getPackagesForUid(eq(Process.myUid()));
+
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new TestHandler(mHandlerThread.getLooper());
@@ -220,6 +258,7 @@
         mAms.mConstants.mNetworkAccessTimeoutMs = 2000;
         mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
         mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
+        mAms.mAtmInternal = mActivityTaskManagerInternal;
         mHandler.setRunnablesToIgnore(
                 List.of(mAms.mUidObserverController.getDispatchRunnableForTest()));
 
@@ -250,6 +289,15 @@
         if (sProcessListSettingsListener != null) {
             sProcessListSettingsListener.unregisterObserver();
         }
+        clearInvocations(mNotificationManagerInternal);
+
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.removeServiceForTest(NotificationManagerInternal.class);
+
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
     }
 
     @SuppressWarnings("GuardedBy")
@@ -445,6 +493,7 @@
         }
     }
 
+    @SuppressWarnings("GuardedBy")
     private UidRecord addUidRecord(int uid) {
         final UidRecord uidRec = new UidRecord(uid, mAms);
         uidRec.procStateSeqWaitingForNetwork = 1;
@@ -453,10 +502,13 @@
 
         ApplicationInfo info = new ApplicationInfo();
         info.packageName = "";
+        info.uid = uid;
 
         final ProcessRecord appRec = new ProcessRecord(mAms, info, TAG, uid);
-        final ProcessStatsService tracker = new ProcessStatsService(mAms, mContext.getCacheDir());
-        appRec.makeActive(mock(IApplicationThread.class), tracker);
+        final ProcessStatsService tracker = mAms.mProcessStats;
+        final IApplicationThread appThread = mock(IApplicationThread.class);
+        doReturn(mock(IBinder.class)).when(appThread).asBinder();
+        appRec.makeActive(appThread, tracker);
         mAms.mProcessList.getLruProcessesLSP().add(appRec);
 
         return uidRec;
@@ -1209,6 +1261,108 @@
         mAms.mUidObserverController.getPendingUidChangesForTest().clear();
     }
 
+    @Test
+    public void testStartForegroundServiceDelegateWithNotification() throws Exception {
+        testStartForegroundServiceDelegate(true);
+    }
+
+    @Test
+    public void testStartForegroundServiceDelegateWithoutNotification() throws Exception {
+        testStartForegroundServiceDelegate(false);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void testStartForegroundServiceDelegate(boolean withNotification) throws Exception {
+        mockNoteOperation();
+
+        final int notificationId = 42;
+        final Notification notification = mock(Notification.class);
+
+        addUidRecord(TEST_UID);
+        final ProcessRecord app = mAms.mProcessList.getLruProcessesLSP().get(0);
+        app.mPid = TEST_PID;
+        app.info.packageName = TEST_PACKAGE_NAME;
+        app.info.processName = TEST_PACKAGE_NAME;
+
+        doReturn(app.info).when(mPackageManager).getApplicationInfo(
+                eq(app.info.packageName), anyLong(), anyInt());
+
+        doReturn(true).when(mActiveServices)
+                .canStartForegroundServiceLocked(anyInt(), anyInt(), anyString());
+        doReturn(REASON_DENIED).when(mActiveServices)
+                .shouldAllowFgsWhileInUsePermissionLocked(anyString(), anyInt(), anyInt(),
+                        any(ProcessRecord.class), any(BackgroundStartPrivileges.class));
+
+        doReturn(true).when(mNotificationManagerInternal).areNotificationsEnabledForPackage(
+                anyString(), anyInt());
+        doReturn(mock(Icon.class)).when(notification).getSmallIcon();
+        doReturn("").when(notification).getChannelId();
+        doReturn(mock(NotificationChannel.class)).when(mNotificationManagerInternal)
+                .getNotificationChannel(anyString(), anyInt(), anyString());
+
+        mAms.mAppProfiler.mCachedAppsWatermarkData.mCachedAppHighWatermark = Integer.MAX_VALUE;
+
+        final ForegroundServiceDelegationOptions.Builder optionsBuilder =
+                new ForegroundServiceDelegationOptions.Builder()
+                .setClientPid(app.mPid)
+                .setClientUid(app.uid)
+                .setClientPackageName(app.info.packageName)
+                .setClientAppThread(app.getThread())
+                .setSticky(false)
+                .setClientInstanceName(
+                        "SystemExemptedFgsDelegate_"
+                        + Process.myUid()
+                        + "_"
+                        + app.uid
+                        + "_"
+                        + app.info.packageName)
+                .setForegroundServiceTypes(ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED)
+                .setDelegationService(
+                        ForegroundServiceDelegationOptions.DELEGATION_SERVICE_SYSTEM_EXEMPTED);
+        if (withNotification) {
+            optionsBuilder.setClientNotification(notificationId, notification);
+        }
+        final ForegroundServiceDelegationOptions options = optionsBuilder.build();
+
+        final CountDownLatch[] latchHolder = new CountDownLatch[1];
+        final ServiceConnection conn = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                latchHolder[0].countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                latchHolder[0].countDown();
+            }
+        };
+
+        latchHolder[0] = new CountDownLatch(1);
+        mAms.mInternal.startForegroundServiceDelegate(options, conn);
+
+        assertThat(latchHolder[0].await(5, TimeUnit.SECONDS)).isTrue();
+        assertEquals(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+                app.mState.getCurProcState());
+        final long timeoutMs = 5000L;
+        final VerificationMode mode = withNotification
+                ? timeout(timeoutMs) : after(timeoutMs).atMost(0);
+        verify(mNotificationManagerInternal, mode)
+                .enqueueNotification(eq(app.info.packageName), eq(app.info.packageName),
+                        eq(app.info.uid), eq(app.mPid), eq(null),
+                        eq(notificationId), eq(notification), anyInt(), eq(true));
+
+        latchHolder[0] = new CountDownLatch(1);
+        mAms.mInternal.stopForegroundServiceDelegate(options);
+
+        assertThat(latchHolder[0].await(5, TimeUnit.SECONDS)).isTrue();
+        assertEquals(ActivityManager.PROCESS_STATE_CACHED_EMPTY,
+                app.mState.getCurProcState());
+        verify(mNotificationManagerInternal, mode)
+                .cancelNotification(eq(app.info.packageName), eq(app.info.packageName),
+                        eq(app.info.uid), eq(app.mPid), eq(null),
+                        eq(notificationId), anyInt());
+    }
+
     private static class TestHandler extends Handler {
         private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
         private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
@@ -1291,6 +1445,19 @@
             usersStartedOnSecondaryDisplays.add(new Pair<>(userId, displayId));
             return returnValueForstartUserOnSecondaryDisplay;
         }
+
+        @Override
+        public ActiveServices getActiveServices(ActivityManagerService service) {
+            if (mActiveServices == null) {
+                mActiveServices = spy(new ActiveServices(service));
+            }
+            return mActiveServices;
+        }
+
+        @Override
+        public BatteryStatsService getBatteryStatsService() {
+            return mBatteryStatsService;
+        }
     }
 
     // TODO: [b/302724778] Remove manual JNI load
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index aec896f..f86ff14 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -64,6 +64,7 @@
 import android.content.PermissionChecker;
 import android.content.res.Resources;
 import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.hardware.display.AmbientDisplayConfiguration;
@@ -154,6 +155,8 @@
 
     private static final float BRIGHTNESS_FACTOR = 0.7f;
     private static final boolean BATTERY_SAVER_ENABLED = true;
+    private static final DeviceState DEVICE_STATE_1 = new DeviceState(
+            new DeviceState.Configuration.Builder(1 /* identifier */, "" /* name */).build());
 
     @Mock private BatterySaverController mBatterySaverControllerMock;
     @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
@@ -2839,7 +2842,7 @@
 
         // Send a display state change event and advance the clock 10.
         final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
-        deviceStateCallback.onStateChanged(1);
+        deviceStateCallback.onDeviceStateChanged(DEVICE_STATE_1);
         final long timeToAdvance = 10;
         advanceTime(timeToAdvance);
 
@@ -2849,7 +2852,7 @@
         assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
 
         // Send the same state and ensure that does not trigger an update.
-        deviceStateCallback.onStateChanged(1);
+        deviceStateCallback.onDeviceStateChanged(DEVICE_STATE_1);
         advanceTime(timeToAdvance);
         final long newTime = timeToAdvance * 2;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index eb89503..b2ecea1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -813,7 +813,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_DISABLE_CONTINUOUS_SHORTCUT_ON_FORCE_STOP)
     public void testPackagesForceStopped_fromContinuousService_removesButtonTarget() {
         final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
         info_a.setComponentName(COMPONENT_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index f86cb7b..cda8b01 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -208,7 +208,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
     public void onServiceConnected_addsWindowTokens() {
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b1964e2..b269beb9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -51,7 +51,6 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
@@ -220,18 +219,6 @@
     }
 
     @Test
-    // addServiceLocked only calls addWindowTokensForAllDisplays when
-    // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on.
-    @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
-    public void addService_flagDisabled_addsWindowTokens() {
-        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
-
-        mUserState.addServiceLocked(mMockConnection);
-
-        verify(mMockConnection).addWindowTokensForAllDisplays();
-    }
-
-    @Test
     public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
         // When soft kb show mode is hidden in settings and is auto in state.
         putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 3ee5f61..95a1f5a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -39,7 +39,6 @@
 import android.content.pm.ServiceInfo;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
-import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.WindowManager;
@@ -209,7 +208,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
     public void registerUiAutomationService_callsAddWindowTokenUsingHandler() {
         register(0);
         // registerUiTestAutomationServiceLocked() should not directly call addWindowToken.
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 67b131f..1249707 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -206,7 +206,6 @@
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -2151,14 +2150,12 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testBackgroundChainEnabled() throws Exception {
         verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true);
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testBackgroundChainOnProcStateChange() throws Exception {
@@ -2188,7 +2185,6 @@
         assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testBackgroundChainOnAllowlistChange() throws Exception {
@@ -2227,7 +2223,6 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testBackgroundChainOnTempAllowlistChange() throws Exception {
@@ -2266,7 +2261,6 @@
                 && uidState.procState == procState && uidState.capability == capability;
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testUidObserverFiltersProcStateChanges() throws Exception {
@@ -2329,7 +2323,6 @@
         waitForUidEventHandlerIdle();
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testUidObserverFiltersStaleChanges() throws Exception {
@@ -2350,7 +2343,6 @@
         waitForUidEventHandlerIdle();
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testUidObserverFiltersCapabilityChanges() throws Exception {
@@ -2430,7 +2422,6 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
     }
 
-    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testObsoleteHandleUidChanged() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index e249cd7..55c48e0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -90,8 +90,6 @@
     public void testCanDeviceOwnerChange() {
         assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
         assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_WALLPAPER));
-        assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(
-                UserManager.DISALLOW_ADD_PRIVATE_PROFILE));
         assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_ADD_USER));
         assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_USER_SWITCH));
     }
@@ -110,10 +108,6 @@
                 UserManager.DISALLOW_USER_SWITCH,
                 true,
                 false));
-        assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
-                true,
-                false));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_ADD_USER,
                 true,
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index fee6582..dff4984 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -43,9 +43,12 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
 
+import static org.mockito.ArgumentMatchers.eq;
+
 import static java.util.Collections.unmodifiableMap;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.view.InputDevice;
@@ -57,6 +60,7 @@
 import com.android.internal.util.test.FakeSettingsProviderRule;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.rules.RuleChain;
 
@@ -70,9 +74,10 @@
     @Rule
     public RuleChain rules = RuleChain.outerRule(mSettingsProviderRule).around(mSetFlagsRule);
 
+    private Resources mResources;
     TestPhoneWindowManager mPhoneWindowManager;
     DispatchedKeyHandler mDispatchedKeyHandler = event -> false;
-    final Context mContext = spy(getInstrumentation().getTargetContext());
+    Context mContext;
 
     /** Modifier key to meta state */
     protected static final Map<Integer, Integer> MODIFIER;
@@ -90,6 +95,16 @@
         MODIFIER = unmodifiableMap(map);
     }
 
+    @Before
+    public void setup() {
+        mContext = spy(getInstrumentation().getTargetContext());
+        mResources = spy(mContext.getResources());
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(mSettingsProviderRule.mockContentResolver(mContext))
+                .when(mContext).getContentResolver();
+    }
+
+
     /** Same as {@link setUpPhoneWindowManager(boolean)}, without supporting settings update. */
     protected final void setUpPhoneWindowManager() {
         setUpPhoneWindowManager(/* supportSettingsUpdate= */ false);
@@ -101,12 +116,14 @@
      * <p>Subclasses must call this at the start of the test if they intend to interact with phone
      * window manager.
      *
-     * @param supportSettingsUpdate {@code true} if this test should read and listen to provider
-     *      settings values.
+     * @param supportSettingsUpdate {@code true} to have PWM respond to any Settings changes upon
+     *    instantiation. Although this is supposed to also allow a test to listen to any Settings
+     *    changes after instantiation, MockContentResolver in this class's setup stubs out
+     *    notifyChange(), which prevents SettingsObserver from getting notified of events. So
+     *    we're effectively always instantiating TestPhoneWindowManager with
+     *    supportSettingsUpdate=false.
      */
     protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
-        doReturn(mSettingsProviderRule.mockContentResolver(mContext))
-                .when(mContext).getContentResolver();
         mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
     }
 
@@ -187,6 +204,23 @@
         sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY);
     }
 
+    /**
+     * Since we use SettingsProviderRule to mock the ContentResolver in these
+     * tests, the settings observer registered by PhoneWindowManager will not
+     * be triggered automatically by the mock. Use this method to force the
+     * settings observer change after modifying any settings.
+     */
+    void triggerSettingsObserverChange() {
+        mPhoneWindowManager.getSettingsObserver().onChange(
+                // This boolean doesn't matter. This observer does the same thing regardless.
+                /*selfChange=*/true);
+    }
+
+    /** Override a resource's return value. */
+    void overrideResource(int resId, int expectedBehavior) {
+        doReturn(expectedBehavior).when(mResources).getInteger(eq(resId));
+    }
+
     private void interceptKey(KeyEvent keyEvent) {
         int actions = mPhoneWindowManager.interceptKeyBeforeQueueing(keyEvent);
         if ((actions & ACTION_PASS_TO_USER) != 0) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 77e7a0a..2e85025 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -291,6 +291,101 @@
         mPhoneWindowManager.assertSwitchToTask(referenceId);
     }
 
+    /**
+     * Ensure the stem rule is added even when button behaviors are set to nothing.
+     *
+     * This makes sure that if stem key behaviors are overridden to NOTHING, then we check the
+     * XML config as the source of truth upon reboot to see whether a device should have a stem
+     * key rule. This test walks us through a scenario where a device powers off during Wear's
+     * Touch Lock mode.
+     */
+    @Test
+    public void stemKeyRuleIsAddedEvenWhenBehaviorsRemoved() {
+        // deactivate stem button presses
+        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+                PhoneWindowManager.SHORT_PRESS_PRIMARY_NOTHING);
+        overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS,
+                PhoneWindowManager.DOUBLE_PRESS_PRIMARY_NOTHING);
+        overrideBehavior(STEM_PRIMARY_BUTTON_TRIPLE_PRESS,
+                PhoneWindowManager.TRIPLE_PRESS_PRIMARY_NOTHING);
+        overrideBehavior(STEM_PRIMARY_BUTTON_LONG_PRESS,
+                PhoneWindowManager.LONG_PRESS_PRIMARY_NOTHING);
+
+        // pretend like we have stem keys enabled in the xmls
+        overrideResource(
+                com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
+                SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+
+        // start the PhoneWindowManager, just like would happen with a reboot
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+        // Set the stem behavior back to something normal after boot
+        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+                SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+        // manually trigger the SettingsObserver's onChange() method because subclasses of
+        // ShortcutKeyTestBase cannot automatically pick up Settings changes.
+        triggerSettingsObserverChange();
+
+        // These calls are required to make the All Apps view show up
+        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+        mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+        mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+        sendKey(KEYCODE_STEM_PRIMARY);
+
+        // Because the rule was loaded and we changed the behavior back to non-zero, PWM should
+        // actually perform this action. It would not perform the action if the rule was missing.
+        mPhoneWindowManager.assertOpenAllAppView();
+    }
+
+    /**
+     * Ensure the stem rule is not added when stem behavior is not defined in the xml.
+     *
+     * This is the opposite of the test above.
+     */
+    @Test
+    public void stemKeyRuleIsNotAddedWhenXmlDoesntDefineIt() {
+        // deactivate stem button presses
+        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+                PhoneWindowManager.SHORT_PRESS_PRIMARY_NOTHING);
+        overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS,
+                PhoneWindowManager.DOUBLE_PRESS_PRIMARY_NOTHING);
+        overrideBehavior(STEM_PRIMARY_BUTTON_TRIPLE_PRESS,
+                PhoneWindowManager.TRIPLE_PRESS_PRIMARY_NOTHING);
+        overrideBehavior(STEM_PRIMARY_BUTTON_LONG_PRESS,
+                PhoneWindowManager.LONG_PRESS_PRIMARY_NOTHING);
+
+        // pretend like we do not have stem keys enabled in the xmls
+        overrideResource(
+                com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
+                PhoneWindowManager.SHORT_PRESS_PRIMARY_NOTHING);
+        overrideResource(
+                com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior,
+                PhoneWindowManager.LONG_PRESS_PRIMARY_NOTHING);
+
+        // start the PhoneWindowManager, just like would happen with a reboot
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+        // Set the stem behavior back to something normal after boot
+        // (Despite this fact, a stem press shouldn't have any behavior because there's no rule.)
+        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+                SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+        // manually trigger the SettingsObserver's onChange() method because subclasses of
+        // ShortcutKeyTestBase cannot automatically pick up Settings changes.
+        triggerSettingsObserverChange();
+
+        // These calls are required to make the All Apps view show up
+        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+        mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+        mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+        sendKey(KEYCODE_STEM_PRIMARY);
+
+        // Because the rule was not loaded, PWM should not actually perform this action, even
+        // though the Settings override is set to non-null.
+        mPhoneWindowManager.assertNotOpenAllAppView();
+    }
+
     private void overrideBehavior(String key, int expectedBehavior) {
         Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
     }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 0776c51..52df010 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -66,6 +66,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
 import android.hardware.SensorPrivacyManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
@@ -246,6 +247,13 @@
         }
     }
 
+    /**
+     * {@link TestPhoneWindowManager}'s constructor.
+     *
+     * @param context The {@Context} to be used in any Context-related actions.
+     * @param supportSettingsUpdate {@code true} if this object should read and listen to provider
+     *      settings values.
+     */
     TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
         MockitoAnnotations.initMocks(this);
         mHandler = new Handler(mTestLooper.getLooper());
@@ -416,6 +424,13 @@
         mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE);
     }
 
+    /**
+     * Provide access to the SettingsObserver so that tests can manually trigger Settings changes.
+     */
+    ContentObserver getSettingsObserver() {
+        return mPhoneWindowManager.mSettingsObserver;
+    }
+
     long getCurrentTime() {
         return mClock.now();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 0290f0f..4e4bbfe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -21,7 +21,6 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
@@ -64,7 +63,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.LetterboxUiController.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
 import static com.android.server.wm.LetterboxUiController.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -83,7 +81,6 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.RoundedCorner;
@@ -104,11 +101,11 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
-/**
+ /**
  * Test class for {@link LetterboxUiControllerTest}.
  *
  * Build/Install/Run:
- * atest WmTests:LetterboxUiControllerTest
+ *  atest WmTests:LetterboxUiControllerTest
  */
 @SmallTest
 @Presubmit
@@ -125,8 +122,6 @@
 
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private ActivityRecord mActivity;
     private Task mTask;
@@ -471,48 +466,10 @@
         assertTrue(mController.shouldForceRotateForCameraCompat());
     }
 
-    // shouldApplyFreeformTreatmentForCameraCompat
-
-    @Test
-    public void testShouldApplyCameraCompatFreeformTreatment_flagIsDisabled_returnsFalse() {
-        mSetFlagsRule.disableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse()
-            throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
-    @Test
-    public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue()
-            throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
     @Test
     public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
         final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
-                WindowInsets.Type.navigationBars());
+                 WindowInsets.Type.navigationBars());
         taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
         final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
         final Rect opaqueBounds = new Rect(0, 0, 500, 300);
@@ -769,7 +726,7 @@
             throws Exception {
         mDisplayContent.setIgnoreOrientationRequest(false);
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
-                /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+             /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
     }
 
     @Test
@@ -779,7 +736,7 @@
         prepareActivityThatShouldApplyUserMinAspectRatioOverride();
 
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
-                /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+             /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
     }
 
     @Test
@@ -902,7 +859,6 @@
         assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
                 /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
     }
-
     @Test
     public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
         spyOn(mController);
@@ -1424,7 +1380,7 @@
 
     private void mockThatProperty(String propertyName, boolean value) throws Exception {
         Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
-                /* className */ "");
+                 /* className */ "");
         PackageManager pm = mWm.mContext.getPackageManager();
         spyOn(pm);
         doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 03b695d..43b424f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1376,8 +1376,9 @@
         assertTrue(w1.syncNextBuffer());
         assertTrue(w2.syncNextBuffer());
 
-        // A drawn window can complete the sync state automatically.
+        // A drawn window in non-explicit sync can complete the sync state automatically.
         w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+        w1.mPrepareSyncSeqId = 0;
         makeLastConfigReportedToClient(w1, true /* visible */);
         mWm.mSyncEngine.onSurfacePlacement();
         verify(mockCallback).onTransactionReady(anyInt(), any());
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 2ff21ad..40537c8 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -978,8 +978,14 @@
 
         shouldRestrictOverlayActivities = filteredAppProcessInfos.stream().anyMatch(pkg -> {
             try {
-                return mPackageManager.getProperty(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES, pkg)
-                        .getBoolean();
+                boolean restrictUsbOverlayActivitiesForPackage = mPackageManager
+                        .getProperty(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES, pkg).getBoolean();
+
+                if (restrictUsbOverlayActivitiesForPackage) {
+                    Slog.d(TAG, "restricting usb overlay activities as package " + pkg
+                            + " is in foreground");
+                }
+                return restrictUsbOverlayActivitiesForPackage;
             } catch (NameNotFoundException e) {
                 if (DEBUG) {
                     Slog.d(TAG, "property PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES "
@@ -989,8 +995,8 @@
             }
         });
 
-        if (shouldRestrictOverlayActivities) {
-            Slog.d(TAG, "restricting starting of usb overlay activities");
+        if (!shouldRestrictOverlayActivities) {
+            Slog.d(TAG, "starting of usb overlay activities");
         }
         return shouldRestrictOverlayActivities;
     }
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 5ba5ee883..8b9a93b 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,6 +16,8 @@
 
 package android.telecom;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,8 +30,11 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.text.TextUtils;
 
+import androidx.annotation.IntDef;
+
 import com.android.server.telecom.flags.Flags;
 
+import java.lang.annotation.Retention;
 import java.util.Objects;
 
 /**
@@ -42,48 +47,69 @@
 public final class DisconnectCause implements Parcelable {
 
     /** Disconnected because of an unknown or unspecified reason. */
-    public static final int UNKNOWN = TelecomProtoEnums.UNKNOWN; // = 0
+    public static final int UNKNOWN = 0;
     /** Disconnected because there was an error, such as a problem with the network. */
-    public static final int ERROR = TelecomProtoEnums.ERROR; // = 1
+    public static final int ERROR = 1;
     /** Disconnected because of a local user-initiated action, such as hanging up. */
-    public static final int LOCAL = TelecomProtoEnums.LOCAL; // = 2
+    public static final int LOCAL = 2;
     /**
      * Disconnected because the remote party hung up an ongoing call, or because an outgoing call
      * was not answered by the remote party.
      */
-    public static final int REMOTE = TelecomProtoEnums.REMOTE; // = 3
+    public static final int REMOTE = 3;
     /** Disconnected because it has been canceled. */
-    public static final int CANCELED = TelecomProtoEnums.CANCELED; // = 4
+    public static final int CANCELED = 4;
     /** Disconnected because there was no response to an incoming call. */
-    public static final int MISSED = TelecomProtoEnums.MISSED; // = 5
+    public static final int MISSED = 5;
     /** Disconnected because the user rejected an incoming call. */
-    public static final int REJECTED = TelecomProtoEnums.REJECTED; // = 6
+    public static final int REJECTED = 6;
     /** Disconnected because the other party was busy. */
-    public static final int BUSY = TelecomProtoEnums.BUSY; // = 7
+    public static final int BUSY = 7;
     /**
      * Disconnected because of a restriction on placing the call, such as dialing in airplane
      * mode.
      */
-    public static final int RESTRICTED = TelecomProtoEnums.RESTRICTED; // = 8
+    public static final int RESTRICTED = 8;
     /** Disconnected for reason not described by other disconnect codes. */
-    public static final int OTHER = TelecomProtoEnums.OTHER; // = 9
+    public static final int OTHER = 9;
     /**
      * Disconnected because the connection manager did not support the call. The call will be tried
      * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
      */
-    public static final int CONNECTION_MANAGER_NOT_SUPPORTED =
-            TelecomProtoEnums.CONNECTION_MANAGER_NOT_SUPPORTED; // = 10
+    public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
 
     /**
      * Disconnected because the user did not locally answer the incoming call, but it was answered
      * on another device where the call was ringing.
      */
-    public static final int ANSWERED_ELSEWHERE = TelecomProtoEnums.ANSWERED_ELSEWHERE; // = 11
+    public static final int ANSWERED_ELSEWHERE = 11;
 
     /**
      * Disconnected because the call was pulled from the current device to another device.
      */
-    public static final int CALL_PULLED = TelecomProtoEnums.CALL_PULLED; // = 12
+    public static final int CALL_PULLED = 12;
+
+    /**
+     * @hide
+     */
+    @Retention(SOURCE)
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @IntDef({
+            UNKNOWN,
+            ERROR,
+            LOCAL,
+            REMOTE,
+            CANCELED,
+            MISSED,
+            REJECTED,
+            BUSY,
+            RESTRICTED,
+            OTHER,
+            CONNECTION_MANAGER_NOT_SUPPORTED,
+            ANSWERED_ELSEWHERE,
+            CALL_PULLED
+    })
+    public @interface DisconnectCauseCode {}
 
     /**
      * Reason code (returned via {@link #getReason()}) which indicates that a call could not be
@@ -116,7 +142,7 @@
      */
     public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
 
-    private int mDisconnectCode;
+    private @DisconnectCauseCode int mDisconnectCode;
     private CharSequence mDisconnectLabel;
     private CharSequence mDisconnectDescription;
     private String mDisconnectReason;
@@ -130,7 +156,7 @@
      *
      * @param code The code for the disconnect cause.
      */
-    public DisconnectCause(int code) {
+    public DisconnectCause(@DisconnectCauseCode int code) {
         this(code, null, null, null, ToneGenerator.TONE_UNKNOWN);
     }
 
@@ -140,7 +166,7 @@
      * @param code The code for the disconnect cause.
      * @param reason The reason for the disconnect.
      */
-    public DisconnectCause(int code, String reason) {
+    public DisconnectCause(@DisconnectCauseCode int code, String reason) {
         this(code, null, null, reason, ToneGenerator.TONE_UNKNOWN);
     }
 
@@ -152,7 +178,8 @@
      * @param description The localized description to show to the user to explain the disconnect.
      * @param reason The reason for the disconnect.
      */
-    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason) {
+    public DisconnectCause(@DisconnectCauseCode int code, CharSequence label,
+            CharSequence description, String reason) {
         this(code, label, description, reason, ToneGenerator.TONE_UNKNOWN);
     }
 
@@ -165,8 +192,8 @@
      * @param reason The reason for the disconnect.
      * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
      */
-    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
-            int toneToPlay) {
+    public DisconnectCause(@DisconnectCauseCode int code, CharSequence label,
+            CharSequence description, String reason, int toneToPlay) {
         this(code, label, description, reason, toneToPlay,
                 android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                 PreciseDisconnectCause.ERROR_UNSPECIFIED, null /* imsReasonInfo */);
@@ -186,8 +213,8 @@
      * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
      * @hide
      */
-    public DisconnectCause(int code, @NonNull CharSequence label,
-            @NonNull CharSequence description, @NonNull String reason,
+    public DisconnectCause(@DisconnectCauseCode int code, @Nullable CharSequence label,
+            @Nullable CharSequence description, @Nullable String reason,
             int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
             @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
             @Nullable ImsReasonInfo imsReasonInfo) {
@@ -206,7 +233,7 @@
      *
      * @return The disconnect code.
      */
-    public int getCode() {
+    public @DisconnectCauseCode int getCode() {
         return mDisconnectCode;
     }
 
@@ -301,28 +328,24 @@
     @SystemApi
     @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public static final class Builder {
-        private int mDisconnectCode;
+        private @DisconnectCauseCode int mDisconnectCode;
         private CharSequence mDisconnectLabel;
         private CharSequence mDisconnectDescription;
         private String mDisconnectReason;
-        private int mToneToPlay;
+        private int mToneToPlay = ToneGenerator.TONE_UNKNOWN;
         private int mTelephonyDisconnectCause;
         private int mTelephonyPreciseDisconnectCause;
         private ImsReasonInfo mImsReasonInfo;
 
-        /**
-         * Sets the code for the reason for this disconnect.
-         * @param code The code denoting the type of disconnect.
-         */
-        public @NonNull DisconnectCause.Builder setCode(int code) {
+        public Builder(@DisconnectCauseCode int code) {
             mDisconnectCode = code;
-            return this;
         }
 
         /**
          * Sets a label which explains the reason for the disconnect cause, used for display in the
          * user interface.
          * @param label The label to associate with the disconnect cause.
+         * @return The {@link DisconnectCause} builder instance.
          */
         public @NonNull DisconnectCause.Builder setLabel(@Nullable CharSequence label) {
             mDisconnectLabel = label;
@@ -333,6 +356,7 @@
          * Sets a description which provides the reason for the disconnect cause, used for display
          * in the user interface.
          * @param description The description to associate with the disconnect cause.
+         * @return The {@link DisconnectCause} builder instance.
          */
         public @NonNull DisconnectCause.Builder setDescription(
                 @Nullable CharSequence description) {
@@ -344,6 +368,7 @@
          * Sets a reason providing explanation for the disconnect (intended for logging and not for
          * displaying in the user interface).
          * @param reason The reason for the disconnect.
+         * @return The {@link DisconnectCause} builder instance.
          */
         public @NonNull DisconnectCause.Builder setReason(@NonNull String reason) {
             mDisconnectReason = reason;
@@ -353,6 +378,7 @@
         /**
          * Sets the tone to play when disconnected.
          * @param toneToPlay The tone as defined in {@link ToneGenerator} to play when disconnected.
+         * @return The {@link DisconnectCause} builder instance.
          */
         public @NonNull DisconnectCause.Builder setTone(int toneToPlay) {
             mToneToPlay = toneToPlay;
@@ -363,6 +389,7 @@
          * Sets the telephony {@link android.telephony.DisconnectCause} for the call (used
          * internally by Telecom for providing extra debug information from Telephony).
          * @param telephonyDisconnectCause The disconnect cause as provided by Telephony.
+         * @return The {@link DisconnectCause} builder instance.
          */
         public @NonNull DisconnectCause.Builder setTelephonyDisconnectCause(
                 @Annotation.DisconnectCauses int telephonyDisconnectCause) {
@@ -375,6 +402,7 @@
          * internally by Telecom for providing extra debug information from Telephony).
          * @param telephonyPreciseDisconnectCause The precise disconnect cause as provided by
          *                                        Telephony.
+         * @return The {@link DisconnectCause} builder instance.
          */
 
         public @NonNull DisconnectCause.Builder setTelephonyPreciseDisconnectCause(
@@ -384,10 +412,11 @@
         }
 
         /**
-         * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. This
+         * Sets the telephony {@link ImsReasonInfo} associated with the call disconnection. This
          * is only used internally by Telecom for providing extra debug information from Telephony.
          *
          * @param imsReasonInfo The {@link ImsReasonInfo} or {@code null} if not known.
+         * @return The {@link DisconnectCause} builder instance.
          */
         public @NonNull DisconnectCause.Builder setImsReasonInfo(
                 @Nullable ImsReasonInfo imsReasonInfo) {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index e3ce766..ebabbf9 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -512,6 +512,7 @@
             EUICC_ACTIVATION_TYPE_BACKUP,
             EUICC_ACTIVATION_TYPE_TRANSFER,
             EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED,
+            EUICC_ACTIVATION_TYPE_TRANSFER_FINAL_HOLD,
     })
     public @interface EuiccActivationType{}
 
@@ -555,6 +556,15 @@
     public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4;
 
     /**
+     * The activation flow of eSIM transfer to block the transfer process before B&R flow.
+     * This is needed to avoid connection overlapping between eSIM connection B&R connection.
+     *
+     * @hide
+     */
+     // TODO(b/329212614): add system api annotation during the allowed api timeline.
+     public static final int EUICC_ACTIVATION_TYPE_TRANSFER_FINAL_HOLD = 5;
+
+    /**
      * Euicc OTA update status which can be got by {@link #getOtaStatus}
      * @removed mistakenly exposed as system-api previously
      */
diff --git a/tools/aapt2/optimize/VersionCollapser.cpp b/tools/aapt2/optimize/VersionCollapser.cpp
index cd791bd..27fff9a 100644
--- a/tools/aapt2/optimize/VersionCollapser.cpp
+++ b/tools/aapt2/optimize/VersionCollapser.cpp
@@ -70,7 +70,7 @@
  * exception is when there is no exact matching resource for the minSdk. The next smallest one will
  * be kept.
  */
-static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
+static void CollapseVersions(IAaptContext* context, int min_sdk, ResourceEntry* entry) {
   // First look for all sdks less than minSdk.
   for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
        ++iter) {
@@ -102,7 +102,14 @@
       auto filter_iter =
           make_filter_iterator(iter + 1, entry->values.rend(), pred);
       while (filter_iter.HasNext()) {
-        filter_iter.Next() = {};
+        auto& next = filter_iter.Next();
+        if (context->IsVerbose()) {
+          context->GetDiagnostics()->Note(android::DiagMessage()
+                                          << "removing configuration " << next->config.to_string()
+                                          << " for entry: " << entry->name
+                                          << ", because its SDK version is smaller than minSdk");
+        }
+        next = {};
       }
     }
   }
@@ -126,6 +133,12 @@
           util::make_unique<ResourceConfigValue>(
               config_value->config.CopyWithoutSdkVersion(),
               config_value->product);
+      if (context->IsVerbose()) {
+        context->GetDiagnostics()->Note(android::DiagMessage()
+                                        << "overriding resource: " << entry->name
+                                        << ", removing SDK version from configuration "
+                                        << config_value->config.to_string());
+      }
       new_value->value = std::move(config_value->value);
       config_value = std::move(new_value);
 
@@ -147,10 +160,14 @@
 bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
   TRACE_NAME("VersionCollapser::Consume");
   const int min_sdk = context->GetMinSdkVersion();
+  if (context->IsVerbose()) {
+    context->GetDiagnostics()->Note(android::DiagMessage()
+                                    << "Running VersionCollapser with minSdk = " << min_sdk);
+  }
   for (auto& package : table->packages) {
     for (auto& type : package->types) {
       for (auto& entry : type->entries) {
-        CollapseVersions(min_sdk, entry.get());
+        CollapseVersions(context, min_sdk, entry.get());
       }
     }
   }