Merge changes from topic "nov29" into main

* changes:
  `LruCache` under Ravenwood.
  `FileUtils` and `AtomicFile` under Ravenwood.
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b3e8ea8..1fb5f34 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -56,6 +56,7 @@
     ":android.credentials.flags-aconfig-java{.generated_srcjars}",
     ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
     ":com.android.server.flags.pinner-aconfig-java{.generated_srcjars}",
+    ":android.service.controls.flags-aconfig-java{.generated_srcjars}",
     ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
     ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
     ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
@@ -598,6 +599,19 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// Controls
+aconfig_declarations {
+    name: "android.service.controls.flags-aconfig",
+    package: "android.service.controls.flags",
+    srcs: ["core/java/android/service/controls/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.service.controls.flags-aconfig-java",
+    aconfig_declarations: "android.service.controls.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Voice
 aconfig_declarations {
     name: "android.service.voice.flags-aconfig",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index df9ac82..03f3f0f 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -32,6 +32,8 @@
     cmd: "$(location hoststubgen) " +
         "@$(location ravenwood/ravenwood-standard-options.txt) " +
 
+        "--debug-log $(location hoststubgen_framework-minus-apex.log) " +
+
         "--out-impl-jar $(location ravenwood.jar) " +
 
         "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
@@ -52,6 +54,8 @@
         // Following files are created just as FYI.
         "hoststubgen_keep_all.txt",
         "hoststubgen_dump.txt",
+
+        "hoststubgen_framework-minus-apex.log",
     ],
     visibility: ["//visibility:private"],
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 1bd8da82..5a32a02 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2163,6 +2163,8 @@
                     mActivityManagerInternal.getBootTimeTempAllowListDuration(),
                     TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                     PowerExemptionManager.REASON_TIMEZONE_CHANGED, "");
+            mOptsTimeBroadcast.setDeliveryGroupPolicy(
+                    BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
             getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                     null /* receiverPermission */, mOptsTimeBroadcast.toBundle());
         }
@@ -4608,6 +4610,8 @@
                                 mActivityManagerInternal.getBootTimeTempAllowListDuration(),
                                 TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                                 PowerExemptionManager.REASON_TIME_CHANGED, "");
+                        mOptsTimeBroadcast.setDeliveryGroupPolicy(
+                                BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
                         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                                 null /* receiverPermission */, mOptsTimeBroadcast.toBundle());
                         // The world has changed on us, so we need to re-evaluate alarms
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b339fa..5e7a383 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9676,6 +9676,7 @@
     method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
     method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
     method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
+    method @FlaggedApi("android.companion.perm_sync_user_consent") public boolean isPermissionTransferUserConsented(int);
     method public void requestNotificationAccess(android.content.ComponentName);
     method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
@@ -40108,6 +40109,9 @@
     method public final boolean onUnbind(@NonNull android.content.Intent);
     method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control);
+    field @FlaggedApi("android.service.controls.flags.home_panel_dream") public static final int CONTROLS_SURFACE_ACTIVITY_PANEL = 0; // 0x0
+    field @FlaggedApi("android.service.controls.flags.home_panel_dream") public static final int CONTROLS_SURFACE_DREAM = 1; // 0x1
+    field @FlaggedApi("android.service.controls.flags.home_panel_dream") public static final String EXTRA_CONTROLS_SURFACE = "android.service.controls.extra.CONTROLS_SURFACE";
     field public static final String EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS = "android.service.controls.extra.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS";
     field public static final String META_DATA_PANEL_ACTIVITY = "android.service.controls.META_DATA_PANEL_ACTIVITY";
     field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
@@ -43132,8 +43136,8 @@
     field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
     field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
     field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
-    field public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool";
-    field public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool";
     field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
     field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
@@ -52050,6 +52054,7 @@
     method public float getHandwritingBoundsOffsetLeft();
     method public float getHandwritingBoundsOffsetRight();
     method public float getHandwritingBoundsOffsetTop();
+    method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public int getHandwritingDelegateFlags();
     method @Nullable public Runnable getHandwritingDelegatorCallback();
     method public final boolean getHasOverlappingRendering();
     method public final int getHeight();
@@ -52423,6 +52428,7 @@
     method public void setForegroundTintMode(@Nullable android.graphics.PorterDuff.Mode);
     method @FlaggedApi("android.view.flags.view_velocity_api") public void setFrameContentVelocity(float);
     method public void setHandwritingBoundsOffsets(float, float, float, float);
+    method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public void setHandwritingDelegateFlags(int);
     method public void setHandwritingDelegatorCallback(@Nullable Runnable);
     method public void setHapticFeedbackEnabled(boolean);
     method public void setHasTransientState(boolean);
@@ -55678,6 +55684,7 @@
   public final class InputMethodManager {
     method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View);
     method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String);
+    method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, int);
     method public void dispatchKeyEventFromInputMethod(@Nullable android.view.View, @NonNull android.view.KeyEvent);
     method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
     method @Nullable public android.view.inputmethod.InputMethodInfo getCurrentInputMethodInfo();
@@ -55725,6 +55732,7 @@
     method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText);
     method public void updateSelection(android.view.View, int, int, int, int);
     method @Deprecated public void viewClicked(android.view.View);
+    field @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public static final int HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED = 1; // 0x1
     field public static final int HIDE_IMPLICIT_ONLY = 1; // 0x1
     field public static final int HIDE_NOT_ALWAYS = 2; // 0x2
     field public static final int RESULT_HIDDEN = 3; // 0x3
@@ -59542,6 +59550,7 @@
     method public void setOnClickFillInIntent(@IdRes int, android.content.Intent);
     method public void setOnClickPendingIntent(@IdRes int, android.app.PendingIntent);
     method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
+    method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public void setOnStylusHandwritingPendingIntent(@IdRes int, @Nullable android.app.PendingIntent);
     method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
     method public void setProgressBar(@IdRes int, int, int, boolean);
     method public void setRadioGroupChecked(@IdRes int, @IdRes int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0497c60..847edd1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10555,6 +10555,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
+    method @NonNull public android.graphics.drawable.Drawable getUserBadge();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
@@ -12787,6 +12788,7 @@
     field @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final int ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED = 8; // 0x8
     field @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final int ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION = 9; // 0x9
     field public static final int ERROR_CODE_REMOTE_EXCEPTION = 7; // 0x7
+    field @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final int ERROR_CODE_SHUTDOWN_HDS_ON_VOICE_ACTIVATION_OP_DISABLED = 10; // 0xa
     field public static final int ERROR_CODE_UNKNOWN = 0; // 0x0
   }
 
@@ -16140,8 +16142,10 @@
 
   public interface RegistrationManager {
     field public static final int SUGGESTED_ACTION_NONE = 0; // 0x0
+    field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; // 0x4
     field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK = 1; // 0x1
     field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2; // 0x2
+    field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3; // 0x3
   }
 
   public static class RegistrationManager.RegistrationCallback {
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index e0ce917..e4a03c5 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1358,6 +1358,36 @@
     }
 
     /**
+     * Return the current state of consent for permission transfer for the association.
+     * True if the user has allowed permission transfer for the association, false otherwise.
+     *
+     * <p>
+     * Note: The initial user consent is collected via
+     * {@link #buildPermissionTransferUserConsentIntent(int) a permission transfer user consent dialog}.
+     * After the user has made their initial selection, they can toggle the permission transfer
+     * feature in the settings.
+     * This method always returns the state of the toggle setting.
+     * </p>
+     *
+     * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the association
+     *                      of the companion device recorded by CompanionDeviceManager
+     * @return True if the user has consented to the permission transfer, or false otherwise.
+     * @throws DeviceNotAssociatedException Exception if the companion device is not associated with
+     *                                      the user or the calling app.
+     */
+    @UserHandleAware
+    @FlaggedApi(Flags.FLAG_PERM_SYNC_USER_CONSENT)
+    public boolean isPermissionTransferUserConsented(int associationId) {
+        try {
+            return mService.isPermissionTransferUserConsented(mContext.getOpPackageName(),
+                    mContext.getUserId(), associationId);
+        } catch (RemoteException e) {
+            ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Start system data transfer which has been previously approved by the user.
      *
      * <p>Before calling this method, the app needs to make sure there's a communication channel
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index c5a1988..22689f3 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -97,6 +97,8 @@
     PendingIntent buildPermissionTransferUserConsentIntent(String callingPackage, int userId,
         int associationId);
 
+    boolean isPermissionTransferUserConsented(String callingPackage, int userId, int associationId);
+
     void startSystemDataTransfer(String packageName, int userId, int associationId,
         in ISystemDataTransferCallback callback);
 
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index 6e33dff..9e410b8 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -27,3 +27,10 @@
     description: "Enable device presence APIs"
     bug: "283000075"
 }
+
+flag {
+    name: "perm_sync_user_consent"
+    namespace: "companion"
+    description: "Expose perm sync user consent API"
+    bug: "309528663"
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index d21b818..1b90570 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -101,3 +101,10 @@
     description: "Feature flag to enable the refactored Package Installer app with updated UI."
     bug: "182205982"
 }
+
+flag {
+    name: "improve_install_dont_kill"
+    namespace: "package_manager_service"
+    description: "Feature flag to reduce app crashes caused by split installs with INSTALL_DONT_KILL"
+    bug: "291212866"
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 11bddfb..f4795f8 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import android.annotation.FlaggedApi;
 import android.Manifest.permission;
 import android.annotation.CallbackExecutor;
 import android.annotation.CurrentTimeMillisLong;
@@ -3103,7 +3102,8 @@
 
     /**
      * Intent that is broadcast when Low Power Standby is enabled or disabled.
-     * This broadcast is only sent to registered receivers.
+     * This broadcast is only sent to registered receivers and receivers holding
+     * {@code android.permission.MANAGE_LOW_POWER_STANDBY}.
      *
      * @see #isLowPowerStandbyEnabled()
      */
@@ -3113,7 +3113,8 @@
 
     /**
      * Intent that is broadcast when Low Power Standby policy is changed.
-     * This broadcast is only sent to registered receivers.
+     * This broadcast is only sent to registered receivers and receivers holding
+     * {@code android.permission.MANAGE_LOW_POWER_STANDBY}.
      *
      * @see #isExemptFromLowPowerStandby()
      * @see #isAllowedInLowPowerStandby(int)
@@ -3125,7 +3126,6 @@
 
     /**
      * Intent that is broadcast when Low Power Standby exempt ports change.
-     * This broadcast is only sent to registered receivers.
      *
      * @see #getActiveLowPowerStandbyPorts
      * @hide
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ec6d20f..c280d13 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
 import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
 import static android.app.admin.DevicePolicyResources.UNDEFINED;
 
@@ -5652,6 +5653,38 @@
     }
 
     /**
+     * Retrieves a user badge associated with the current context user. This is only
+     * applicable to profile users since non-profile users do not have badges.
+     *
+     * @return A {@link Drawable} user badge corresponding to the context user
+     * @throws android.content.res.Resources.NotFoundException if the user is not a profile or
+     * does not have a badge defined.
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.INTERACT_ACROSS_USERS})
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public @NonNull Drawable getUserBadge() {
+        if (!isProfile(mUserId)) {
+            throw new Resources.NotFoundException("No badge found for this user.");
+        }
+        if (isManagedProfile(mUserId)) {
+            DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+            return dpm.getResources().getDrawable(
+                    android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON_BADGE,
+                    SOLID_COLORED, () -> getDefaultUserBadge(mUserId));
+        }
+        return getDefaultUserBadge(mUserId);
+    }
+
+    private Drawable getDefaultUserBadge(@UserIdInt int userId){
+        return mContext.getResources().getDrawable(getUserBadgeResId(userId), mContext.getTheme());
+    }
+
+    /**
      * If the target user is a profile of the calling user or the caller
      * is itself a profile, then this returns a copy of the label with
      * badging for accessibility services like talkback. E.g. passing in "Email"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7f64400..1a33b768 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8846,6 +8846,24 @@
                 "reduce_bright_colors_persist_across_reboots";
 
         /**
+         * Setting that specifies whether Even Dimmer - a feature that allows the brightness
+         * slider to go below what the display can conventionally do, should be enabled.
+         *
+         * @hide
+         */
+        public static final String EVEN_DIMMER_ACTIVATED =
+                "even_dimmer_activated";
+
+        /**
+         * Setting that specifies which nits level Even Dimmer should allow the screen brightness
+         * to go down to.
+         *
+         * @hide
+         */
+        public static final String EVEN_DIMMER_MIN_NITS =
+                "even_dimmer_min_nits";
+
+        /**
          * List of the enabled print services.
          *
          * N and beyond uses {@link #DISABLED_PRINT_SERVICES}. But this might be used in an upgrade
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 0133bd8..0dc0413 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -8,7 +8,7 @@
 }
 
 flag {
-    name: "fix_unlocked_device_required_keys"
+    name: "fix_unlocked_device_required_keys_v2"
     namespace: "hardware_backed_security"
     description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys"
     bug: "296464083"
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 0272bb9..5cbde9f 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -16,6 +16,8 @@
 package android.service.controls;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -33,12 +35,15 @@
 import android.os.RemoteException;
 import android.service.controls.actions.ControlAction;
 import android.service.controls.actions.ControlActionWrapper;
+import android.service.controls.flags.Flags;
 import android.service.controls.templates.ControlTemplate;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.concurrent.Flow.Publisher;
 import java.util.concurrent.Flow.Subscriber;
@@ -82,6 +87,40 @@
     public static final String EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS =
             "android.service.controls.extra.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({CONTROLS_SURFACE_ACTIVITY_PANEL, CONTROLS_SURFACE_DREAM})
+    public @interface ControlsSurface {
+    }
+
+    /**
+     * Controls are being shown on the device controls activity panel.
+     */
+    @FlaggedApi(Flags.FLAG_HOME_PANEL_DREAM)
+    public static final int CONTROLS_SURFACE_ACTIVITY_PANEL = 0;
+
+    /**
+     * Controls are being shown as a dream, while the device is idle.
+     */
+    @FlaggedApi(Flags.FLAG_HOME_PANEL_DREAM)
+    public static final int CONTROLS_SURFACE_DREAM = 1;
+
+    /**
+     * Integer extra whose value specifies the surface which controls are being displayed on.
+     * <p>
+     * The possible values are:
+     * <ul>
+     * <li>{@link #CONTROLS_SURFACE_ACTIVITY_PANEL}
+     * <li>{@link #CONTROLS_SURFACE_DREAM}
+     * </ul>
+     *
+     * This is passed with the intent when the panel specified by {@link #META_DATA_PANEL_ACTIVITY}
+     * is launched.
+     */
+    @FlaggedApi(Flags.FLAG_HOME_PANEL_DREAM)
+    public static final String EXTRA_CONTROLS_SURFACE =
+            "android.service.controls.extra.CONTROLS_SURFACE";
+
     /**
      * @hide
      */
diff --git a/core/java/android/service/controls/flags/flags.aconfig b/core/java/android/service/controls/flags/flags.aconfig
new file mode 100644
index 0000000..3a28844
--- /dev/null
+++ b/core/java/android/service/controls/flags/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.service.controls.flags"
+
+flag {
+    name: "home_panel_dream"
+    namespace: "systemui"
+    description: "Enables the home controls dream feature."
+    bug: "298025023"
+}
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 46ea158..7660ed9 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -233,7 +233,7 @@
             } finally {
                 mapParcel.recycle();
                 // To prevent memory leaks, we can close the ranking map fd here.
-                // Because a reference to this still exists
+                // This is safe to do because a reference to this still exists.
                 if (buffer != null && mRankingMapFd != null) {
                     SharedMemory.unmap(buffer);
                     mRankingMapFd.close();
diff --git a/core/java/android/service/voice/HotwordDetectionServiceFailure.java b/core/java/android/service/voice/HotwordDetectionServiceFailure.java
index 420dac1..c8b60c4 100644
--- a/core/java/android/service/voice/HotwordDetectionServiceFailure.java
+++ b/core/java/android/service/voice/HotwordDetectionServiceFailure.java
@@ -89,6 +89,11 @@
     @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
     public static final int ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION = 9;
 
+    /** Indicates shutdown of {@link HotwordDetectionService} due to voice activation op being
+     * disabled. */
+    @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+    public static final int ERROR_CODE_SHUTDOWN_HDS_ON_VOICE_ACTIVATION_OP_DISABLED = 10;
+
     /**
      * @hide
      */
@@ -100,7 +105,10 @@
             ERROR_CODE_DETECT_TIMEOUT,
             ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION,
             ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE,
-            ERROR_CODE_REMOTE_EXCEPTION
+            ERROR_CODE_REMOTE_EXCEPTION,
+            ERROR_CODE_ON_TRAINING_DATA_EGRESS_LIMIT_EXCEEDED,
+            ERROR_CODE_ON_TRAINING_DATA_SECURITY_EXCEPTION,
+            ERROR_CODE_SHUTDOWN_HDS_ON_VOICE_ACTIVATION_OP_DISABLED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface HotwordDetectionServiceErrorCode {}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 90663c7..147c15b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -216,6 +216,14 @@
         default CompatibilityInfo.Translator getTranslator() {
             return null;
         }
+
+        /**
+         * Notifies when the state of running animation is changed. The state is either "running" or
+         * "idle".
+         *
+         * @param running {@code true} if there is any animation running; {@code false} otherwise.
+         */
+        default void notifyAnimationRunningStateChanged(boolean running) {}
     }
 
     private static final String TAG = "InsetsController";
@@ -749,6 +757,9 @@
                     final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner(
                             mFrame, state1, mToState, RESIZE_INTERPOLATOR,
                             ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this);
+                    if (mRunningAnimations.isEmpty()) {
+                        mHost.notifyAnimationRunningStateChanged(true);
+                    }
                     mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
                 }
             };
@@ -1382,6 +1393,9 @@
             }
         }
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
+        if (mRunningAnimations.isEmpty()) {
+            mHost.notifyAnimationRunningStateChanged(true);
+        }
         mRunningAnimations.add(new RunningAnimation(runner, animationType));
         if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
                 + useInsetsAnimationThread);
@@ -1588,6 +1602,9 @@
                 break;
             }
         }
+        if (mRunningAnimations.isEmpty()) {
+            mHost.notifyAnimationRunningStateChanged(false);
+        }
         onAnimationStateChanged(removedTypes, false /* running */);
     }
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index d0a4d1a..ad0bf7c 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -197,7 +197,9 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"FRAME_RATE_COMPATIBILITY_"},
-            value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE})
+            value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                    FRAME_RATE_COMPATIBILITY_EXACT, FRAME_RATE_COMPATIBILITY_NO_VOTE,
+                    FRAME_RATE_COMPATIBILITY_MIN, FRAME_RATE_COMPATIBILITY_GTE})
     public @interface FrameRateCompatibility {}
 
     // From native_window.h. Keep these in sync.
@@ -242,6 +244,13 @@
      */
     public static final int FRAME_RATE_COMPATIBILITY_MIN = 102;
 
+    // From window.h. Keep these in sync.
+    /**
+     * The surface requests a frame rate that is greater than or equal to {@code frameRate}.
+     * @hide
+     */
+    public static final int FRAME_RATE_COMPATIBILITY_GTE = 103;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"CHANGE_FRAME_RATE_"},
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d58c07d..a268bca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34,6 +34,7 @@
 import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
 import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
 import static android.view.flags.Flags.viewVelocityApi;
+import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
 
 import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
 import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
@@ -5154,9 +5155,10 @@
     private Runnable mHandwritingDelegatorCallback;
     private String mAllowedHandwritingDelegatePackageName;
 
-    // These two fields are set if the view is a handwriting delegate.
+    // These three fields are set if the view is a handwriting delegate.
     private boolean mIsHandwritingDelegate;
     private String mAllowedHandwritingDelegatorPackageName;
+    private @InputMethodManager.HandwritingDelegateFlags int mHandwritingDelegateFlags;
 
     /**
      * Solid color to use as a background when creating the drawing cache. Enables
@@ -12747,6 +12749,30 @@
     }
 
     /**
+     * Sets flags configuring the handwriting delegation behavior for this delegate editor view.
+     *
+     * <p>This method has no effect unless {@link #setIsHandwritingDelegate} is also called to
+     * configure this view to act as a handwriting delegate.
+     *
+     * @param flags {@link InputMethodManager#HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or
+     *     {@code 0}
+     */
+    @FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
+    public void setHandwritingDelegateFlags(
+            @InputMethodManager.HandwritingDelegateFlags int flags) {
+        mHandwritingDelegateFlags = flags;
+    }
+
+    /**
+     * Returns flags configuring the handwriting delegation behavior for this delegate editor view,
+     * as set by {@link #setHandwritingDelegateFlags}.
+     */
+    @FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
+    public @InputMethodManager.HandwritingDelegateFlags int getHandwritingDelegateFlags() {
+        return mHandwritingDelegateFlags;
+    }
+
+    /**
      * Gets the coordinates of this view in the coordinate space of the
      * {@link Surface} that contains the view.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cac5387..9d2ab1f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -92,6 +92,7 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.accessibility.Flags.forceInvertColor;
+import static android.view.accessibility.Flags.reduceWindowContentChangedEventThrottle;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
 import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
@@ -816,6 +817,8 @@
     private long mFpsPrevTime = -1;
     private int mFpsNumFrames;
 
+    private boolean mInsetsAnimationRunning;
+
     /**
      * The resolved pointer icon type requested by this window.
      * A null value indicates the resolved pointer icon has not yet been calculated.
@@ -2179,6 +2182,10 @@
         }
     }
 
+    void notifyInsetsAnimationRunningStateChanged(boolean running) {
+        mInsetsAnimationRunning = running;
+    }
+
     @Override
     public void requestLayout() {
         if (!mHandlingLayoutInLayoutRequest) {
@@ -11355,6 +11362,10 @@
         }
 
         private boolean canContinueThrottle(View source, int changeType) {
+            if (!reduceWindowContentChangedEventThrottle()) {
+                // Old behavior. Always throttle.
+                return true;
+            }
             if (mSource == null) {
                 // We don't have a pending event.
                 return true;
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index a2708ee..40730e8 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -279,6 +279,13 @@
         return null;
     }
 
+    @Override
+    public void notifyAnimationRunningStateChanged(boolean running) {
+        if (mViewRoot != null) {
+            mViewRoot.notifyInsetsAnimationRunningStateChanged(running);
+        }
+    }
+
     private boolean isVisibleToUser() {
         return mViewRoot.getHostVisibility() == View.VISIBLE;
     }
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index c337cb4..aa4275d6 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -53,7 +53,21 @@
 
 flag {
     namespace: "accessibility"
+    name: "reduce_window_content_changed_event_throttle"
+    description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED"
+    bug: "277305460"
+}
+
+flag {
+    namespace: "accessibility"
     name: "update_always_on_a11y_service"
     description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
     bug: "298869916"
 }
+
+flag {
+    name: "enable_system_pinch_zoom_gesture"
+    namespace: "accessibility"
+    description: "Feature flag for system pinch zoom gesture detector and related opt-out apis"
+    bug: "283323770"
+}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 614102b..c244287 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -514,14 +514,15 @@
             @NonNull IInputMethodClient client,
             @UserIdInt int userId,
             @NonNull String delegatePackageName,
-            @NonNull String delegatorPackageName) {
+            @NonNull String delegatorPackageName,
+            @InputMethodManager.HandwritingDelegateFlags int flags) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return false;
         }
         try {
             return service.acceptStylusHandwritingDelegation(
-                    client, userId, delegatePackageName, delegatorPackageName);
+                    client, userId, delegatePackageName, delegatorPackageName, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 589b7a3..6d7a543 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -17,6 +17,7 @@
 package android.view.inputmethod;
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
 import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE;
 import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
@@ -425,6 +426,23 @@
     private static final boolean OPTIMIZE_NONEDITABLE_VIEWS =
             SystemProperties.getBoolean("debug.imm.optimize_noneditable_views", true);
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "HANDWRITING_DELEGATE_FLAG_" }, value = {
+            HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HandwritingDelegateFlags {}
+
+    /**
+     * Flag indicating that views from the default home screen ({@link Intent#CATEGORY_HOME}) may
+     * act as a handwriting delegator for the delegate editor view. If set, views from the home
+     * screen package will be trusted for handwriting delegation, in addition to views in the {@code
+     * delegatorPackageName} passed to {@link #acceptStylusHandwritingDelegation(View, String,
+     * int)}.
+     */
+    @FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
+    public static final int HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED = 0x0001;
+
     /**
      * @deprecated Use {@link IInputMethodManagerGlobalInvoker} instead.
      */
@@ -2345,17 +2363,20 @@
      * @see #isStylusHandwritingAvailable()
      */
     public void startStylusHandwriting(@NonNull View view) {
-        startStylusHandwritingInternal(view, null /* delegatorPackageName */);
+        startStylusHandwritingInternal(
+                view, /* delegatorPackageName= */ null, /* handwritingDelegateFlags= */ 0);
     }
 
     private boolean startStylusHandwritingInternal(
-            @NonNull View view, @Nullable String delegatorPackageName) {
+            @NonNull View view, @Nullable String delegatorPackageName,
+            @HandwritingDelegateFlags int handwritingDelegateFlags) {
         Objects.requireNonNull(view);
 
         // Re-dispatch if there is a context mismatch.
         final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
         if (fallbackImm != null) {
-            fallbackImm.startStylusHandwritingInternal(view, delegatorPackageName);
+            fallbackImm.startStylusHandwritingInternal(
+                    view, delegatorPackageName, handwritingDelegateFlags);
         }
 
         boolean useDelegation = !TextUtils.isEmpty(delegatorPackageName);
@@ -2375,7 +2396,7 @@
             if (useDelegation) {
                 return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation(
                         mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
-                        delegatorPackageName);
+                        delegatorPackageName, handwritingDelegateFlags);
             } else {
                 IInputMethodManagerGlobalInvoker.startStylusHandwriting(mClient);
             }
@@ -2470,16 +2491,17 @@
      */
     public boolean acceptStylusHandwritingDelegation(@NonNull View delegateView) {
         return startStylusHandwritingInternal(
-                delegateView, delegateView.getContext().getOpPackageName());
+                delegateView, delegateView.getContext().getOpPackageName(),
+                delegateView.getHandwritingDelegateFlags());
     }
 
     /**
      * Accepts and starts a stylus handwriting session on the delegate view, if handwriting
      * initiation delegation was previously requested using
-     * {@link #prepareStylusHandwritingDelegation(View, String)} from te delegator and the view
+     * {@link #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view
      * belongs to a specified delegate package.
      *
-     * <p>Note: If delegator and delegate are in same application package use
+     * <p>Note: If delegator and delegate are in the same application package, use
      * {@link #acceptStylusHandwritingDelegation(View)} instead.</p>
      *
      * @param delegateView delegate view capable of receiving input via {@link InputConnection}
@@ -2493,8 +2515,35 @@
     public boolean acceptStylusHandwritingDelegation(
             @NonNull View delegateView, @NonNull String delegatorPackageName) {
         Objects.requireNonNull(delegatorPackageName);
+        return startStylusHandwritingInternal(
+                delegateView, delegatorPackageName, delegateView.getHandwritingDelegateFlags());
+    }
 
-        return startStylusHandwritingInternal(delegateView, delegatorPackageName);
+    /**
+     * Accepts and starts a stylus handwriting session on the delegate view, if handwriting
+     * initiation delegation was previously requested using {@link
+     * #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view belongs to
+     * a specified delegate package.
+     *
+     * <p>Note: If delegator and delegate are in the same application package, use {@link
+     * #acceptStylusHandwritingDelegation(View)} instead.
+     *
+     * @param delegateView delegate view capable of receiving input via {@link InputConnection} on
+     *     which {@link #startStylusHandwriting(View)} will be called.
+     * @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
+     * @param flags {@link #HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or {@code 0}
+     * @return {@code true} if view belongs to allowed delegate package declared in {@link
+     *     #prepareStylusHandwritingDelegation(View, String)} and handwriting session can start.
+     * @see #prepareStylusHandwritingDelegation(View, String)
+     * @see #acceptStylusHandwritingDelegation(View)
+     */
+    @FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
+    public boolean acceptStylusHandwritingDelegation(
+            @NonNull View delegateView, @NonNull String delegatorPackageName,
+            @HandwritingDelegateFlags int flags) {
+        Objects.requireNonNull(delegatorPackageName);
+
+        return startStylusHandwritingInternal(delegateView, delegatorPackageName, flags);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 7486362..dc6aa6c 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -31,3 +31,10 @@
     bug: "284527000"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "home_screen_handwriting_delegator"
+    namespace: "input_method"
+    description: "Feature flag for supporting stylus handwriting delegation from RemoteViews on the home screen"
+    bug: "279959705"
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1acebf4..da31348 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -16,11 +16,14 @@
 
 package android.widget;
 
+import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
+
 import android.annotation.AttrRes;
 import android.annotation.ColorInt;
 import android.annotation.ColorRes;
 import android.annotation.DimenRes;
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.IdRes;
 import android.annotation.IntDef;
 import android.annotation.LayoutRes;
@@ -92,6 +95,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
+import android.view.MotionEvent;
 import android.view.RemotableViewMethod;
 import android.view.View;
 import android.view.ViewGroup;
@@ -239,6 +243,7 @@
     private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31;
     private static final int ATTRIBUTE_REFLECTION_ACTION_TAG = 32;
     private static final int SET_REMOTE_ADAPTER_TAG = 33;
+    private static final int SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG = 34;
 
     /** @hide **/
     @IntDef(prefix = "MARGIN_", value = {
@@ -1510,6 +1515,53 @@
         }
     }
 
+    /** Helper action to configure handwriting delegation via {@link PendingIntent}. */
+    private class SetOnStylusHandwritingResponse extends Action {
+        final PendingIntent mPendingIntent;
+
+        SetOnStylusHandwritingResponse(@IdRes int id, @Nullable PendingIntent pendingIntent) {
+            this.mViewId = id;
+            this.mPendingIntent = pendingIntent;
+        }
+
+        SetOnStylusHandwritingResponse(@NonNull Parcel parcel) {
+            mViewId = parcel.readInt();
+            mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+        }
+
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mViewId);
+            PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
+            final View target = root.findViewById(mViewId);
+            if (target == null) return;
+
+            if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
+                Log.w(LOG_TAG, "Cannot use setOnStylusHandwritingPendingIntent for collection item "
+                        + "(id: " + mViewId + ")");
+                return;
+            }
+
+            if (mPendingIntent != null) {
+                RemoteResponse response = RemoteResponse.fromPendingIntent(mPendingIntent);
+                target.setHandwritingDelegatorCallback(
+                        () -> response.handleViewInteraction(target, params.handler));
+                target.setAllowedHandwritingDelegatePackage(mPendingIntent.getCreatorPackage());
+            } else {
+                target.setHandwritingDelegatorCallback(null);
+                target.setAllowedHandwritingDelegatePackage(null);
+            }
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG;
+        }
+    }
+
     /**
      * Equivalent to calling
      * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
@@ -4189,6 +4241,8 @@
                 return new SetRemoteCollectionItemListAdapterAction(parcel);
             case ATTRIBUTE_REFLECTION_ACTION_TAG:
                 return new AttributeReflectionAction(parcel);
+            case SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG:
+                return new SetOnStylusHandwritingResponse(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
@@ -4742,6 +4796,33 @@
     }
 
     /**
+     * Equivalent to calling {@link View#setHandwritingDelegatorCallback(Runnable)} to send the
+     * provided {@link PendingIntent}.
+     *
+     * <p>A common use case is a remote view which looks like a text editor but does not actually
+     * support text editing itself, and clicking on the remote view launches an activity containing
+     * an EditText. To support handwriting initiation in this case, this method can be called on the
+     * remote view to configure it as a handwriting delegator, meaning that stylus movement on the
+     * remote view triggers a {@link PendingIntent} and starts handwriting mode for the delegate
+     * EditText. The {@link PendingIntent} is typically the same as the one passed to {@link
+     * #setOnClickPendingIntent} which launches the activity containing the EditText. The EditText
+     * should call {@link View#setIsHandwritingDelegate} to set it as a delegate, and also use
+     * {@link View#setAllowedHandwritingDelegatorPackage} or {@link
+     * android.view.inputmethod.InputMethodManager#HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED}
+     * if necessary to support delegators from the package displaying the remote view.
+     *
+     * @param viewId identifier of the view that will trigger the {@link PendingIntent} when a
+     *     stylus {@link MotionEvent} occurs within the view's bounds
+     * @param pendingIntent the {@link PendingIntent} to send, or {@code null} to clear the
+     *     handwriting delegation
+     */
+    @FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
+    public void setOnStylusHandwritingPendingIntent(
+            @IdRes int viewId, @Nullable PendingIntent pendingIntent) {
+        addAction(new SetOnStylusHandwritingResponse(viewId, pendingIntent));
+    }
+
+    /**
      * @hide
      * Equivalent to calling
      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index e10f7c8..478aeec 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -70,20 +70,4 @@
 
     /** Updates a state of camera compat control for stretched issues in the viewfinder. */
     void updateCameraCompatControlState(in WindowContainerToken task, int state);
-
-    /**
-     * Controls whether ignore orientation request logic in {@link
-     * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some
-     * requested orientations to others.
-     *
-     * @param isDisabled when {@code true}, the system always ignores the value of {@link
-     *                   com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} and app
-     *                   requested orientation is respected.
-     * @param fromOrientations The orientations we want to map to the correspondent orientations
-     *                        in toOrientation.
-     * @param toOrientations The orientations we map to the ones in fromOrientations at the same
-     *                       index
-     */
-     void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
-            in int[] fromOrientations, in int[] toOrientations);
 }
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index cd1275c..6d36b57 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -266,31 +266,6 @@
     }
 
     /**
-     * Controls whether ignore orientation request logic in {@link
-     * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some
-     * requested orientation to others.
-     *
-     * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the
-     *           value of  {@link com.android.server.wm.DisplayArea#getIgnoreOrientationRequest}
-     *           and app requested orientation is respected.
-     * @param fromOrientations The orientations we want to map to the correspondent orientations
-     *                        in toOrientation.
-     * @param toOrientations The orientations we map to the ones in fromOrientations at the same
-     *                       index
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
-    public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
-            @Nullable int[] fromOrientations, @Nullable int[] toOrientations) {
-        try {
-            mTaskOrganizerController.setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled,
-                    fromOrientations, toOrientations);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Gets the executor to run callbacks on.
      * @hide
      */
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 7be27be..1bd0982 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -87,10 +87,6 @@
         public static final Flag NOTIF_COOLDOWN_RULE = devFlag(
                 "persist.debug.sysui.notification.notif_cooldown_rule", "rule1");
 
-        /** b/301242692: Visit extra URIs used in notifications to prevent security issues. */
-        public static final Flag VISIT_RISKY_URIS = devFlag(
-                "persist.sysui.notification.visit_risky_uris");
-
         /** b/303716154: For debugging only: use short bitmap duration. */
         public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag(
                 "persist.sysui.notification.debug_short_bitmap_duration");
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1c3fd93..595bf3b 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -152,8 +152,8 @@
                 in String delegatorPackageName);
 
     /** Accepts and starts a stylus handwriting session for the delegate view **/
-    boolean acceptStylusHandwritingDelegation(in IInputMethodClient client,
-                in int userId, in String delegatePackageName, in String delegatorPackageName);
+    boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,
+            in String delegatePackageName, in String delegatorPackageName, int flags);
 
     /** Returns {@code true} if currently selected IME supports Stylus handwriting. */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 4d6ed80..3887dd7 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -268,6 +268,13 @@
 
     optional SettingProto enhanced_voice_privacy_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    message EvenDimmer {
+        optional SettingProto even_dimmer_activated = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto even_dimmer_min_nits = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+
+    optional EvenDimmer even_dimmer = 98;
+
     optional SettingProto font_weight_adjustment = 85 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message Gesture {
@@ -712,5 +719,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 98;
+    // Next tag = 99;
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1d4e01a..ba1f392 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2246,6 +2246,9 @@
     <!-- The default volume for the ring stream -->
     <integer name="config_audio_ring_vol_default">5</integer>
 
+    <!-- The default min volume for the alarm stream -->
+    <integer name="config_audio_alarm_min_vol">1</integer>
+
     <!-- The default value for whether head tracking for
          spatial audio is enabled for a newly connected audio device -->
     <bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
@@ -6830,6 +6833,9 @@
      window that does not wrap content). -->
     <bool name="config_allowFloatingWindowsFillScreen">false</bool>
 
+    <!-- Whether to enable left-right split in portrait on this device -->
+    <bool name="config_leftRightSplitInPortrait">false</bool>
+
     <!-- Whether scroll haptic feedback is enabled for rotary encoder scrolls on
          {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER}
          devices. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4b0fa4b..7787c5d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -287,6 +287,7 @@
   <java-symbol type="integer" name="config_audio_notif_vol_steps" />
   <java-symbol type="integer" name="config_audio_ring_vol_default" />
   <java-symbol type="integer" name="config_audio_ring_vol_steps" />
+  <java-symbol type="integer" name="config_audio_alarm_min_vol" />
   <java-symbol type="bool" name="config_spatial_audio_head_tracking_enabled_default" />
   <java-symbol type="bool" name="config_avoidGfxAccel" />
   <java-symbol type="bool" name="config_bluetooth_address_validation" />
@@ -406,6 +407,7 @@
   <java-symbol type="bool" name="config_supportsMultiWindow" />
   <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
   <java-symbol type="bool" name="config_supportsMultiDisplay" />
+  <java-symbol type="bool" name="config_leftRightSplitInPortrait" />
   <java-symbol type="integer" name="config_supportsNonResizableMultiWindow" />
   <java-symbol type="integer" name="config_respectsActivityMinWidthHeightMultiWindow" />
   <java-symbol type="dimen" name="config_minPercentageMultiWindowSupportHeight" />
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1048742..e7e1740 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -500,7 +500,7 @@
 
 key ESCAPE {
     base:                               none
-    alt, meta:                          fallback HOME
+    alt:                                fallback HOME
     ctrl:                               fallback MENU
 }
 
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 4d2d960..4511f3b 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -50,3 +50,10 @@
     bug: "290220798"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "enable_left_right_split_in_portrait"
+    namespace: "multitasking"
+    description: "Enables left/right split in portrait"
+    bug: "291018646"
+}
diff --git a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
deleted file mode 100644
index d732b01..0000000
--- a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open 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.
--->
-
-<com.android.wm.shell.legacysplitscreen.DividerView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent">
-
-    <View
-        style="@style/DockedDividerBackground"
-        android:id="@+id/docked_divider_background"
-        android:background="@color/split_divider_background"/>
-
-    <com.android.wm.shell.legacysplitscreen.MinimizedDockShadow
-        style="@style/DockedDividerMinimizedShadow"
-        android:id="@+id/minimized_dock_shadow"
-        android:alpha="0"/>
-
-    <com.android.wm.shell.common.split.DividerHandleView
-        style="@style/DockedDividerHandle"
-        android:id="@+id/docked_divider_handle"
-        android:contentDescription="@string/accessibility_divider"
-        android:background="@null"/>
-
-</com.android.wm.shell.legacysplitscreen.DividerView>
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index e3be700..db35c8c 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -24,17 +24,16 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <View
-            style="@style/DockedDividerBackground"
-            android:id="@+id/docked_divider_background"/>
-
         <com.android.wm.shell.common.split.DividerHandleView
-            style="@style/DockedDividerHandle"
             android:id="@+id/docked_divider_handle"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:layout_gravity="center"
             android:contentDescription="@string/accessibility_divider"
             android:background="@null"/>
 
         <com.android.wm.shell.common.split.DividerRoundedCorner
+            android:id="@+id/docked_divider_rounded_corner"
             android:layout_width="match_parent"
             android:layout_height="match_parent"/>
 
diff --git a/libs/WindowManager/Shell/res/values-land/dimens.xml b/libs/WindowManager/Shell/res/values-land/dimens.xml
index a95323f..1b96fa2 100644
--- a/libs/WindowManager/Shell/res/values-land/dimens.xml
+++ b/libs/WindowManager/Shell/res/values-land/dimens.xml
@@ -16,13 +16,6 @@
 */
 -->
 <resources>
-    <!-- Divider handle size for legacy split screen -->
-    <dimen name="docked_divider_handle_width">2dp</dimen>
-    <dimen name="docked_divider_handle_height">16dp</dimen>
-    <!-- Divider handle size for split screen -->
-    <dimen name="split_divider_handle_width">3dp</dimen>
-    <dimen name="split_divider_handle_height">72dp</dimen>
-
     <!-- Padding between status bar and bubbles when displayed in expanded state, smaller
      value in landscape since we have limited vertical space-->
     <dimen name="bubble_padding_top">4dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
deleted file mode 100644
index e89f65b..0000000
--- a/libs/WindowManager/Shell/res/values-land/styles.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="DockedDividerBackground">
-        <item name="android:layout_width">@dimen/split_divider_bar_width</item>
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_gravity">center_horizontal</item>
-        <item name="android:background">@color/split_divider_background</item>
-    </style>
-
-    <style name="DockedDividerHandle">
-        <item name="android:layout_gravity">center</item>
-        <item name="android:layout_width">48dp</item>
-        <item name="android:layout_height">96dp</item>
-    </style>
-
-    <style name="DockedDividerMinimizedShadow">
-        <item name="android:layout_width">8dp</item>
-        <item name="android:layout_height">match_parent</item>
-    </style>
-</resources>
-
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f20d44d..8f9de61 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -96,6 +96,9 @@
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
     <!-- Divider handle size for split screen -->
+    <dimen name="split_divider_handle_region_width">96dp</dimen>
+    <dimen name="split_divider_handle_region_height">48dp</dimen>
+
     <dimen name="split_divider_handle_width">72dp</dimen>
     <dimen name="split_divider_handle_height">3dp</dimen>
 
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 468cfd5..08c2a02 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -60,20 +60,9 @@
 
     <style name="DockedDividerBackground">
         <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">@dimen/split_divider_bar_width</item>
-        <item name="android:layout_gravity">center_vertical</item>
-        <item name="android:background">@color/split_divider_background</item>
-    </style>
-
-    <style name="DockedDividerMinimizedShadow">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">8dp</item>
-    </style>
-
-    <style name="DockedDividerHandle">
+        <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
-        <item name="android:layout_width">96dp</item>
-        <item name="android:layout_height">48dp</item>
+        <item name="android:background">@color/split_divider_background</item>
     </style>
 
     <style name="TvPipEduText">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index ec26800..999da24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -68,24 +68,33 @@
             };
 
     private final Paint mPaint = new Paint();
-    private final int mWidth;
-    private final int mHeight;
-    private final int mTouchingWidth;
-    private final int mTouchingHeight;
+    private int mWidth;
+    private int mHeight;
+    private int mTouchingWidth;
+    private int mTouchingHeight;
     private int mCurrentWidth;
     private int mCurrentHeight;
     private AnimatorSet mAnimator;
     private boolean mTouching;
     private boolean mHovering;
-    private final int mHoveringWidth;
-    private final int mHoveringHeight;
+    private int mHoveringWidth;
+    private int mHoveringHeight;
+    private boolean mIsLeftRightSplit;
 
     public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
         mPaint.setAntiAlias(true);
-        mWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_width);
-        mHeight = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_height);
+        updateDimens();
+    }
+
+    private void updateDimens() {
+        mWidth = getResources().getDimensionPixelSize(mIsLeftRightSplit
+                ? R.dimen.split_divider_handle_height
+                : R.dimen.split_divider_handle_width);
+        mHeight = getResources().getDimensionPixelSize(mIsLeftRightSplit
+                ? R.dimen.split_divider_handle_width
+                : R.dimen.split_divider_handle_height);
         mCurrentWidth = mWidth;
         mCurrentHeight = mHeight;
         mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth;
@@ -94,6 +103,11 @@
         mHoveringHeight = mHeight > mWidth ? ((int) (mHeight * 1.5f)) : mHeight;
     }
 
+    void setIsLeftRightSplit(boolean isLeftRightSplit) {
+        mIsLeftRightSplit = isLeftRightSplit;
+        updateDimens();
+    }
+
     /** Sets touching state for this handle view. */
     public void setTouching(boolean touching, boolean animate) {
         if (touching == mTouching) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
index 364bb65..834c15d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
 import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
 import static android.view.RoundedCorner.POSITION_TOP_LEFT;
@@ -47,6 +46,7 @@
     private InvertedRoundedCornerDrawInfo mTopRightCorner;
     private InvertedRoundedCornerDrawInfo mBottomLeftCorner;
     private InvertedRoundedCornerDrawInfo mBottomRightCorner;
+    private boolean mIsLeftRightSplit;
 
     public DividerRoundedCorner(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -98,8 +98,8 @@
         return false;
     }
 
-    private boolean isLandscape() {
-        return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+    void setIsLeftRightSplit(boolean isLeftRightSplit) {
+        mIsLeftRightSplit = isLeftRightSplit;
     }
 
     /**
@@ -134,7 +134,7 @@
         }
 
         private void calculateStartPos(Point outPos) {
-            if (isLandscape()) {
+            if (mIsLeftRightSplit) {
                 // Place left corner at the right side of the divider bar.
                 outPos.x = isLeftCorner()
                         ? getWidth() / 2 + mDividerWidth / 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 0b0c693..0f0fbd9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
@@ -27,6 +26,8 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.DeviceConfig;
@@ -65,12 +66,15 @@
     public static final long TOUCH_ANIMATION_DURATION = 150;
     public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
 
+    private final Paint mPaint = new Paint();
+    private final Rect mBackgroundRect = new Rect();
     private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
     private SplitLayout mSplitLayout;
     private SplitWindowManager mSplitWindowManager;
     private SurfaceControlViewHost mViewHost;
     private DividerHandleView mHandle;
+    private DividerRoundedCorner mCorners;
     private View mBackground;
     private int mTouchElevation;
 
@@ -81,6 +85,8 @@
     private boolean mInteractive;
     private boolean mSetTouchRegion = true;
     private int mLastDraggingPosition;
+    private int mHandleRegionWidth;
+    private int mHandleRegionHeight;
 
     /**
      * Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
@@ -123,7 +129,7 @@
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
             final DividerSnapAlgorithm snapAlgorithm = mSplitLayout.mDividerSnapAlgorithm;
-            if (isLandscape()) {
+            if (mSplitLayout.isLeftRightSplit()) {
                 info.addAction(new AccessibilityAction(R.id.action_move_tl_full,
                         mContext.getString(R.string.accessibility_action_divider_left_full)));
                 if (snapAlgorithm.isFirstSplitTargetAvailable()) {
@@ -215,6 +221,17 @@
         mViewHost = viewHost;
         layout.getDividerBounds(mDividerBounds);
         onInsetsChanged(insetsState, false /* animate */);
+
+        final boolean isLeftRightSplit = mSplitLayout.isLeftRightSplit();
+        mHandle.setIsLeftRightSplit(isLeftRightSplit);
+        mCorners.setIsLeftRightSplit(isLeftRightSplit);
+
+        mHandleRegionWidth = getResources().getDimensionPixelSize(isLeftRightSplit
+                ? R.dimen.split_divider_handle_region_height
+                : R.dimen.split_divider_handle_region_width);
+        mHandleRegionHeight = getResources().getDimensionPixelSize(isLeftRightSplit
+                ? R.dimen.split_divider_handle_region_width
+                : R.dimen.split_divider_handle_region_height);
     }
 
     void onInsetsChanged(InsetsState insetsState, boolean animate) {
@@ -255,30 +272,47 @@
         super.onFinishInflate();
         mDividerBar = findViewById(R.id.divider_bar);
         mHandle = findViewById(R.id.docked_divider_handle);
-        mBackground = findViewById(R.id.docked_divider_background);
+        mCorners = findViewById(R.id.docked_divider_rounded_corner);
         mTouchElevation = getResources().getDimensionPixelSize(
                 R.dimen.docked_stack_divider_lift_elevation);
         mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
         mInteractive = true;
         setOnTouchListener(this);
         mHandle.setAccessibilityDelegate(mHandleDelegate);
+        setWillNotDraw(false);
+        mPaint.setColor(getResources().getColor(R.color.split_divider_background, null));
+        mPaint.setAntiAlias(true);
+        mPaint.setStyle(Paint.Style.FILL);
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         if (mSetTouchRegion) {
-            mTempRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(),
-                    mHandle.getBottom());
+            int startX = (mDividerBounds.width() - mHandleRegionWidth) / 2;
+            int startY = (mDividerBounds.height() - mHandleRegionHeight) / 2;
+            mTempRect.set(startX, startY, startX + mHandleRegionWidth,
+                    startY + mHandleRegionHeight);
             mSplitWindowManager.setTouchRegion(mTempRect);
             mSetTouchRegion = false;
         }
+
+        if (changed) {
+            boolean isHorizontalSplit = mSplitLayout.isLeftRightSplit();
+            int dividerSize = getResources().getDimensionPixelSize(R.dimen.split_divider_bar_width);
+            left = isHorizontalSplit ? (getWidth() - dividerSize) / 2 : 0;
+            top = isHorizontalSplit ? 0 : (getHeight() - dividerSize) / 2;
+            right = isHorizontalSplit ? left + dividerSize : getWidth();
+            bottom = isHorizontalSplit ? getHeight() : top + dividerSize;
+            mBackgroundRect.set(left, top, right, bottom);
+        }
     }
 
     @Override
     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
         return PointerIcon.getSystemIcon(getContext(),
-                isLandscape() ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW);
+                mSplitLayout.isLeftRightSplit() ? TYPE_HORIZONTAL_DOUBLE_ARROW
+                        : TYPE_VERTICAL_DOUBLE_ARROW);
     }
 
     @Override
@@ -295,8 +329,8 @@
         // moving divider bar and calculating dragging velocity.
         event.setLocation(event.getRawX(), event.getRawY());
         final int action = event.getAction() & MotionEvent.ACTION_MASK;
-        final boolean isLandscape = isLandscape();
-        final int touchPos = (int) (isLandscape ? event.getX() : event.getY());
+        final boolean isLeftRightSplit = mSplitLayout.isLeftRightSplit();
+        final int touchPos = (int) (isLeftRightSplit ? event.getX() : event.getY());
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mVelocityTracker = VelocityTracker.obtain();
@@ -328,7 +362,7 @@
 
                 mVelocityTracker.addMovement(event);
                 mVelocityTracker.computeCurrentVelocity(1000 /* units */);
-                final float velocity = isLandscape
+                final float velocity = isLeftRightSplit
                         ? mVelocityTracker.getXVelocity()
                         : mVelocityTracker.getYVelocity();
                 final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
@@ -410,6 +444,11 @@
                 .start();
     }
 
+    @Override
+    protected void onDraw(@NonNull Canvas canvas) {
+        canvas.drawRect(mBackgroundRect, mPaint);
+    }
+
     @VisibleForTesting
     void releaseHovering() {
         mHandle.setHovering(false, true);
@@ -446,10 +485,6 @@
         mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE);
     }
 
-    private boolean isLandscape() {
-        return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
-    }
-
     private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
         @Override
         public boolean onDoubleTap(MotionEvent e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 63cdb4f..b699533 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -79,7 +79,7 @@
  * divide position changes.
  */
 public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
-
+    private static final String TAG = "SplitLayout";
     public static final int PARALLAX_NONE = 0;
     public static final int PARALLAX_DISMISSING = 1;
     public static final int PARALLAX_ALIGN_CENTER = 2;
@@ -121,12 +121,15 @@
     private int mDividerPosition;
     private boolean mInitialized = false;
     private boolean mFreezeDividerWindow = false;
+    private boolean mIsLargeScreen = false;
     private int mOrientation;
     private int mRotation;
     private int mDensity;
     private int mUiMode;
 
     private final boolean mDimNonImeSide;
+    private final boolean mAllowLeftRightSplitInPortrait;
+    private boolean mIsLeftRightSplit;
     private ValueAnimator mDividerFlingAnimator;
 
     public SplitLayout(String windowName, Context context, Configuration configuration,
@@ -138,6 +141,7 @@
         mOrientation = configuration.orientation;
         mRotation = configuration.windowConfiguration.getRotation();
         mDensity = configuration.densityDpi;
+        mIsLargeScreen = configuration.smallestScreenWidthDp >= 600;
         mSplitLayoutHandler = splitLayoutHandler;
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
@@ -147,14 +151,17 @@
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
         mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
 
+        final Resources res = mContext.getResources();
+        mDimNonImeSide = res.getBoolean(R.bool.config_dimNonImeAttachedSide);
+        mAllowLeftRightSplitInPortrait = SplitScreenUtils.allowLeftRightSplitInPortrait(res);
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                configuration);
+
         updateDividerConfig(mContext);
 
         mRootBounds.set(configuration.windowConfiguration.getBounds());
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
         resetDividerPosition();
-
-        mDimNonImeSide = mContext.getResources().getBoolean(R.bool.config_dimNonImeAttachedSide);
-
         updateInvisibleRect();
     }
 
@@ -284,17 +291,17 @@
      * Returns the divider position as a fraction from 0 to 1.
      */
     public float getDividerPositionAsFraction() {
-        return Math.min(1f, Math.max(0f, isLandscape()
+        return Math.min(1f, Math.max(0f, mIsLeftRightSplit
                 ? (float) ((mBounds1.right + mBounds2.left) / 2f) / mBounds2.right
                 : (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom));
     }
 
     private void updateInvisibleRect() {
         mInvisibleBounds.set(mRootBounds.left, mRootBounds.top,
-                isLandscape() ? mRootBounds.right / 2 : mRootBounds.right,
-                isLandscape() ? mRootBounds.bottom : mRootBounds.bottom / 2);
-        mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
-                isLandscape() ? 0 : mRootBounds.bottom);
+                mIsLeftRightSplit ? mRootBounds.right / 2 : mRootBounds.right,
+                mIsLeftRightSplit ? mRootBounds.bottom : mRootBounds.bottom / 2);
+        mInvisibleBounds.offset(mIsLeftRightSplit ? mRootBounds.right : 0,
+                mIsLeftRightSplit ? 0 : mRootBounds.bottom);
     }
 
     /** Applies new configuration, returns {@code false} if there's no effect to the layout. */
@@ -309,6 +316,7 @@
         final int orientation = configuration.orientation;
         final int density = configuration.densityDpi;
         final int uiMode = configuration.uiMode;
+        final boolean wasLeftRightSplit = mIsLeftRightSplit;
 
         if (mOrientation == orientation
                 && mRotation == rotation
@@ -326,9 +334,12 @@
         mRotation = rotation;
         mDensity = density;
         mUiMode = uiMode;
+        mIsLargeScreen = configuration.smallestScreenWidthDp >= 600;
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                configuration);
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
         updateDividerConfig(mContext);
-        initDividerPosition(mTempRect);
+        initDividerPosition(mTempRect, wasLeftRightSplit);
         updateInvisibleRect();
 
         return true;
@@ -347,18 +358,27 @@
         }
 
         // We only need new bounds here, other configuration should be update later.
+        final boolean wasLeftRightSplit = SplitScreenUtils.isLeftRightSplit(
+                mAllowLeftRightSplitInPortrait, mIsLargeScreen,
+                mRootBounds.width() >= mRootBounds.height());
         mTempRect.set(mRootBounds);
         mRootBounds.set(tmpRect);
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                mIsLargeScreen, mRootBounds.width() >= mRootBounds.height());
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
-        initDividerPosition(mTempRect);
+        initDividerPosition(mTempRect, wasLeftRightSplit);
     }
 
-    private void initDividerPosition(Rect oldBounds) {
+    /**
+     * Updates the divider position to the position in the current orientation and bounds using the
+     * snap fraction calculated based on the previous orientation and bounds.
+     */
+    private void initDividerPosition(Rect oldBounds, boolean wasLeftRightSplit) {
         final float snapRatio = (float) mDividerPosition
-                / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
+                / (float) (wasLeftRightSplit ? oldBounds.width() : oldBounds.height());
         // Estimate position by previous ratio.
         final float length =
-                (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
+                (float) (mIsLeftRightSplit ? mRootBounds.width() : mRootBounds.height());
         final int estimatePosition = (int) (length * snapRatio);
         // Init divider position by estimated position using current bounds snap algorithm.
         mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
@@ -376,8 +396,7 @@
         dividerBounds.set(mRootBounds);
         bounds1.set(mRootBounds);
         bounds2.set(mRootBounds);
-        final boolean isLandscape = isLandscape(mRootBounds);
-        if (isLandscape) {
+        if (mIsLeftRightSplit) {
             position += mRootBounds.left;
             dividerBounds.left = position - mDividerInsets;
             dividerBounds.right = dividerBounds.left + mDividerWindowWidth;
@@ -393,7 +412,7 @@
         DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
         DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
         if (setEffectBounds) {
-            mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
+            mSurfaceEffectPolicy.applyDividerPosition(position, mIsLeftRightSplit);
         }
     }
 
@@ -563,13 +582,12 @@
     }
 
     private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
-        final boolean isLandscape = isLandscape(rootBounds);
         final Rect insets = getDisplayStableInsets(context);
 
         // Make split axis insets value same as the larger one to avoid bounds1 and bounds2
         // have difference for avoiding size-compat mode when switching unresizable apps in
         // landscape while they are letterboxed.
-        if (!isLandscape) {
+        if (!mIsLeftRightSplit) {
             final int largerInsets = Math.max(insets.top, insets.bottom);
             insets.set(insets.left, largerInsets, insets.right, largerInsets);
         }
@@ -579,9 +597,9 @@
                 rootBounds.width(),
                 rootBounds.height(),
                 mDividerSize,
-                !isLandscape,
+                !mIsLeftRightSplit,
                 insets,
-                isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
+                mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
     }
 
     /** Fling divider from current position to end or start position then exit */
@@ -643,13 +661,12 @@
     /** Switch both surface position with animation. */
     public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
             SurfaceControl leash2, Consumer<Rect> finishCallback) {
-        final boolean isLandscape = isLandscape();
         final Rect insets = getDisplayStableInsets(mContext);
-        insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top,
-                isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom);
+        insets.set(mIsLeftRightSplit ? insets.left : 0, mIsLeftRightSplit ? 0 : insets.top,
+                mIsLeftRightSplit ? insets.right : 0, mIsLeftRightSplit ? 0 : insets.bottom);
 
         final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
-                isLandscape ? mBounds2.width() : mBounds2.height()).position;
+                mIsLeftRightSplit ? mBounds2.width() : mBounds2.height()).position;
         final Rect distBounds1 = new Rect();
         final Rect distBounds2 = new Rect();
         final Rect distDividerBounds = new Rect();
@@ -740,15 +757,12 @@
                         .toRect();
     }
 
-    private static boolean isLandscape(Rect bounds) {
-        return bounds.width() > bounds.height();
-    }
-
     /**
-     * Return if this layout is landscape.
+     * @return {@code true} if we should create a left-right split, {@code false} if we should
+     * create a top-bottom split.
      */
-    public boolean isLandscape() {
-        return isLandscape(mRootBounds);
+    public boolean isLeftRightSplit() {
+        return mIsLeftRightSplit;
     }
 
     /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
@@ -850,9 +864,13 @@
 
     /** Dumps the current split bounds recorded in this layout. */
     public void dump(@NonNull PrintWriter pw, String prefix) {
-        pw.println(prefix + "bounds1=" + mBounds1.toShortString());
-        pw.println(prefix + "dividerBounds=" + mDividerBounds.toShortString());
-        pw.println(prefix + "bounds2=" + mBounds2.toShortString());
+        final String innerPrefix = prefix + "\t";
+        pw.println(prefix + TAG + ":");
+        pw.println(innerPrefix + "mAllowLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait);
+        pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit);
+        pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString());
+        pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString());
+        pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString());
     }
 
     /** Handles layout change event. */
@@ -937,32 +955,32 @@
          * Applies a parallax to the task to hint dismissing progress.
          *
          * @param position    the split position to apply dismissing parallax effect
-         * @param isLandscape indicates whether it's splitting horizontally or vertically
+         * @param isLeftRightSplit indicates whether it's splitting horizontally or vertically
          */
-        void applyDividerPosition(int position, boolean isLandscape) {
+        void applyDividerPosition(int position, boolean isLeftRightSplit) {
             mDismissingSide = DOCKED_INVALID;
             mParallaxOffset.set(0, 0);
             mDismissingDimValue = 0;
 
             int totalDismissingDistance = 0;
             if (position < mDividerSnapAlgorithm.getFirstSplitTarget().position) {
-                mDismissingSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                mDismissingSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
                 totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
                         - mDividerSnapAlgorithm.getFirstSplitTarget().position;
             } else if (position > mDividerSnapAlgorithm.getLastSplitTarget().position) {
-                mDismissingSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                mDismissingSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
                 totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
                         - mDividerSnapAlgorithm.getDismissEndTarget().position;
             }
 
-            final boolean topLeftShrink = isLandscape
+            final boolean topLeftShrink = isLeftRightSplit
                     ? position < mWinBounds1.right : position < mWinBounds1.bottom;
             if (topLeftShrink) {
-                mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
                 mContentBounds.set(mWinBounds1);
                 mSurfaceBounds.set(mBounds1);
             } else {
-                mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
                 mContentBounds.set(mWinBounds2);
                 mSurfaceBounds.set(mBounds2);
             }
@@ -973,7 +991,7 @@
                 mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
                 if (mParallaxType == PARALLAX_DISMISSING) {
                     fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
-                    if (isLandscape) {
+                    if (isLeftRightSplit) {
                         mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
                     } else {
                         mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
@@ -982,7 +1000,7 @@
             }
 
             if (mParallaxType == PARALLAX_ALIGN_CENTER) {
-                if (isLandscape) {
+                if (isLeftRightSplit) {
                     mParallaxOffset.x =
                             (mSurfaceBounds.width() - mContentBounds.width()) / 2;
                 } else {
@@ -1129,7 +1147,7 @@
             // Calculate target bounds offset for IME
             mLastYOffset = mYOffsetForIme;
             final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
-                    && !isFloating && !isLandscape(mRootBounds) && mImeShown;
+                    && !isFloating && !mIsLeftRightSplit && mImeShown;
             mTargetYOffset = needOffset ? getTargetYOffset() : 0;
 
             if (mTargetYOffset != mLastYOffset) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index d7ea1c0..0693543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.common.split;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -25,9 +27,14 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 
 /** Helper utility class for split screen components to use. */
@@ -94,4 +101,38 @@
     public static String splitFailureMessage(String caller, String reason) {
         return "(" + caller + ") Splitscreen aborted: " + reason;
     }
+
+    /**
+     * Returns whether left/right split is allowed in portrait.
+     */
+    public static boolean allowLeftRightSplitInPortrait(Resources res) {
+        return Flags.enableLeftRightSplitInPortrait() && res.getBoolean(
+                com.android.internal.R.bool.config_leftRightSplitInPortrait);
+    }
+
+    /**
+     * Returns whether left/right split is supported in the given configuration.
+     */
+    public static boolean isLeftRightSplit(boolean allowLeftRightSplitInPortrait,
+            Configuration config) {
+        // Compare the max bounds sizes as on near-square devices, the insets may result in a
+        // configuration in the other orientation
+        final boolean isLargeScreen = config.smallestScreenWidthDp >= 600;
+        final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+        final boolean isLandscape = maxBounds.width() >= maxBounds.height();
+        return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen, isLandscape);
+    }
+
+    /**
+     * Returns whether left/right split is supported in the given configuration state. This method
+     * is useful for cases where we need to calculate this given last saved state.
+     */
+    public static boolean isLeftRightSplit(boolean allowLeftRightSplitInPortrait,
+            boolean isLargeScreen, boolean isLandscape) {
+        if (allowLeftRightSplitInPortrait && isLargeScreen) {
+            return !isLandscape;
+        } else {
+            return isLandscape;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 0bf8ec3..fdfb6f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -94,6 +94,7 @@
     private ShellExecutor mMainExecutor;
     private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();
 
+    // Map of displayId -> per-display info
     private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
 
     /**
@@ -362,7 +363,7 @@
      */
     private boolean isReadyToHandleDrag() {
         for (int i = 0; i < mDisplayDropTargets.size(); i++) {
-            if (mDisplayDropTargets.valueAt(i).mHasDrawn) {
+            if (mDisplayDropTargets.valueAt(i).hasDrawn) {
                 return true;
             }
         }
@@ -398,8 +399,13 @@
      * Dumps information about this controller.
      */
     public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
-        pw.println(prefix + " listeners=" + mListeners.size());
+        pw.println(innerPrefix + "listeners=" + mListeners.size());
+        pw.println(innerPrefix + "Per display:");
+        for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+            mDisplayDropTargets.valueAt(i).dump(pw, innerPrefix);
+        }
     }
     
     /**
@@ -440,7 +446,7 @@
         final FrameLayout rootView;
         final DragLayout dragLayout;
         // Tracks whether the window has fully drawn since it was last made visible
-        boolean mHasDrawn;
+        boolean hasDrawn;
 
         boolean isHandlingDrag;
         // A count of the number of active drags in progress to ensure that we only hide the window
@@ -464,17 +470,29 @@
             rootView.setVisibility(visibility);
             if (visibility == View.VISIBLE) {
                 rootView.requestApplyInsets();
-                if (!mHasDrawn && rootView.getViewRootImpl() != null) {
+                if (!hasDrawn && rootView.getViewRootImpl() != null) {
                     rootView.getViewRootImpl().registerRtFrameCallback(this);
                 }
             } else {
-                mHasDrawn = false;
+                hasDrawn = false;
             }
         }
 
         @Override
         public void onFrameDraw(long frame) {
-            mHasDrawn = true;
+            hasDrawn = true;
+        }
+
+        /**
+         * Dumps information about this display's shell drop target.
+         */
+        public void dump(@NonNull PrintWriter pw, String prefix) {
+            final String innerPrefix = prefix + "  ";
+            pw.println(innerPrefix + "displayId=" + displayId);
+            pw.println(innerPrefix + "hasDrawn=" + hasDrawn);
+            pw.println(innerPrefix + "isHandlingDrag=" + isHandlingDrag);
+            pw.println(innerPrefix + "activeDragCount=" + activeDragCount);
+            dragLayout.dump(pw, innerPrefix);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index e70768b..162ce19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -138,7 +138,7 @@
         final Rect displayRegion = new Rect(l, t, l + iw, t + ih);
         final Rect fullscreenDrawRegion = new Rect(displayRegion);
         final Rect fullscreenHitRegion = new Rect(displayRegion);
-        final boolean inLandscape = mSession.displayLayout.isLandscape();
+        final boolean isLeftRightSplit = mSplitScreen != null && mSplitScreen.isLeftRightSplit();
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
         final float dividerWidth = mContext.getResources().getDimensionPixelSize(
                 R.dimen.split_divider_bar_width);
@@ -155,7 +155,7 @@
             topOrLeftBounds.intersect(displayRegion);
             bottomOrRightBounds.intersect(displayRegion);
 
-            if (inLandscape) {
+            if (isLeftRightSplit) {
                 final Rect leftHitRegion = new Rect();
                 final Rect rightHitRegion = new Rect();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 205a455..445ba89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -47,14 +48,18 @@
 import android.view.WindowInsets.Type;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -74,6 +79,11 @@
     private final StatusBarManager mStatusBarManager;
     private final Configuration mLastConfiguration = new Configuration();
 
+    // Whether this device supports left/right split in portrait
+    private final boolean mAllowLeftRightSplitInPortrait;
+    // Whether the device is currently in left/right split mode
+    private boolean mIsLeftRightSplit;
+
     private DragAndDropPolicy.Target mCurrentTarget = null;
     private DropZoneView mDropZoneView1;
     private DropZoneView mDropZoneView2;
@@ -106,17 +116,18 @@
         setLayoutDirection(LAYOUT_DIRECTION_LTR);
         mDropZoneView1 = new DropZoneView(context);
         mDropZoneView2 = new DropZoneView(context);
-        addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT,
-                MATCH_PARENT));
-        addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT,
-                MATCH_PARENT));
+        addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         ((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
         ((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
-        int orientation = getResources().getConfiguration().orientation;
-        setOrientation(orientation == Configuration.ORIENTATION_LANDSCAPE
-                ? LinearLayout.HORIZONTAL
-                : LinearLayout.VERTICAL);
-        updateContainerMargins(getResources().getConfiguration().orientation);
+        // We don't use the configuration orientation here to determine landscape because
+        // near-square devices may report the same orietation with insets taken into account
+        mAllowLeftRightSplitInPortrait = SplitScreenUtils.allowLeftRightSplitInPortrait(
+                context.getResources());
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                getResources().getConfiguration());
+        setOrientation(mIsLeftRightSplit ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+        updateContainerMargins(mIsLeftRightSplit);
     }
 
     @Override
@@ -124,11 +135,12 @@
         mInsets = insets.getInsets(Type.tappableElement() | Type.displayCutout());
         recomputeDropTargets();
 
-        final int orientation = getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+        boolean isLeftRightSplit = mSplitScreenController != null
+                && mSplitScreenController.isLeftRightSplit();
+        if (isLeftRightSplit) {
             mDropZoneView1.setBottomInset(mInsets.bottom);
             mDropZoneView2.setBottomInset(mInsets.bottom);
-        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+        } else {
             mDropZoneView1.setBottomInset(0);
             mDropZoneView2.setBottomInset(mInsets.bottom);
         }
@@ -136,14 +148,12 @@
     }
 
     public void onConfigChanged(Configuration newConfig) {
-        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
-                && getOrientation() != HORIZONTAL) {
-            setOrientation(LinearLayout.HORIZONTAL);
-            updateContainerMargins(newConfig.orientation);
-        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                && getOrientation() != VERTICAL) {
-            setOrientation(LinearLayout.VERTICAL);
-            updateContainerMargins(newConfig.orientation);
+        boolean isLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                newConfig);
+        if (isLeftRightSplit != mIsLeftRightSplit) {
+            mIsLeftRightSplit = isLeftRightSplit;
+            setOrientation(mIsLeftRightSplit ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+            updateContainerMargins(mIsLeftRightSplit);
         }
 
         final int diff = newConfig.diff(mLastConfiguration);
@@ -162,14 +172,14 @@
         mDropZoneView2.setContainerMargin(0, 0, 0, 0);
     }
 
-    private void updateContainerMargins(int orientation) {
+    private void updateContainerMargins(boolean isLeftRightSplit) {
         final float halfMargin = mDisplayMargin / 2f;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+        if (isLeftRightSplit) {
             mDropZoneView1.setContainerMargin(
                     mDisplayMargin, mDisplayMargin, halfMargin, mDisplayMargin);
             mDropZoneView2.setContainerMargin(
                     halfMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
-        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+        } else {
             mDropZoneView1.setContainerMargin(
                     mDisplayMargin, mDisplayMargin, mDisplayMargin, halfMargin);
             mDropZoneView2.setContainerMargin(
@@ -257,23 +267,21 @@
      * @param bounds2 bounds to apply to the second dropzone view, null if split in half.
      */
     private void updateDropZoneSizes(Rect bounds1, Rect bounds2) {
-        final int orientation = getResources().getConfiguration().orientation;
-        final boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;
         final int halfDivider = mDividerSize / 2;
         final LinearLayout.LayoutParams dropZoneView1 =
                 (LayoutParams) mDropZoneView1.getLayoutParams();
         final LinearLayout.LayoutParams dropZoneView2 =
                 (LayoutParams) mDropZoneView2.getLayoutParams();
-        if (isPortrait) {
-            dropZoneView1.width = MATCH_PARENT;
-            dropZoneView2.width = MATCH_PARENT;
-            dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
-            dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
-        } else {
+        if (mIsLeftRightSplit) {
             dropZoneView1.width = bounds1 != null ? bounds1.width() + halfDivider : MATCH_PARENT;
             dropZoneView2.width = bounds2 != null ? bounds2.width() + halfDivider : MATCH_PARENT;
             dropZoneView1.height = MATCH_PARENT;
             dropZoneView2.height = MATCH_PARENT;
+        } else {
+            dropZoneView1.width = MATCH_PARENT;
+            dropZoneView2.width = MATCH_PARENT;
+            dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
+            dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
         }
         dropZoneView1.weight = bounds1 != null ? 0 : 1;
         dropZoneView2.weight = bounds2 != null ? 0 : 1;
@@ -371,7 +379,7 @@
         // Reset the state if we previously force-ignore the bottom margin
         mDropZoneView1.setForceIgnoreBottomMargin(false);
         mDropZoneView2.setForceIgnoreBottomMargin(false);
-        updateContainerMargins(getResources().getConfiguration().orientation);
+        updateContainerMargins(mIsLeftRightSplit);
         mCurrentTarget = null;
     }
 
@@ -481,4 +489,19 @@
         final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
         return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
     }
+
+    /**
+     * Dumps information about this drag layout.
+     */
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + "DragLayout:");
+        pw.println(innerPrefix + "mIsLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait);
+        pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit);
+        pw.println(innerPrefix + "mDisplayMargin=" + mDisplayMargin);
+        pw.println(innerPrefix + "mDividerSize=" + mDividerSize);
+        pw.println(innerPrefix + "mIsShowing=" + mIsShowing);
+        pw.println(innerPrefix + "mHasDropped=" + mHasDropped);
+        pw.println(innerPrefix + "mCurrentTarget=" + mCurrentTarget);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index 478b6a9..353d702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -18,31 +18,17 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.ClipDescription.EXTRA_PENDING_INTENT;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
-import static android.content.Intent.EXTRA_USER;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
 import android.app.WindowConfiguration;
 import android.content.ClipData;
-import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.net.Uri;
-import android.os.UserHandle;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 
 import com.android.wm.shell.common.DisplayLayout;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b4067d0..fdd3044 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -123,7 +123,7 @@
 
     private static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS =
             SystemProperties.getInt(
-                    "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 0);
+                    "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 450);
 
     private final Context mContext;
     private final SyncTransactionQueue mSyncTransactionQueue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 63afd3e..79c2076 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -590,7 +590,7 @@
                 cancel("transit_sleep");
                 return;
             }
-            if (mKeyguardLocked) {
+            if (mKeyguardLocked || (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                         "[%d] RecentsController.merge: keyguard is locked", mInstanceId);
                 // We will not accept new changes if we are swiping over the keyguard.
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 664d4491..37b24e5 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
@@ -366,6 +366,14 @@
         return mStageCoordinator.getStageOfTask(taskId);
     }
 
+    /**
+     * @return {@code true} if we should create a left-right split, {@code false} if we should
+     * create a top-bottom split.
+     */
+    public boolean isLeftRightSplit() {
+        return mStageCoordinator.isLeftRightSplit();
+    }
+
     /** Check split is foreground and task is under split or not by taskId. */
     public boolean isTaskInSplitScreenForeground(int taskId) {
         return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
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 7a4834c..36e0eb4 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
@@ -1301,7 +1301,7 @@
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
         mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                 getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                mSplitLayout.isLandscape());
+                mSplitLayout.isLeftRightSplit());
     }
 
     void setSideStagePosition(@SplitPosition int sideStagePosition,
@@ -1659,7 +1659,7 @@
             mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
                     getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                     getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                    mSplitLayout.isLandscape());
+                    mSplitLayout.isLeftRightSplit());
         }
     }
 
@@ -1749,10 +1749,10 @@
         }
         if (stage == STAGE_TYPE_MAIN) {
             mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
-                    mSplitLayout.isLandscape());
+                    mSplitLayout.isLeftRightSplit());
         } else {
             mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                    mSplitLayout.isLandscape());
+                    mSplitLayout.isLeftRightSplit());
         }
         if (present) {
             updateRecentTasksSplitPair();
@@ -2113,7 +2113,7 @@
                 mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
                         getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                         getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                        mSplitLayout.isLandscape());
+                        mSplitLayout.isLeftRightSplit());
             }
         }
     }
@@ -2205,8 +2205,12 @@
         mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
     }
 
-    private boolean isLandscape() {
-        return mSplitLayout.isLandscape();
+    /**
+     * @return {@code true} if we should create a left-right split, {@code false} if we should
+     * create a top-bottom split.
+     */
+    boolean isLeftRightSplit() {
+        return mSplitLayout.isLeftRightSplit();
     }
 
     /**
@@ -3177,6 +3181,7 @@
         pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible);
         pw.println(innerPrefix + "isSplitActive=" + isSplitActive());
         pw.println(innerPrefix + "isSplitVisible=" + isSplitScreenVisible());
+        pw.println(innerPrefix + "isLeftRightSplit=" + mSplitLayout.isLeftRightSplit());
         pw.println(innerPrefix + "MainStage");
         pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
         pw.println(childPrefix + "isActive=" + mMainStage.isActive());
@@ -3188,10 +3193,7 @@
         mSideStage.dump(pw, childPrefix);
         pw.println(innerPrefix + "SideStageListener");
         mSideStageListener.dump(pw, childPrefix);
-        if (mMainStage.isActive()) {
-            pw.println(innerPrefix + "SplitLayout");
-            mSplitLayout.dump(pw, childPrefix);
-        }
+        mSplitLayout.dump(pw, childPrefix);
         if (!mPausingTasks.isEmpty()) {
             pw.println(childPrefix + "mPausingTasks=" + mPausingTasks);
         }
@@ -3243,7 +3245,7 @@
         mLogger.logExit(exitReason,
                 SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */,
                 SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */,
-                mSplitLayout.isLandscape());
+                mSplitLayout.isLeftRightSplit());
     }
 
     /**
@@ -3256,7 +3258,7 @@
                 toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
                 !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED,
                 !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
-                mSplitLayout.isLandscape());
+                mSplitLayout.isLeftRightSplit());
     }
 
     class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 366f7b1..4abaf5b 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -52,7 +52,7 @@
 }
 
 java_defaults {
-    name: "WMShellFlickerTestsDefaultWithoutTemplate",
+    name: "WMShellFlickerTestsDefault",
     platform_apis: true,
     certificate: "platform",
     optimize: {
@@ -75,16 +75,9 @@
     ],
     data: [
         ":FlickerTestApp",
-        "trace_config/*",
     ],
 }
 
-java_defaults {
-    name: "WMShellFlickerTestsDefault",
-    defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"],
-    test_config_template: "AndroidTestTemplate.xml",
-}
-
 java_library {
     name: "WMShellFlickerTestsBase",
     defaults: ["WMShellFlickerTestsDefault"],
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
deleted file mode 100644
index b00d88e..0000000
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
+++ /dev/null
@@ -1,112 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
-    <option name="test-tag" value="FlickerTests"/>
-    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
-    <option name="isolated-storage" value="false"/>
-
-    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
-        <!-- keeps the screen on during tests -->
-        <option name="screen-always-on" value="on"/>
-        <!-- prevents the phone from restarting -->
-        <option name="force-skip-system-props" value="true"/>
-        <!-- set WM tracing verbose level to all -->
-        <option name="run-command" value="cmd window tracing level all"/>
-        <!-- set WM tracing to frame (avoid incomplete states) -->
-        <option name="run-command" value="cmd window tracing frame"/>
-        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
-        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
-        <!-- ensure lock screen mode is swipe -->
-        <option name="run-command" value="locksettings set-disabled false"/>
-        <!-- restart launcher to activate TAPL -->
-        <option name="run-command"
-                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
-        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
-        <option name="run-command" value="cmd window tracing size 20480"/>
-        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
-        <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
-        <option name="run-command"
-                value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="test-user-token" value="%TEST_USER%"/>
-        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
-        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
-        <option name="run-command" value="settings put system show_touches 1"/>
-        <option name="run-command" value="settings put system pointer_location 1"/>
-        <option name="teardown-command"
-                value="settings delete secure show_ime_with_hard_keyboard"/>
-        <option name="teardown-command" value="settings delete system show_touches"/>
-        <option name="teardown-command" value="settings delete system pointer_location"/>
-        <option name="teardown-command"
-                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true"/>
-        <option name="test-file-name" value="{MODULE}.apk"/>
-        <option name="test-file-name" value="FlickerTestApp.apk"/>
-    </target_preparer>
-    <!-- Enable mocking GPS location by the test app -->
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command"
-                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
-        <option name="teardown-command"
-                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
-    </target_preparer>
-
-    <!-- Needed for pushing the trace config file -->
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="push-file"
-                key="trace_config.textproto"
-                value="/data/misc/perfetto-traces/trace_config.textproto"
-        />
-        <!--Install the content provider automatically when we push some file in sdcard folder.-->
-        <!--Needed to avoid the installation during the test suite.-->
-        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="{PACKAGE}"/>
-        <option name="shell-timeout" value="6600s"/>
-        <option name="test-timeout" value="6000s"/>
-        <option name="hidden-api-checks" value="false"/>
-        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
-        <!-- PerfettoListener related arguments -->
-        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
-        <option name="instrumentation-arg"
-                key="perfetto_config_file"
-                value="trace_config.textproto"
-        />
-        <option name="instrumentation-arg" key="per_run" value="true"/>
-    </test>
-    <!-- Needed for pulling the collected trace config on to the host -->
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="pull-pattern-keys" value="perfetto_file_path"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
-        <option name="collect-on-run-ended-only" value="true"/>
-        <option name="clean-up" value="true"/>
-    </metrics_collector>
-</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
index bae701f..e151ab2 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
@@ -36,6 +36,8 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker",
     instrumentation_target_package: "com.android.wm.shell.flicker",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [":WMShellFlickerTestsAppCompat-src"],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
index c4e9a84..f0b4f1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
@@ -29,6 +29,8 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.bubbles",
     instrumentation_target_package: "com.android.wm.shell.flicker.bubbles",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: ["src/**/*.kt"],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index b9b56c2..e61f762 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -62,11 +62,13 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.pip",
     instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsPip1-src",
         ":WMShellFlickerTestsPipCommon-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
 
 android_test {
@@ -75,11 +77,13 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.pip",
     instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsPip2-src",
         ":WMShellFlickerTestsPipCommon-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
 
 android_test {
@@ -88,6 +92,7 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.pip",
     instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsPip3-src",
         ":WMShellFlickerTestsPipCommon-src",
@@ -98,6 +103,7 @@
         ":WMShellFlickerTestsPipApps-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
 
 android_test {
@@ -106,19 +112,22 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.pip.apps",
     instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsPipApps-src",
         ":WMShellFlickerTestsPipCommon-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
 
 android_test {
     name: "WMShellFlickerTestsPipAppsCSuite",
-    defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"],
+    defaults: ["WMShellFlickerTestsDefault"],
     additional_manifests: ["AndroidManifest.xml"],
     package_name: "com.android.wm.shell.flicker.pip.apps",
     instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsPipApps-src",
         ":WMShellFlickerTestsPipCommon-src",
@@ -128,6 +137,7 @@
         "device-tests",
         "csuite",
     ],
+    data: ["trace_config/*"],
 }
 
 csuite_test {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/Android.bp b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
index 9b8cd94..4f1a68a 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
@@ -52,8 +52,10 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.service",
     instrumentation_target_package: "com.android.wm.shell.flicker.service",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: ["src/**/*.kt"],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
 
 android_test {
@@ -62,6 +64,8 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.service",
     instrumentation_target_package: "com.android.wm.shell.flicker.service",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [":WMShellFlickerServicePlatinumTests-src"],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
index 80ab24d..824e454 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -64,4 +66,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index cc982d1..c52ada3b 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
@@ -29,6 +30,7 @@
 import org.junit.After
 import org.junit.Assume
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -75,4 +77,8 @@
         secondaryApp.exit(wmHelper)
         sendNotificationApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
index fa12bb8..8134fdd 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
@@ -29,6 +30,7 @@
 import org.junit.After
 import org.junit.Assume
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -82,4 +84,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
index 2592fd4..3417744 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -28,6 +29,7 @@
 import org.junit.After
 import org.junit.Assume
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -70,4 +72,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
index 983653b..f1a011c 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -70,4 +72,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 068171d..c9b1c91 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -20,6 +20,7 @@
 import android.graphics.Point
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.helpers.WindowUtils
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
@@ -29,6 +30,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -149,4 +151,8 @@
         val LARGE_SCREEN_DP_THRESHOLD = 600
         return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 64b75c5..72f2db3 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -67,4 +69,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
index 1795010..511de4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -66,4 +68,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index 7065846..558d2bf 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -68,4 +70,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
index 251cb50..ecd68295 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -69,4 +71,8 @@
         thirdApp.exit(wmHelper)
         fourthApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
index a9933bbe..f50d5c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.AndroidLoggerSetupRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -27,6 +28,7 @@
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
+import org.junit.ClassRule
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -66,4 +68,8 @@
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
     }
+
+    companion object {
+        @ClassRule @JvmField val setupLoggerRule = AndroidLoggerSetupRule()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
index 4629c53..f813b0d 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
@@ -54,11 +54,13 @@
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.splitscreen",
     instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsSplitScreenBase-src",
         ":WMShellFlickerTestsSplitScreenGroup1-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
 
 android_test {
@@ -74,4 +76,5 @@
         ":WMShellFlickerTestsSplitScreenGroup1-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto
deleted file mode 100644
index 406ada9..0000000
--- a/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto
+++ /dev/null
@@ -1,75 +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.
-
-# proto-message: TraceConfig
-
-# Enable periodic flushing of the trace buffer into the output file.
-write_into_file: true
-
-# Writes the userspace buffer into the file every 1s.
-file_write_period_ms: 2500
-
-# See b/126487238 - we need to guarantee ordering of events.
-flush_period_ms: 30000
-
-# The trace buffers needs to be big enough to hold |file_write_period_ms| of
-# trace data. The trace buffer sizing depends on the number of trace categories
-# enabled and the device activity.
-
-# RSS events
-buffers: {
-  size_kb: 63488
-  fill_policy: RING_BUFFER
-}
-
-data_sources {
-  config {
-    name: "linux.process_stats"
-    target_buffer: 0
-    # polled per-process memory counters and process/thread names.
-    # If you don't want the polled counters, remove the "process_stats_config"
-    # section, but keep the data source itself as it still provides on-demand
-    # thread/process naming for ftrace data below.
-    process_stats_config {
-      scan_all_processes_on_start: true
-    }
-  }
-}
-
-data_sources: {
-  config {
-    name: "linux.ftrace"
-    ftrace_config {
-      ftrace_events: "ftrace/print"
-      ftrace_events: "task/task_newtask"
-      ftrace_events: "task/task_rename"
-      atrace_categories: "ss"
-      atrace_categories: "wm"
-      atrace_categories: "am"
-      atrace_categories: "aidl"
-      atrace_categories: "input"
-      atrace_categories: "binder_driver"
-      atrace_categories: "sched_process_exit"
-      atrace_apps: "com.android.server.wm.flicker.testapp"
-      atrace_apps: "com.android.systemui"
-      atrace_apps: "com.android.wm.shell.flicker"
-      atrace_apps: "com.android.wm.shell.flicker.other"
-      atrace_apps: "com.android.wm.shell.flicker.bubbles"
-      atrace_apps: "com.android.wm.shell.flicker.pip"
-      atrace_apps: "com.android.wm.shell.flicker.splitscreen"
-      atrace_apps: "com.google.android.apps.nexuslauncher"
-    }
-  }
-}
-
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 527dc01..1b347e0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -204,6 +204,7 @@
 
     @Test
     public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
+        doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
         setRunningTask(mHomeTask);
         DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
                 mLandscapeDisplayLayout, mActivityClipData);
@@ -219,6 +220,7 @@
 
     @Test
     public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
+        doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
                 mLandscapeDisplayLayout, mActivityClipData);
@@ -239,6 +241,7 @@
 
     @Test
     public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
+        doReturn(false).when(mSplitScreenStarter).isLeftRightSplit();
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
                 mPortraitDisplayLayout, mActivityClipData);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenUtilsTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenUtilsTests.java
new file mode 100644
index 0000000..30847d3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenUtilsTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.split.SplitScreenUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/** Tests for {@link com.android.wm.shell.common.split.SplitScreenUtils} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SplitScreenUtilsTests extends ShellTestCase {
+
+    @Test
+    public void testIsLeftRightSplit() {
+        Configuration portraitTablet = new Configuration();
+        portraitTablet.smallestScreenWidthDp = 720;
+        portraitTablet.windowConfiguration.setMaxBounds(new Rect(0, 0, 500, 1000));
+        Configuration landscapeTablet = new Configuration();
+        landscapeTablet.smallestScreenWidthDp = 720;
+        landscapeTablet.windowConfiguration.setMaxBounds(new Rect(0, 0, 1000, 500));
+        Configuration portraitPhone = new Configuration();
+        portraitPhone.smallestScreenWidthDp = 420;
+        portraitPhone.windowConfiguration.setMaxBounds(new Rect(0, 0, 500, 1000));
+        Configuration landscapePhone = new Configuration();
+        landscapePhone.smallestScreenWidthDp = 420;
+        landscapePhone.windowConfiguration.setMaxBounds(new Rect(0, 0, 1000, 500));
+
+        // Allow L/R split in portrait = false
+        assertTrue(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                landscapeTablet));
+        assertTrue(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                landscapePhone));
+        assertFalse(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                portraitTablet));
+        assertFalse(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                portraitPhone));
+
+        // Allow L/R split in portrait = true, only affects large screens
+        assertFalse(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                landscapeTablet));
+        assertTrue(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                landscapePhone));
+        assertTrue(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                portraitTablet));
+        assertFalse(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                portraitPhone));
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index fff65f3..d819261 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -140,7 +140,7 @@
         when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
         when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
         when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
-        when(mSplitLayout.isLandscape()).thenReturn(false);
+        when(mSplitLayout.isLeftRightSplit()).thenReturn(false);
         when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true);
         when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash);
 
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 12cb69d..d747489 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -105,7 +105,7 @@
                     ProfileType::None != Properties::getProfileType())) {
         SkCanvas* profileCanvas = backBuffer->getCanvas();
         SkAutoCanvasRestore saver(profileCanvas, true);
-        profileCanvas->concat(mVkSurface->getCurrentPreTransform());
+        profileCanvas->concat(preTransform);
         SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height());
         profiler->draw(profileRenderer);
     }
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index ef218fd..c80891c 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -210,6 +210,18 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <activity android:name=".UnarchiveErrorActivity"
+                  android:configChanges="orientation|keyboardHidden|screenSize"
+                  android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
+                  android:excludeFromRecents="true"
+                  android:noHistory="true"
+                  android:exported="true">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.intent.action.UNARCHIVE_ERROR_DIALOG" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 
 </manifest>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 0a2e880..e5036b0 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -267,4 +267,86 @@
     <!-- The action to restore (i.e. re-install, re-download) an app after parts of the app have been previously moved
          into the cloud for temporary storage. [CHAR LIMIT=15] -->
     <string name="restore">Restore</string>
+
+    <!-- Dialog title shown when the user is trying to restore an app but the associated download
+         cannot happen immediately because the device is offline (has no internet connection.
+         [CHAR LIMIT=50] -->
+    <string name="unarchive_error_offline_title">You\'re offline</string>
+
+    <!-- Dialog body test shown when the user is trying to restore an app but the associated download
+         cannot happen immediately because the device is offline (has no internet connection.
+         [CHAR LIMIT=none] -->
+    <string name="unarchive_error_offline_body">
+        This app will automatically restore when you\'re connected to the internet
+    </string>
+
+    <!-- Dialog title shown when the user is trying to restore an app but a generic error happened.
+         [CHAR LIMIT=50] -->
+    <string name="unarchive_error_generic_title">Something went wrong</string>
+
+    <!-- Dialog body shown when the user is trying to restore an app but a generic error happened.
+         [CHAR LIMIT=none] -->
+    <string name="unarchive_error_generic_body">
+        There was a problem trying to restore this app
+    </string>
+
+    <!-- Dialog title shown when the user is trying to restore an app but the device is out of
+         storage. [CHAR LIMIT=50] -->
+    <string name="unarchive_error_storage_title">Not enough storage</string>
+
+    <!-- Dialog body shown when the user is trying to restore an app but the device is out of
+         storage. [CHAR LIMIT=none] -->
+    <string name="unarchive_error_storage_body">
+        To restore this app, you can free up space on this device. Storage
+        required: <xliff:g id="bytes" example="100 MB">%1$s</xliff:g>
+    </string>
+
+    <!-- Dialog title shown when the user is trying to restore an app but the installer needs to
+         perform an additional action. [CHAR LIMIT=50] -->
+    <string name="unarchive_action_required_title">Action required</string>
+
+    <!-- Dialog body shown when the user is trying to restore an app but the installer needs to
+         perform an additional action. [CHAR LIMIT=none] -->
+    <string name="unarchive_action_required_body">
+        Follow the next steps to restore this app
+    </string>
+    <!-- Dialog title shown when the user is trying to restore an app but the installer responsible
+         for the action is in a disabled state. [CHAR LIMIT=50] -->
+    <string name="unarchive_error_installer_disabled_title">
+        <xliff:g id="installername" example="App Store">%1$s</xliff:g> is disabled
+    </string>
+
+    <!-- Dialog body shown when the user is trying to restore an app but the installer responsible
+         for the action is in a disabled state. [CHAR LIMIT=none] -->
+    <string name="unarchive_error_installer_disabled_body">
+        To restore this app, enable the
+        <xliff:g id="installername" example="App Store">%1$s</xliff:g> in Settings
+    </string>
+
+    <!-- Dialog title shown when the user is trying to restore an app but the installer responsible
+     for the action is uninstalled. [CHAR LIMIT=50] -->
+    <string name="unarchive_error_installer_uninstalled_title">
+        <xliff:g id="installername" example="App Store">%1$s</xliff:g> is uninstalled
+    </string>
+
+    <!-- Dialog body shown when the user is trying to restore an app but the installer responsible
+         for the action is uninstalled. [CHAR LIMIT=none] -->
+    <string name="unarchive_error_installer_uninstalled_body">
+        To restore this app, you\'ll need to install
+        <xliff:g id="installername" example="App Store">%1$s</xliff:g>
+    </string>
+
+    <!-- Dialog action to continue with the next action. [CHAR LIMIT=30] -->
+    <string name="unarchive_action_required_continue">Continue</string>
+
+    <!-- Dialog action to clear device storage, as in deleting some apps or photos to free up bytes
+         [CHAR LIMIT=30] -->
+    <string name="unarchive_clear_storage_button">Clear storage</string>
+
+    <!-- Dialog action to open the Settings app. [CHAR LIMIT=30] -->
+    <string name="unarchive_settings_button">Settings</string>
+
+    <!-- Dialog action to close a dialog. [CHAR LIMIT=30] -->
+    <string name="close">Close</string>
+
 </resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index daedb1a..8d8254a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -298,7 +298,14 @@
                         broadcastIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
-                session.commit(pendingIntent.getIntentSender());
+                try {
+                    session.commit(pendingIntent.getIntentSender());
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, "Cannot install package: ", e);
+                    launchFailure(PackageInstaller.STATUS_FAILURE,
+                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+                    return;
+                }
                 mCancelButton.setEnabled(false);
                 setFinishOnTouchOutside(false);
             } else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorActivity.java
new file mode 100644
index 0000000..78d2f88
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorActivity.java
@@ -0,0 +1,79 @@
+/*
+ * 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.packageinstaller;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.os.Bundle;
+
+import java.util.Objects;
+
+public class UnarchiveErrorActivity extends Activity {
+
+    static final String EXTRA_REQUIRED_BYTES =
+            "com.android.content.pm.extra.UNARCHIVE_EXTRA_REQUIRED_BYTES";
+    static final String EXTRA_INSTALLER_PACKAGE_NAME =
+            "com.android.content.pm.extra.UNARCHIVE_INSTALLER_PACKAGE_NAME";
+    static final String EXTRA_INSTALLER_TITLE =
+            "com.android.content.pm.extra.UNARCHIVE_INSTALLER_TITLE";
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(null);
+
+        Bundle extras = getIntent().getExtras();
+        int unarchivalStatus = extras.getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS);
+        long requiredBytes = extras.getLong(EXTRA_REQUIRED_BYTES);
+        PendingIntent intent = extras.getParcelable(Intent.EXTRA_INTENT, PendingIntent.class);
+        String installerPackageName = extras.getString(EXTRA_INSTALLER_PACKAGE_NAME);
+        // We cannot derive this from the package name because the installer might not be installed
+        // anymore.
+        String installerAppTitle = extras.getString(EXTRA_INSTALLER_TITLE);
+
+        if (unarchivalStatus == PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED) {
+            Objects.requireNonNull(intent);
+        }
+
+        FragmentTransaction ft = getFragmentManager().beginTransaction();
+        Fragment prev = getFragmentManager().findFragmentByTag("dialog");
+        if (prev != null) {
+            ft.remove(prev);
+        }
+
+        Bundle args = new Bundle();
+        args.putInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, unarchivalStatus);
+        args.putLong(EXTRA_REQUIRED_BYTES, requiredBytes);
+        if (intent != null) {
+            args.putParcelable(Intent.EXTRA_INTENT, intent);
+        }
+        if (installerPackageName != null) {
+            args.putString(EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
+        }
+        if (installerAppTitle != null) {
+            args.putString(EXTRA_INSTALLER_TITLE, installerAppTitle);
+        }
+
+        DialogFragment fragment = new UnarchiveErrorFragment();
+        fragment.setArguments(args);
+        fragment.show(ft, "dialog");
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
new file mode 100644
index 0000000..d33433f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
@@ -0,0 +1,196 @@
+/*
+ * 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.packageinstaller;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.PendingIntent;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+public class UnarchiveErrorFragment extends DialogFragment implements
+        DialogInterface.OnClickListener {
+
+    private static final String TAG = "UnarchiveErrorFragment";
+
+    private int mStatus;
+
+    @Nullable
+    private PendingIntent mExtraIntent;
+
+    @Nullable
+    private String mInstallerPackageName;
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Bundle args = getArguments();
+        mStatus = args.getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, -1);
+        mExtraIntent = args.getParcelable(Intent.EXTRA_INTENT, PendingIntent.class);
+        long requiredBytes = args.getLong(UnarchiveErrorActivity.EXTRA_REQUIRED_BYTES);
+        mInstallerPackageName = args.getString(
+                UnarchiveErrorActivity.EXTRA_INSTALLER_PACKAGE_NAME);
+        String installerAppTitle = args.getString(UnarchiveErrorActivity.EXTRA_INSTALLER_TITLE);
+
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
+
+        dialogBuilder.setTitle(getDialogTitle(mStatus, installerAppTitle));
+        dialogBuilder.setMessage(getBodyText(mStatus, installerAppTitle, requiredBytes));
+
+        addButtons(dialogBuilder, mStatus);
+
+        return dialogBuilder.create();
+    }
+
+    private void addButtons(AlertDialog.Builder dialogBuilder, int status) {
+        switch (status) {
+            case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
+                dialogBuilder.setPositiveButton(R.string.unarchive_action_required_continue, this);
+                dialogBuilder.setNegativeButton(R.string.close, this);
+                break;
+            case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
+                dialogBuilder.setPositiveButton(R.string.unarchive_clear_storage_button, this);
+                dialogBuilder.setNegativeButton(R.string.close, this);
+                break;
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
+                dialogBuilder.setPositiveButton(R.string.external_sources_settings, this);
+                dialogBuilder.setNegativeButton(R.string.close, this);
+                break;
+            case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY:
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED:
+            case PackageInstaller.UNARCHIVAL_GENERIC_ERROR:
+                dialogBuilder.setPositiveButton(android.R.string.ok, this);
+                break;
+            default:
+                // This should never happen through normal API usage.
+                throw new IllegalArgumentException("Invalid unarchive status " + status);
+        }
+    }
+
+    private String getBodyText(int status, String installerAppTitle, long requiredBytes) {
+        switch (status) {
+            case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
+                return getString(R.string.unarchive_action_required_body);
+            case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
+                return String.format(getString(R.string.unarchive_error_storage_body),
+                        Formatter.formatShortFileSize(getActivity(), requiredBytes));
+            case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY:
+                return getString(R.string.unarchive_error_offline_body);
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
+                return String.format(getString(R.string.unarchive_error_installer_disabled_body),
+                        installerAppTitle);
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED:
+                return String.format(
+                        getString(R.string.unarchive_error_installer_uninstalled_body),
+                        installerAppTitle);
+            case PackageInstaller.UNARCHIVAL_GENERIC_ERROR:
+                return getString(R.string.unarchive_error_generic_body);
+            default:
+                // This should never happen through normal API usage.
+                throw new IllegalArgumentException("Invalid unarchive status " + status);
+        }
+    }
+
+    private String getDialogTitle(int status, String installerAppTitle) {
+        switch (status) {
+            case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
+                return getString(R.string.unarchive_action_required_title);
+            case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
+                return getString(R.string.unarchive_error_storage_title);
+            case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY:
+                return getString(R.string.unarchive_error_offline_title);
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
+                return String.format(getString(R.string.unarchive_error_installer_disabled_title),
+                        installerAppTitle);
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED:
+                return String.format(
+                        getString(R.string.unarchive_error_installer_uninstalled_title),
+                        installerAppTitle);
+            case PackageInstaller.UNARCHIVAL_GENERIC_ERROR:
+                return getString(R.string.unarchive_error_generic_title);
+            default:
+                // This should never happen through normal API usage.
+                throw new IllegalArgumentException("Invalid unarchive status " + status);
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which != Dialog.BUTTON_POSITIVE) {
+            return;
+        }
+
+        try {
+            onClickInternal();
+        } catch (IntentSender.SendIntentException e) {
+            Log.e(TAG, "Failed to start intent after onClick.", e);
+        }
+    }
+
+    private void onClickInternal() throws IntentSender.SendIntentException {
+        Activity activity = getActivity();
+        if (activity == null) {
+            // This probably shouldn't happen in practice.
+            Log.i(TAG, "Lost reference to activity, cannot act onClick.");
+            return;
+        }
+
+        switch (mStatus) {
+            case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
+                activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
+                        null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+                break;
+            case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
+                if (mExtraIntent != null) {
+                    activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
+                            null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+                } else {
+                    Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
+                    startActivity(intent);
+                }
+                break;
+            case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                Uri uri = Uri.fromParts("package", mInstallerPackageName, null);
+                intent.setData(uri);
+                startActivity(intent);
+                break;
+            default:
+                // Do nothing. The rest of the dialogs are purely informational.
+        }
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        super.onDismiss(dialog);
+        if (isAdded()) {
+            getActivity().finish();
+        }
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index 8386bc1..f216abb 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -17,24 +17,32 @@
 package com.android.settingslib.spa.gallery.card
 
 import android.os.Bundle
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Error
 import androidx.compose.material.icons.outlined.PowerOff
 import androidx.compose.material.icons.outlined.Shield
 import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.gallery.R
 import com.android.settingslib.spa.widget.card.CardButton
 import com.android.settingslib.spa.widget.card.CardModel
 import com.android.settingslib.spa.widget.card.SettingsCard
 import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
+import com.android.settingslib.spa.widget.card.SettingsCardContent
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -50,6 +58,7 @@
             SettingsCardWithIcon()
             SettingsCardWithoutIcon()
             SampleSettingsCollapsibleCard()
+            SampleSettingsCardContent()
         }
     }
 
@@ -108,6 +117,32 @@
         )
     }
 
+    @Composable
+    fun SampleSettingsCardContent() {
+        SettingsCard {
+            SettingsCardContent {
+                Box(
+                    Modifier
+                        .fillMaxWidth()
+                        .clickable { }
+                        .padding(SettingsDimension.dialogItemPadding),
+                ) {
+                    Text(text = "Abc")
+                }
+            }
+            SettingsCardContent {
+                Box(
+                    Modifier
+                        .fillMaxWidth()
+                        .clickable { }
+                        .padding(SettingsDimension.dialogItemPadding),
+                ) {
+                    Text(text = "123")
+                }
+            }
+        }
+    }
+
     fun buildInjectEntry(): SettingsEntryBuilder {
         return SettingsEntryBuilder.createInject(owner = createSettingsPage())
             .setUiLayoutFn {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index 8c862d4..f7c5414 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -20,6 +20,8 @@
 import androidx.compose.ui.unit.dp
 
 object SettingsShape {
+    val CornerExtraSmall = RoundedCornerShape(4.dp)
+
     val CornerMedium = RoundedCornerShape(12.dp)
 
     val CornerExtraLarge = RoundedCornerShape(28.dp)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 10e2686..4379278 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -36,10 +36,13 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
 import com.android.settingslib.spa.debug.UiModePreviews
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraLarge
+import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraSmall
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -49,7 +52,7 @@
     Card(
         shape = CornerExtraLarge,
         colors = CardDefaults.cardColors(
-            containerColor = SettingsTheme.colorScheme.surface,
+            containerColor = Color.Transparent,
         ),
         modifier = Modifier
             .fillMaxWidth()
@@ -62,6 +65,20 @@
 }
 
 @Composable
+fun SettingsCardContent(content: @Composable ColumnScope.() -> Unit) {
+    Card(
+        shape = CornerExtraSmall,
+        colors = CardDefaults.cardColors(
+            containerColor = SettingsTheme.colorScheme.surface,
+        ),
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(vertical = 1.dp),
+        content = content,
+    )
+}
+
+@Composable
 fun SettingsCard(model: CardModel) {
     SettingsCard {
         SettingsCardImpl(model)
@@ -70,14 +87,16 @@
 
 @Composable
 internal fun SettingsCardImpl(model: CardModel) {
-    Column(
-        modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
-        verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
-    ) {
-        CardIcon(model.imageVector)
-        SettingsTitle(model.title)
-        SettingsBody(model.text)
-        Buttons(model.buttons)
+    SettingsCardContent {
+        Column(
+            modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+            verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
+        ) {
+            CardIcon(model.imageVector)
+            SettingsTitle(model.title)
+            SettingsBody(model.text)
+            Buttons(model.buttons)
+        }
     }
 }
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
index 7d10645..bf192a1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -30,7 +30,6 @@
 import androidx.compose.material.icons.outlined.Error
 import androidx.compose.material.icons.outlined.PowerOff
 import androidx.compose.material.icons.outlined.Shield
-import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
@@ -54,18 +53,16 @@
 fun SettingsCollapsibleCard(
     title: String,
     imageVector: ImageVector,
-    models: List<CardModel>
+    models: List<CardModel>,
 ) {
     var expanded by rememberSaveable { mutableStateOf(false) }
     SettingsCard {
-        Header(title, imageVector, models.size, expanded) { expanded = it }
+        SettingsCardContent {
+            Header(title, imageVector, models.size, expanded) { expanded = it }
+        }
         AnimatedVisibility(expanded) {
             Column {
                 for (model in models) {
-                    HorizontalDivider(
-                        thickness = SettingsDimension.paddingSmall,
-                        color = MaterialTheme.colorScheme.surface,
-                    )
                     SettingsCardImpl(model)
                 }
             }
diff --git a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
index 31e9696..bee9e20 100644
--- a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
@@ -53,7 +53,7 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:maxLines="2"
+            android:maxLines="10"
             android:hyphenationFrequency="normalFast"
             android:lineBreakWordStyle="phrase"
             android:textAppearance="?android:attr/textAppearanceListItem"
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8412cba..5c09b16 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -251,6 +251,8 @@
         Settings.Secure.STYLUS_HANDWRITING_ENABLED,
         Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
         Settings.Secure.CREDENTIAL_SERVICE,
-        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY
+        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+        Settings.Secure.EVEN_DIMMER_ACTIVATED,
+        Settings.Secure.EVEN_DIMMER_MIN_NITS
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9197554..b0169a1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -110,6 +110,9 @@
         VALIDATORS.put(Secure.FONT_WEIGHT_ADJUSTMENT, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.REDUCE_BRIGHT_COLORS_LEVEL, PERCENTAGE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.EVEN_DIMMER_ACTIVATED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.EVEN_DIMMER_MIN_NITS,
+                new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
         VALIDATORS.put(Secure.TTS_DEFAULT_RATE, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_PITCH, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_SYNTH, PACKAGE_NAME_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a509ba3..a978889 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2135,6 +2135,15 @@
                 Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
                 SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
 
+        final long evenDimmerToken = p.start(SecureSettingsProto.EVEN_DIMMER);
+        dumpSetting(s, p,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED,
+                SecureSettingsProto.EvenDimmer.EVEN_DIMMER_ACTIVATED);
+        dumpSetting(s, p,
+                Settings.Secure.EVEN_DIMMER_MIN_NITS,
+                SecureSettingsProto.EvenDimmer.EVEN_DIMMER_MIN_NITS);
+        p.end(evenDimmerToken);
+
         final long gestureToken = p.start(SecureSettingsProto.GESTURE);
         dumpSetting(s, p,
                 Settings.Secure.AWARE_ENABLED,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0a71cda..f1029a3 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -512,15 +512,6 @@
             </intent-filter>
         </activity-alias>
 
-        <activity
-            android:name="com.android.wm.shell.legacysplitscreen.ForcedResizableInfoActivity"
-            android:theme="@style/ForcedResizableTheme"
-            android:excludeFromRecents="true"
-            android:stateNotNeeded="true"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-            android:exported="false">
-        </activity>
-
         <!-- 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"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 3e84597..77c4aa6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -68,6 +68,16 @@
 }
 
 flag {
+    name: "notification_background_tint_optimization"
+    namespace: "systemui"
+    description: "Re-enable the codepath that removed tinting of notifications when the"
+        " standard background color is desired.  This was the behavior before we discovered"
+        " a resources threading issue, which we worked around by tinting the notification"
+        " backgrounds and footer buttons."
+    bug: "294347738"
+}
+
+flag {
     name: "scene_container"
     namespace: "systemui"
     description: "Enables the scene container framework go/flexiglass."
@@ -165,8 +175,16 @@
 }
 
 flag {
+   name: "rest_to_unlock"
+   namespace: "systemui"
+   description: "Require prolonged touch for fingerprint authentication"
+   bug: "303672286"
+}
+
+flag {
    name: "record_issue_qs_tile"
    namespace: "systemui"
    description: "Replace Record Trace QS Tile with expanded Record Issue QS Tile"
    bug: "305049544"
 }
+
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 02db0d7..a613ad8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -29,17 +29,16 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.wallet.controller.QuickAccessWalletController
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -48,7 +47,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@@ -56,26 +54,32 @@
     @Mock private lateinit var walletController: QuickAccessWalletController
     @Mock private lateinit var activityStarter: ActivityStarter
 
+    private lateinit var testDispatcher: TestDispatcher
+    private lateinit var testScope: TestScope
+
     private lateinit var underTest: QuickAccessWalletKeyguardQuickAffordanceConfig
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+
         underTest =
             QuickAccessWalletKeyguardQuickAffordanceConfig(
                 context,
+                testDispatcher,
                 walletController,
                 activityStarter,
             )
     }
 
     @Test
-    fun affordance_keyguardShowing_hasWalletCard_visibleModel() = runBlockingTest {
+    fun affordance_keyguardShowing_hasWalletCard_visibleModel() = testScope.runTest {
         setUpState()
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
         assertThat(visibleModel.icon)
@@ -88,77 +92,61 @@
                         ),
                 )
             )
-        job.cancel()
     }
 
     @Test
-    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
-        runTest(UnconfinedTestDispatcher()) {
+    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = testScope.runTest {
             setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
-            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.lockScreenState)
 
             assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-            job.cancel()
         }
 
     @Test
-    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
-        runTest(UnconfinedTestDispatcher()) {
-            setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
-            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = testScope.runTest {
+        setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
 
-            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
-            val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
-            assertThat(visibleModel.icon)
-                .isEqualTo(
-                    Icon.Loaded(
-                        drawable = ICON,
-                        contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_wallet_button,
-                            ),
-                    )
+        val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+        assertThat(visibleModel.icon)
+            .isEqualTo(
+                Icon.Loaded(
+                    drawable = ICON,
+                    contentDescription =
+                        ContentDescription.Resource(
+                            res = R.string.accessibility_wallet_button,
+                        ),
                 )
-            job.cancel()
-        }
+            )
+    }
 
     @Test
-    fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
+    fun affordance_walletFeatureNotEnabled_modelIsNone() = testScope.runTest {
         setUpState(isWalletFeatureAvailable = false)
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
-        job.cancel()
     }
 
     @Test
-    fun affordance_queryNotSuccessful_modelIsNone() = runBlockingTest {
+    fun affordance_queryNotSuccessful_modelIsNone() = testScope.runTest {
         setUpState(isWalletQuerySuccessful = false)
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
-        job.cancel()
     }
 
     @Test
-    fun affordance_noSelectedCard_modelIsNone() = runBlockingTest {
+    fun affordance_noSelectedCard_modelIsNone() = testScope.runTest {
         setUpState(hasSelectedCard = false)
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
-        job.cancel()
     }
 
     @Test
@@ -179,7 +167,7 @@
     }
 
     @Test
-    fun getPickerScreenState_default() = runTest {
+    fun getPickerScreenState_default() = testScope.runTest {
         setUpState()
 
         assertThat(underTest.getPickerScreenState())
@@ -187,7 +175,7 @@
     }
 
     @Test
-    fun getPickerScreenState_unavailable() = runTest {
+    fun getPickerScreenState_unavailable() = testScope.runTest {
         setUpState(
             isWalletServiceAvailable = false,
         )
@@ -197,7 +185,7 @@
     }
 
     @Test
-    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest {
+    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = testScope.runTest {
         setUpState(
             isWalletFeatureAvailable = false,
         )
@@ -207,7 +195,7 @@
     }
 
     @Test
-    fun getPickerScreenState_disabledWhenThereIsNoCard() = runTest {
+    fun getPickerScreenState_disabledWhenThereIsNoCard() = testScope.runTest {
         setUpState(
             hasSelectedCard = false,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 8d1d905..b343add 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -37,17 +37,23 @@
 
     @MainThread
     fun enable(onShadeInteraction: Runnable) {
-        if (shadeExpansionCollectorJob == null) {
-            shadeExpansionCollectorJob =
-                scope.launch {
-                    // wait for it to emit true once
-                    shadeInteractorLazy.get().isUserInteracting.first { it }
-                    onShadeInteraction.run()
-                }
-            shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
-        } else {
+        if (shadeExpansionCollectorJob != null) {
             Log.e(TAG, "Already enabled")
+            return
         }
+        if (shadeInteractorLazy.get().isUserInteracting.value) {
+            Log.e(TAG, "isUserInteracting already true, skipping enable")
+            return
+        }
+        shadeExpansionCollectorJob =
+            scope.launch {
+                Log.i(TAG, "Enable detector")
+                // wait for it to emit true once
+                shadeInteractorLazy.get().isUserInteracting.first { it }
+                Log.i(TAG, "Detector detected shade interaction")
+                onShadeInteraction.run()
+            }
+        shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
     }
 
     @MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0c24752..ff65b31 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -240,11 +240,6 @@
     @JvmField
     val WALLPAPER_PICKER_PREVIEW_ANIMATION = releasedFlag("wallpaper_picker_preview_animation")
 
-    /** Flag to enable rest to unlock feature. */
-    // TODO(b/303672286): Tracking bug
-    @JvmField
-    val REST_TO_UNLOCK: UnreleasedFlag = unreleasedFlag("rest_to_unlock")
-
     /**
      * TODO(b/278086361): Tracking bug
      * Complete rewrite of the interactions between System UI and Window Manager involving keyguard
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 7337292..a988a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -32,13 +32,19 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.wallet.controller.QuickAccessWalletController
 import com.android.systemui.wallet.util.getPaymentCards
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
 
 /** Quick access wallet quick affordance data source. */
 @SysUISingleton
@@ -46,6 +52,7 @@
 @Inject
 constructor(
     @Application private val context: Context,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val walletController: QuickAccessWalletController,
     private val activityStarter: ActivityStarter,
 ) : KeyguardQuickAffordanceConfig {
@@ -56,6 +63,7 @@
 
     override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
         conflatedCallbackFlow {
             val callback =
@@ -63,11 +71,7 @@
                     override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                         val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
                         trySendWithFailureLogging(
-                            state(
-                                isFeatureEnabled = isWalletAvailable(),
-                                hasCard = hasCards,
-                                tileIcon = walletController.walletClient.tileIcon,
-                            ),
+                            hasCards,
                             TAG,
                         )
                     }
@@ -75,7 +79,7 @@
                     override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
                         Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"")
                         trySendWithFailureLogging(
-                            KeyguardQuickAffordanceConfig.LockScreenState.Hidden,
+                            null,
                             TAG,
                         )
                     }
@@ -86,8 +90,12 @@
                 QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
                 QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
             )
-            walletController.updateWalletPreference()
-            walletController.queryWalletCards(callback)
+
+            withContext(backgroundDispatcher) {
+                // Both must be called on background thread
+                walletController.updateWalletPreference()
+                walletController.queryWalletCards(callback)
+            }
 
             awaitClose {
                 walletController.unregisterWalletChangeObservers(
@@ -95,6 +103,19 @@
                     QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
                 )
             }
+        }.flatMapLatest { hasCards ->
+            // If hasCards is null, this indicates an error occurred upon card retrieval
+            val state =
+                if (hasCards == null) {
+                    KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+                } else {
+                    state(
+                        isWalletAvailable(),
+                        hasCards,
+                        walletController.walletClient.tileIcon,
+                    )
+                }
+            flowOf(state)
         }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
@@ -131,25 +152,33 @@
     }
 
     private suspend fun queryCards(): List<WalletCard> {
-        return suspendCancellableCoroutine { continuation ->
-            val callback =
-                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
-                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
-                        continuation.resumeWith(
-                            Result.success(getPaymentCards(response.walletCards) ?: emptyList())
-                        )
-                    }
+        return withContext(backgroundDispatcher) {
+            suspendCancellableCoroutine { continuation ->
+                val callback =
+                    object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+                        override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
+                            continuation.resumeWith(
+                                Result.success(getPaymentCards(response.walletCards))
+                            )
+                        }
 
-                    override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
-                        continuation.resumeWith(Result.success(emptyList()))
+                        override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
+                            continuation.resumeWith(Result.success(emptyList()))
+                        }
                     }
-                }
-            walletController.queryWalletCards(callback)
+                // Must be called on background thread
+                walletController.queryWalletCards(callback)
+            }
         }
     }
 
-    private fun isWalletAvailable() =
-        with(walletController.walletClient) { isWalletServiceAvailable && isWalletFeatureAvailable }
+    private suspend fun isWalletAvailable() =
+        withContext(backgroundDispatcher) {
+            with(walletController.walletClient) {
+                // Must be called on background thread
+                isWalletServiceAvailable && isWalletFeatureAvailable
+            }
+        }
 
     private fun state(
         isFeatureEnabled: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
index 0bee48a..560e4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -19,11 +19,10 @@
 import android.animation.ValueAnimator
 import android.graphics.Point
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
 import com.android.systemui.biometrics.SideFpsController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.log.SideFpsLogger
@@ -50,11 +49,10 @@
     private val sfpsController: dagger.Lazy<SideFpsController>,
     private val logger: SideFpsLogger,
     private val commandRegistry: CommandRegistry,
-    private val featureFlagsClassic: FeatureFlagsClassic,
 ) : CoreStartable {
 
     override fun start() {
-        if (!featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) {
+        if (!Flags.restToUnlock()) {
             return
         }
         // When the rest to unlock feature is disabled by the user, stop any coroutines that are
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index a0f5baf..2d0712c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -20,14 +20,13 @@
 import android.content.Context
 import android.graphics.Point
 import androidx.core.animation.doOnEnd
+import com.android.systemui.Flags
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.isDefaultOrientation
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
@@ -58,7 +57,6 @@
     private val sfpsSensorInteractor: SideFpsSensorInteractor,
     displayStateInteractor: DisplayStateInteractor,
     @Application private val applicationScope: CoroutineScope,
-    private val featureFlagsClassic: FeatureFlagsClassic,
 ) {
     private val _progress = MutableStateFlow(0.0f)
     private val _visible = MutableStateFlow(false)
@@ -155,7 +153,7 @@
         sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication
 
     init {
-        if (featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) {
+        if (Flags.restToUnlock()) {
             launchAnimator()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
index 5bfc7dc..039d0e0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
@@ -105,8 +105,8 @@
         float height = mRadius * 2 + additionalHeight;
         float additionalWidth = mAdditionalWidthForAnimation * mPulseAnimationProgress;
         float width = getWidth() + additionalWidth;
-        float x = -(additionalWidth / 2);
-        float y = navHeight - mBottom - height - (additionalHeight / 2);
+        float x = -additionalWidth;
+        float y = navHeight - mBottom - height + (additionalHeight / 2);
         float adjustedRadius = height / 2;
         canvas.drawRoundRect(x, y, width, y + height, adjustedRadius, adjustedRadius, mPaint);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 6a9757f..31a4de4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -39,7 +39,7 @@
      * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended
      * but a transition they initiated is still animating.
      */
-    val isUserInteracting: Flow<Boolean>
+    val isUserInteracting: StateFlow<Boolean>
 
     /** Are touches allowed on the notification panel? */
     val isShadeTouchable: Flow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
index d41c5a6..6defbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -39,7 +39,7 @@
     override val isAnyExpanded: StateFlow<Boolean> = inactiveFlowBoolean
     override val isUserInteractingWithShade: Flow<Boolean> = inactiveFlowBoolean
     override val isUserInteractingWithQs: Flow<Boolean> = inactiveFlowBoolean
-    override val isUserInteracting: Flow<Boolean> = inactiveFlowBoolean
+    override val isUserInteracting: StateFlow<Boolean> = inactiveFlowBoolean
     override val isShadeTouchable: Flow<Boolean> = inactiveFlowBoolean
     override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index 68600e9..7a340d2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -65,9 +65,10 @@
     override val isShadeFullyExpanded: Flow<Boolean> =
         baseShadeInteractor.shadeExpansion.map { it >= 1f }.distinctUntilChanged()
 
-    override val isUserInteracting: Flow<Boolean> =
+    override val isUserInteracting: StateFlow<Boolean> =
         combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
             .distinctUntilChanged()
+            .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isShadeTouchable: Flow<Boolean> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index c1a630f..909cff37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -493,11 +493,11 @@
                         Arrays.asList(
                                 Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
                 /* Back: go back to previous state (back button) */
-                /* Meta + Grave, Meta + backspace, Meta + left arrow */
+                /* Meta + Escape, Meta + Grave, Meta + backspace, Meta + left arrow */
                 new ShortcutKeyGroupMultiMappingInfo(
                         context.getString(R.string.group_system_go_back),
                         Arrays.asList(
-                                Pair.create(KeyEvent.KEYCODE_GRAVE, KeyEvent.META_META_ON),
+                                Pair.create(KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_META_ON),
                                 Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON),
                                 Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))),
                 /* Access home screen: Meta + H, Meta + Enter */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 31893b4..e90ddf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,37 +15,48 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 class ActiveNotificationsInteractor
 @Inject
 constructor(
     private val repository: ActiveNotificationListRepository,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
     /** Notifications actively presented to the user in the notification stack, in order. */
     val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
-        repository.activeNotifications.map { store ->
-            store.renderList.map { key ->
-                val entry =
-                    store[key]
-                        ?: error("Could not find notification with key $key in active notif store.")
-                when (entry) {
-                    is ActiveNotificationGroupModel -> entry.summary
-                    is ActiveNotificationModel -> entry
+        repository.activeNotifications
+            .map { store ->
+                store.renderList.map { key ->
+                    val entry =
+                        store[key]
+                            ?: error(
+                                "Could not find notification with key $key in active notif store."
+                            )
+                    when (entry) {
+                        is ActiveNotificationGroupModel -> entry.summary
+                        is ActiveNotificationModel -> entry
+                    }
                 }
             }
-        }
+            .flowOn(backgroundDispatcher)
 
     /** Are any notifications being actively presented in the notification stack? */
     val areAnyNotificationsPresent: Flow<Boolean> =
-        repository.activeNotifications.map { it.renderList.isNotEmpty() }.distinctUntilChanged()
+        repository.activeNotifications
+            .map { it.renderList.isNotEmpty() }
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
 
     /**
      * The same as [areAnyNotificationsPresent], but without flows, for easy access in synchronous
@@ -59,6 +70,7 @@
         repository.notifStats
             .map { it.hasClearableAlertingNotifs || it.hasClearableSilentNotifs }
             .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
 
     fun setNotifStats(notifStats: NotifStats) {
         repository.notifStats.value = notifStats
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
index 87b8e55..73341db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -15,19 +15,24 @@
  */
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
 
 /** Domain logic pertaining to notifications on the keyguard. */
 class NotificationsKeyguardInteractor
 @Inject
 constructor(
     repository: NotificationsKeyguardViewStateRepository,
+    @Background backgroundDispatcher: CoroutineDispatcher,
 ) {
     /** Is a pulse expansion occurring? */
-    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
+    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding.flowOn(backgroundDispatcher)
 
     /** Are notifications fully hidden from view? */
-    val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
+    val areNotificationsFullyHidden: Flow<Boolean> =
+        repository.areNotificationsFullyHidden.flowOn(backgroundDispatcher)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 3184d5e..3616fd6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -18,6 +18,8 @@
 
 import static android.graphics.PorterDuff.Mode.SRC_ATOP;
 
+import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
 import android.annotation.StringRes;
@@ -313,15 +315,16 @@
         Resources.Theme theme = mContext.getTheme();
         final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(mContext,
                 com.android.internal.R.attr.materialColorOnSurface);
-        final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
-                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
         final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
         final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
-        // TODO(b/282173943): Remove redundant tinting once Resources are thread-safe
-        final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
-        if (scHigh != 0) {
-            clearAllBg.setColorFilter(bgColorFilter);
-            manageBg.setColorFilter(bgColorFilter);
+        if (!notificationBackgroundTintOptimization()) {
+            final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
+                    com.android.internal.R.attr.materialColorSurfaceContainerHigh);
+            if (scHigh != 0) {
+                final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
+                clearAllBg.setColorFilter(bgColorFilter);
+                manageBg.setColorFilter(bgColorFilter);
+            }
         }
         mClearAllButton.setBackground(clearAllBg);
         mClearAllButton.setTextColor(onSurface);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 7c8d762..4fe05ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -309,8 +311,7 @@
     protected void setBackgroundTintColor(int color) {
         if (color != mCurrentBackgroundTint) {
             mCurrentBackgroundTint = color;
-            // TODO(282173943): re-enable this tinting optimization when Resources are thread-safe
-            if (false && color == mNormalColor) {
+            if (notificationBackgroundTintOptimization() && color == mNormalColor) {
                 // We don't need to tint a normal notification
                 color = 0;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 0e6df6b..e031be2 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
 
+import android.annotation.WorkerThread;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -146,7 +147,9 @@
 
     /**
      * Update the "show wallet" preference.
+     * This should not be called on the main thread.
      */
+    @WorkerThread
     public void updateWalletPreference() {
         mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable()
                 && mQuickAccessWalletClient.isWalletFeatureAvailable()
@@ -155,10 +158,12 @@
 
     /**
      * Query the wallet cards from {@link QuickAccessWalletClient}.
+     * This should not be called on the main thread.
      *
      * @param cardsRetriever a callback to retrieve wallet cards.
      * @param maxCards the maximum number of cards requested from the QuickAccessWallet
      */
+    @WorkerThread
     public void queryWalletCards(
             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, int maxCards) {
         if (mClock.elapsedRealtime() - mQawClientCreatedTimeMillis
@@ -182,9 +187,11 @@
 
     /**
      * Query the wallet cards from {@link QuickAccessWalletClient}.
+     * This should not be called on the main thread.
      *
      * @param cardsRetriever a callback to retrieve wallet cards.
      */
+    @WorkerThread
     public void queryWalletCards(
             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
         queryWalletCards(cardsRetriever, /* maxCards= */ 1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 993dbac..54d6b53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -95,6 +95,18 @@
         }
 
     @Test
+    fun enableDetector_isUserInteractingTrue_shouldNotPostRunnable() =
+        testComponent.runTest {
+            // GIVEN isInteracting starts true
+            shadeRepository.setLegacyShadeTracking(true)
+            runCurrent()
+            detector.enable(action)
+
+            // THEN action was not run
+            verifyZeroInteractions(action)
+        }
+
+    @Test
     fun enableDetector_shadeExpandImmediate_shouldNotPostRunnable() =
         testComponent.runTest {
             // GIVEN shade is closed and detector is enabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 1dbb297..62c0ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
@@ -293,8 +295,10 @@
                 )
         );
 
-        mActiveNotificationsInteractor =
-                new ActiveNotificationsInteractor(new ActiveNotificationListRepository());
+        mActiveNotificationsInteractor = new ActiveNotificationsInteractor(
+                        new ActiveNotificationListRepository(),
+                        StandardTestDispatcher(/* scheduler = */ null, /* name = */ null)
+                );
 
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index b86f841..6374d5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -25,14 +25,19 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 
 @SmallTest
 class RenderNotificationsListInteractorTest : SysuiTestCase() {
+    private val backgroundDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(backgroundDispatcher)
 
     private val notifsRepository = ActiveNotificationListRepository()
-    private val notifsInteractor = ActiveNotificationsInteractor(notifsRepository)
+    private val notifsInteractor =
+        ActiveNotificationsInteractor(notifsRepository, backgroundDispatcher)
     private val underTest =
         RenderNotificationListInteractor(
             notifsRepository,
@@ -40,21 +45,26 @@
         )
 
     @Test
-    fun setRenderedList_preservesOrdering() = runTest {
-        val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
-        val keys = (1..50).shuffled().map { "$it" }
-        val entries =
-            keys.map {
-                mock<ListEntry> {
-                    val mockRep = mock<NotificationEntry> {
-                        whenever(key).thenReturn(it)
-                        whenever(sbn).thenReturn(mock())
-                        whenever(icons).thenReturn(mock())
+    fun setRenderedList_preservesOrdering() =
+        testScope.runTest {
+            val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+            val keys = (1..50).shuffled().map { "$it" }
+            val entries =
+                keys.map {
+                    mock<ListEntry> {
+                        val mockRep =
+                            mock<NotificationEntry> {
+                                whenever(key).thenReturn(it)
+                                whenever(sbn).thenReturn(mock())
+                                whenever(icons).thenReturn(mock())
+                            }
+                        whenever(representativeEntry).thenReturn(mockRep)
                     }
-                    whenever(representativeEntry).thenReturn(mockRep)
                 }
-            }
-        underTest.setRenderedList(entries)
-        assertThat(notifs).comparingElementsUsing(byKey).containsExactlyElementsIn(keys).inOrder()
-    }
+            underTest.setRenderedList(entries)
+            assertThat(notifs)
+                .comparingElementsUsing(byKey)
+                .containsExactlyElementsIn(keys)
+                .inOrder()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ff5c026..7558974 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
 import android.metrics.LogMaker;
 import android.testing.AndroidTestingRunner;
@@ -169,7 +170,8 @@
             new ActiveNotificationListRepository();
 
     private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
-            new ActiveNotificationsInteractor(mActiveNotificationsRepository);
+            new ActiveNotificationsInteractor(mActiveNotificationsRepository,
+                    StandardTestDispatcher(/* scheduler = */ null, /* name = */ null));
 
     private final SeenNotificationsInteractor mSeenNotificationsInteractor =
             new SeenNotificationsInteractor(mActiveNotificationsRepository);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
index 3d7fb6d..01f4535 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
@@ -17,7 +17,10 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 
 val Kosmos.activeNotificationsInteractor by
-    Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) }
+    Kosmos.Fixture {
+        ActiveNotificationsInteractor(activeNotificationListRepository, testDispatcher)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
index 61a38b8..432464e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
@@ -18,11 +18,13 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 
 val Kosmos.notificationsKeyguardInteractor by Fixture {
     NotificationsKeyguardInteractor(
         repository = notificationsKeyguardViewStateRepository,
+        backgroundDispatcher = testDispatcher,
     )
 }
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-sw600dp/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..be1f081
--- /dev/null
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-sw600dp/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+    <!-- If true, attach the navigation bar to the app during app transition -->
+    <bool name="config_attachNavBarToAppDuringTransition">false</bool>
+</resources>
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values-sw600dp/config.xml b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..be1f081
--- /dev/null
+++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values-sw600dp/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+    <!-- If true, attach the navigation bar to the app during app transition -->
+    <bool name="config_attachNavBarToAppDuringTransition">false</bool>
+</resources>
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values-sw600dp/config.xml b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..be1f081
--- /dev/null
+++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values-sw600dp/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+    <!-- If true, attach the navigation bar to the app during app transition -->
+    <bool name="config_attachNavBarToAppDuringTransition">false</bool>
+</resources>
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values-sw600dp/config.xml b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..be1f081
--- /dev/null
+++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values-sw600dp/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+    <!-- If true, attach the navigation bar to the app during app transition -->
+    <bool name="config_attachNavBarToAppDuringTransition">false</bool>
+</resources>
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
index 8ad21fa..f64f26d 100644
--- a/ravenwood/ravenwood-standard-options.txt
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -1,6 +1,6 @@
 # File containing standard options to HostStubGen for Ravenwood
 
---debug
+# --debug # To enable debug log on consone
 
 # Keep all classes / methods / fields, but make the methods throw.
 --default-throw
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 71a1f01..cce596b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -833,6 +833,13 @@
         }
 
         @Override
+        public boolean isPermissionTransferUserConsented(String packageName, int userId,
+                int associationId) {
+            return mSystemDataTransferProcessor.isPermissionTransferUserConsented(packageName,
+                    userId, associationId);
+        }
+
+        @Override
         public void startSystemDataTransfer(String packageName, int userId, int associationId,
                 ISystemDataTransferCallback callback) {
             mSystemDataTransferProcessor.startSystemDataTransfer(packageName, userId,
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index e5c847a..bd646fa 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -120,7 +120,8 @@
      * Resolve the requested association, throwing if the caller doesn't have
      * adequate permissions.
      */
-    private @NonNull AssociationInfo resolveAssociation(String packageName, int userId,
+    @NonNull
+    private AssociationInfo resolveAssociation(String packageName, int userId,
             int associationId) {
         AssociationInfo association = mAssociationStore.getAssociationById(associationId);
         association = PermissionsUtils.sanitizeWithCallerChecks(mContext, association);
@@ -133,6 +134,20 @@
     }
 
     /**
+     * Return whether the user has consented to the permission transfer for the association.
+     */
+    public boolean isPermissionTransferUserConsented(String packageName, @UserIdInt int userId,
+            int associationId) {
+        resolveAssociation(packageName, userId, associationId);
+
+        PermissionSyncRequest request = getPermissionSyncRequest(associationId);
+        if (request == null) {
+            return false;
+        }
+        return request.isUserConsented();
+    }
+
+    /**
      * Build a PendingIntent of permission sync user consent dialog
      */
     public PendingIntent buildPermissionTransferUserConsentIntent(String packageName,
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 6794f75..3280afdf 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1797,8 +1797,13 @@
                 mFingerprints);
 
         try {
-            dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
-                    FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
+            File userKeys = new File("/data/misc/adb/adb_keys");
+            if (userKeys.exists()) {
+                dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
+                           FileUtils.readTextFile(userKeys, 0, null));
+            } else {
+                Slog.i(TAG, "No user keys on this device");
+            }
         } catch (IOException e) {
             Slog.i(TAG, "Cannot read user keys", e);
         }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 1928780..f2d9759 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -480,6 +480,7 @@
     }
 
     public void systemServicesReady() {
+        mStats.setPowerStatsCollectorEnabled(Flags.streamlinedBatteryStats());
         mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats());
         mWorker.systemServicesReady();
         mStats.systemServicesReady(mContext);
@@ -2648,7 +2649,9 @@
         pw.println("     --proto: output as a binary protobuffer");
         pw.println("     --model power-profile: use the power profile model"
                 + " even if measured energy is available");
-        pw.println("  --sample: collect and dump a sample of stats for debugging purpose");
+        if (Flags.streamlinedBatteryStats()) {
+            pw.println("  --sample: collect and dump a sample of stats for debugging purpose");
+        }
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -2938,10 +2941,10 @@
                     mCpuWakeupStats.dump(new IndentingPrintWriter(pw, "  "),
                             SystemClock.elapsedRealtime());
                     return;
-                } else if ("--sample".equals(arg)) {
+                } else if (Flags.streamlinedBatteryStats() && "--sample".equals(arg)) {
                     dumpStatsSample(pw);
                     return;
-                } else if ("--aggregated".equals(arg)) {
+                } else if (Flags.streamlinedBatteryStats() && "--aggregated".equals(arg)) {
                     dumpAggregatedStats(pw);
                     return;
                 } else if ("--store".equals(arg)) {
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 3ce92bc..0b56146 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -361,7 +361,8 @@
                     && (record.userId == testRecord.userId)
                     && record.intent.filterEquals(testRecord.intent)
                     && isReceiverEquals(receiver, testReceiver)
-                    && testRecord.allReceiversPending()) {
+                    && testRecord.allReceiversPending()
+                    && record.isMatchingRecord(testRecord)) {
                 // Exact match found; perform in-place swap
                 args.arg1 = record;
                 args.argi1 = recordIndex;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index e07631c..ad49991 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -230,6 +230,14 @@
             new AtomicReference<>();
 
     /**
+     * Container for holding the set of broadcast records that matches an enqueueing record.
+     * @see BroadcastRecord#isMatchingRecord(BroadcastRecord)
+     */
+    @GuardedBy("mService")
+    private final AtomicReference<ArrayMap<BroadcastRecord, Boolean>> mMatchingRecordsCache =
+            new AtomicReference<>();
+
+    /**
      * Map from UID to its last known "foreground" state. A UID is considered to be in
      * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}.
      * <p>
@@ -747,6 +755,12 @@
         if (replacedBroadcasts == null) {
             replacedBroadcasts = new ArraySet<>();
         }
+        ArrayMap<BroadcastRecord, Boolean> matchingBroadcasts =
+                mMatchingRecordsCache.getAndSet(null);
+        if (matchingBroadcasts == null) {
+            matchingBroadcasts = new ArrayMap<>();
+        }
+        r.setMatchingRecordsCache(matchingBroadcasts);
         boolean enqueuedBroadcast = false;
 
         for (int i = 0; i < r.receivers.size(); i++) {
@@ -780,6 +794,9 @@
         skipAndCancelReplacedBroadcasts(replacedBroadcasts);
         replacedBroadcasts.clear();
         mReplacedBroadcastsCache.compareAndSet(null, replacedBroadcasts);
+        matchingBroadcasts.clear();
+        r.clearMatchingRecordsCache();
+        mMatchingRecordsCache.compareAndSet(null, matchingBroadcasts);
 
         // If nothing to dispatch, send any pending result immediately
         if (r.receivers.isEmpty() || !enqueuedBroadcast) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 198adcb..99b91ff 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -61,6 +61,7 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.PrintWriterPrinter;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -164,6 +165,11 @@
     @Nullable
     final BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver;
 
+    // Cache of records that are "matching" this. Only used at the time of enqueuing this record
+    // into the queue.
+    @Nullable
+    private ArrayMap<BroadcastRecord, Boolean> mMatchingRecordsCache;
+
     private @Nullable String mCachedToString;
     private @Nullable String mCachedToShortString;
 
@@ -1250,6 +1256,33 @@
         return (terminalCount == 0 && dispatchTime <= 0);
     }
 
+    boolean isMatchingRecord(@NonNull BroadcastRecord record) {
+        final int idx = mMatchingRecordsCache.indexOfKey(record);
+        if (idx > 0) {
+            return mMatchingRecordsCache.valueAt(idx);
+        }
+        // Consider a record to be matching if has the same receivers in the same order.
+        boolean matches = (receivers.size() == record.receivers.size());
+        if (matches) {
+            for (int i = receivers.size() - 1; i >= 0; --i) {
+                if (!isReceiverEquals(receivers.get(i), record.receivers.get(i))) {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+        mMatchingRecordsCache.put(record, matches);
+        return matches;
+    }
+
+    void setMatchingRecordsCache(@NonNull ArrayMap<BroadcastRecord, Boolean> matchingRecordsCache) {
+        mMatchingRecordsCache = matchingRecordsCache;
+    }
+
+    void clearMatchingRecordsCache() {
+        mMatchingRecordsCache = null;
+    }
+
     @Override
     public String toString() {
         if (mCachedToString == null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5d47e35..4f6c6d6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -38,6 +38,7 @@
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
 
+import static com.android.media.audio.Flags.alarmMinVolumeZero;
 import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization;
 import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
 import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
@@ -94,6 +95,7 @@
 import android.hidl.manager.V1_0.IServiceManager;
 import android.media.AudioAttributes;
 import android.media.AudioAttributes.AttributeSystemUsage;
+import android.media.AudioDescriptor;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioDeviceVolumeManager;
@@ -106,6 +108,7 @@
 import android.media.AudioManagerInternal;
 import android.media.AudioMixerAttributes;
 import android.media.AudioPlaybackConfiguration;
+import android.media.AudioProfile;
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
@@ -233,6 +236,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -1191,6 +1195,19 @@
             MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM] = maxAlarmVolume;
         }
 
+        if (alarmMinVolumeZero()) {
+            try {
+                int minAlarmVolume = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_audio_alarm_min_vol);
+                if (minAlarmVolume <= MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]) {
+                    MIN_STREAM_VOLUME[AudioSystem.STREAM_ALARM] = minAlarmVolume;
+                } else {
+                    Log.e(TAG, "Error min alarm volume greater than max alarm volume");
+                }
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Error querying for alarm min volume ", e);
+            }
+        }
         int defaultAlarmVolume = SystemProperties.getInt("ro.config.alarm_vol_default", -1);
         if (defaultAlarmVolume != -1 &&
                 defaultAlarmVolume <= MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]) {
@@ -7673,6 +7690,13 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ConnectionState {}
 
+    /**
+     * Default SAD for a TV using ARC, used when the Amplifier didn't report any SADs.
+     * Represents 2-channel LPCM including all defined sample rates and bit depths.
+     * For the format definition, see Table 34 in the CEA standard CEA-861-D.
+     */
+    private static final byte[] DEFAULT_ARC_AUDIO_DESCRIPTOR = new byte[]{0x09, 0x7f, 0x07};
+
     @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     /**
      * see AudioManager.setWiredDeviceConnectionState()
@@ -7684,6 +7708,27 @@
 
         attributes = retrieveBluetoothAddress(attributes);
 
+        // When using ARC, a TV should use default 2 channel LPCM if the Amplifier didn't
+        // report any SADs. See section 13.15.3 of the HDMI-CEC spec version 1.4b.
+        if (attributes.getType() == AudioDeviceInfo.TYPE_HDMI_ARC
+                && attributes.getRole() == AudioDeviceAttributes.ROLE_OUTPUT
+                && attributes.getAudioDescriptors().isEmpty()) {
+            attributes = new AudioDeviceAttributes(
+                    attributes.getRole(),
+                    attributes.getType(),
+                    attributes.getAddress(),
+                    attributes.getName(),
+                    attributes.getAudioProfiles(),
+                    new ArrayList<AudioDescriptor>(Collections.singletonList(
+                            new AudioDescriptor(
+                                    AudioDescriptor.STANDARD_EDID,
+                                    AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE,
+                                    DEFAULT_ARC_AUDIO_DESCRIPTOR
+                            )
+                    ))
+            );
+        }
+
         if (state != CONNECTION_STATE_CONNECTED
                 && state != CONNECTION_STATE_DISCONNECTED) {
             throw new IllegalArgumentException("Invalid state " + state);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index 8a884b6..42ebc40 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -25,7 +25,7 @@
 import java.io.PrintWriter;
 
 /**
- * Provides max allowed brightness
+ * Provides brightness range constraints
  */
 abstract class BrightnessClamper<T> {
 
@@ -74,6 +74,7 @@
     protected enum Type {
         THERMAL,
         POWER,
-        BEDTIME_MODE
+        BEDTIME_MODE,
+        LUX,
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 765608e..01694dd 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -68,14 +68,14 @@
     private boolean mClamperApplied = false;
 
     public BrightnessClamperController(Handler handler,
-            ClamperChangeListener clamperChangeListener, DisplayDeviceData data,  Context context,
+            ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
             DisplayManagerFlags flags) {
         this(new Injector(), handler, clamperChangeListener, data, context, flags);
     }
 
     @VisibleForTesting
     BrightnessClamperController(Injector injector, Handler handler,
-            ClamperChangeListener clamperChangeListener, DisplayDeviceData data,  Context context,
+            ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
             DisplayManagerFlags flags) {
         mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
         mHandler = handler;
@@ -147,7 +147,8 @@
      * Should be moved to DisplayBrightnessState OR derived from DisplayBrightnessState
      * TODO: b/263362199
      */
-    @BrightnessInfo.BrightnessMaxReason public int getBrightnessMaxReason() {
+    @BrightnessInfo.BrightnessMaxReason
+    public int getBrightnessMaxReason() {
         if (mClamperType == null) {
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
         } else if (mClamperType == Type.THERMAL) {
@@ -241,12 +242,15 @@
                     new BrightnessThermalClamper(handler, clamperChangeListener, data));
             if (flags.isPowerThrottlingClamperEnabled()) {
                 clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
-                            data));
+                        data));
             }
             if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
                 clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
                         clamperChangeListener, data));
             }
+            if (flags.isEvenDimmerEnabled()) {
+                clampers.add(new BrightnessMinClamper(handler, clamperChangeListener, context));
+            }
             return clampers;
         }
 
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java
new file mode 100644
index 0000000..71efca1
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java
@@ -0,0 +1,137 @@
+/*
+ * 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.brightness.clamper;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.utils.DebugUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Class used to prevent the screen brightness dipping below a certain value, based on current
+ * lux conditions.
+ */
+public class BrightnessMinClamper extends BrightnessClamper {
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.BrightnessMinClamper DEBUG && adb reboot'
+    private static final String TAG = "BrightnessMinClamper";
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+
+    private final SettingsObserver mSettingsObserver;
+
+    ContentResolver mContentResolver;
+    private float mNitsLowerBound;
+
+    @VisibleForTesting
+    BrightnessMinClamper(Handler handler,
+            BrightnessClamperController.ClamperChangeListener listener, Context context) {
+        super(handler, listener);
+
+        mContentResolver = context.getContentResolver();
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mHandler.post(() -> {
+            start();
+        });
+    }
+
+    private void recalculateLowerBound() {
+        final int userId = UserHandle.USER_CURRENT;
+        float settingNitsLowerBound = Settings.Secure.getFloatForUser(
+                mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
+                /* def= */ PowerManager.BRIGHTNESS_MIN, userId);
+
+        boolean isActive = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED,
+                /* def= */ 0, userId) == 1;
+
+        // TODO: luxBasedNitsLowerBound = mMinNitsToLuxSpline(currentLux);
+        float luxBasedNitsLowerBound = PowerManager.BRIGHTNESS_MIN;
+        final float nitsLowerBound = Math.max(settingNitsLowerBound, luxBasedNitsLowerBound);
+
+        if (mNitsLowerBound != nitsLowerBound || mIsActive != isActive) {
+            mIsActive = isActive;
+            mNitsLowerBound = nitsLowerBound;
+            if (DEBUG) {
+                Slog.i(TAG, "mIsActive: " + mIsActive);
+            }
+            // TODO: mBrightnessCap = nitsToBrightnessSpline(mNitsLowerBound);
+            mChangeListener.onChanged();
+        }
+    }
+
+    void start() {
+        recalculateLowerBound();
+    }
+
+
+    @Override
+    Type getType() {
+        return Type.LUX;
+    }
+
+    @Override
+    void onDeviceConfigChanged() {
+        // TODO
+    }
+
+    @Override
+    void onDisplayChanged(Object displayData) {
+
+    }
+
+    @Override
+    void stop() {
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    void dump(PrintWriter pw) {
+        pw.println("BrightnessMinClamper:");
+        pw.println("  mBrightnessCap=" + mBrightnessCap);
+        pw.println("  mIsActive=" + mIsActive);
+        pw.println("  mNitsLowerBound=" + mNitsLowerBound);
+        super.dump(pw);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+            mContentResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_MIN_NITS),
+                    false, this);
+            mContentResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_ACTIVATED),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            recalculateLowerBound();
+        }
+    }
+}
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 c71f0cf2..2d5da71 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -78,6 +78,9 @@
             Flags.FLAG_ENABLE_POWER_THROTTLING_CLAMPER,
             Flags::enablePowerThrottlingClamper);
 
+    private final FlagState mEvenDimmerFlagState = new FlagState(
+            Flags.FLAG_EVEN_DIMMER,
+            Flags::evenDimmer);
     private final FlagState mSmallAreaDetectionFlagState = new FlagState(
             com.android.graphics.surfaceflinger.flags.Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
             com.android.graphics.surfaceflinger.flags.Flags::enableSmallAreaDetection);
@@ -174,6 +177,11 @@
         return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled();
     }
 
+    /** Returns whether brightness range is allowed to extend below traditional range. */
+    public boolean isEvenDimmerEnabled() {
+        return mEvenDimmerFlagState.isEnabled();
+    }
+
     public boolean isSmallAreaDetectionEnabled() {
         return mSmallAreaDetectionFlagState.isEnabled();
     }
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 9dfa1ee..1b4d74c 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
@@ -106,6 +106,14 @@
 }
 
 flag {
+    name: "even_dimmer"
+    namespace: "display_manager"
+    description: "Feature flag for extending the brightness below traditional range"
+    bug: "179428400"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "brightness_int_range_user_perception"
     namespace: "display_manager"
     description: "Feature flag for converting the brightness integer range to the user perception scale"
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 2dd2a16..ebc784d 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -369,15 +369,15 @@
         if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
             return;
         }
-        int vendorId = inputDevice.getVendorId();
-        int productId = inputDevice.getProductId();
         if (keyboardSystemEvent == null) {
             Slog.w(TAG, "Invalid keyboard event logging, keycode = " + Arrays.toString(keyCodes)
                     + ", modifier state = " + modifierState);
             return;
         }
         FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
-                vendorId, productId, keyboardSystemEvent.getIntValue(), keyCodes, modifierState);
+                inputDevice.getVendorId(), inputDevice.getProductId(),
+                keyboardSystemEvent.getIntValue(), keyCodes, modifierState,
+                inputDevice.getDeviceBus());
 
         if (DEBUG) {
             Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemEvent.mName);
@@ -402,7 +402,7 @@
         // Push the atom to Statsd
         FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_CONFIGURED,
                 event.isFirstConfiguration(), event.getVendorId(), event.getProductId(),
-                proto.getBytes());
+                proto.getBytes(), event.getDeviceBus());
 
         if (DEBUG) {
             Slog.d(TAG, "Logging Keyboard configuration event: " + event);
@@ -467,6 +467,10 @@
             return mInputDevice.getProductId();
         }
 
+        public int getDeviceBus() {
+            return mInputDevice.getDeviceBus();
+        }
+
         public boolean isFirstConfiguration() {
             return mIsFirstConfiguration;
         }
@@ -479,6 +483,7 @@
         public String toString() {
             return "InputDevice = {VendorId = " + Integer.toHexString(getVendorId())
                     + ", ProductId = " + Integer.toHexString(getProductId())
+                    + ", Device Bus = " + Integer.toHexString(getDeviceBus())
                     + "}, isFirstConfiguration = " + mIsFirstConfiguration
                     + ", LayoutConfigurations = " + mLayoutConfigurations;
         }
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index 749d6b0..dcb86a7 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -22,6 +22,8 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.UiThread;
+import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
 import android.hardware.input.InputManagerGlobal;
 import android.os.Handler;
 import android.os.IBinder;
@@ -66,6 +68,7 @@
     private final Looper mLooper;
     private final InputManagerInternal mInputManagerInternal;
     private final WindowManagerInternal mWindowManagerInternal;
+    private final PackageManagerInternal mPackageManagerInternal;
 
     private ArrayList<MotionEvent> mHandwritingBuffer;
     private InputEventReceiver mHandwritingEventReceiver;
@@ -75,6 +78,7 @@
     // when set, package names are used for handwriting delegation.
     private @Nullable String mDelegatePackageName;
     private @Nullable String mDelegatorPackageName;
+    private boolean mDelegatorFromDefaultHomePackage;
     private Runnable mDelegationIdleTimeoutRunnable;
     private Handler mDelegationIdleTimeoutHandler;
 
@@ -88,6 +92,7 @@
         mCurrentDisplayId = Display.INVALID_DISPLAY;
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mCurrentRequestId = 0;
         mInkWindowInitRunnable = inkWindowInitRunnable;
     }
@@ -151,9 +156,20 @@
      * @see InputMethodManager#prepareStylusHandwritingDelegation(View, String)
      */
     void prepareStylusHandwritingDelegation(
-            @NonNull String delegatePackageName, @NonNull String delegatorPackageName) {
+            int userId, @NonNull String delegatePackageName, @NonNull String delegatorPackageName) {
         mDelegatePackageName = delegatePackageName;
         mDelegatorPackageName = delegatorPackageName;
+        mDelegatorFromDefaultHomePackage = false;
+        // mDelegatorFromDefaultHomeActivity is only used in the cross-package delegation case.
+        // For same-package delegation, it doesn't need to be checked.
+        if (!delegatorPackageName.equals(delegatePackageName)) {
+            ComponentName defaultHomeActivity =
+                    mPackageManagerInternal.getDefaultHomeActivity(userId);
+            if (defaultHomeActivity != null) {
+                mDelegatorFromDefaultHomePackage =
+                        delegatorPackageName.equals(defaultHomeActivity.getPackageName());
+            }
+        }
         if (mHandwritingBuffer == null) {
             mHandwritingBuffer = new ArrayList<>(getHandwritingBufferSize());
         } else {
@@ -170,6 +186,10 @@
         return mDelegatorPackageName;
     }
 
+    boolean isDelegatorFromDefaultHomePackage() {
+        return mDelegatorFromDefaultHomePackage;
+    }
+
     private void scheduleHandwritingDelegationTimeout() {
         if (mDelegationIdleTimeoutHandler == null) {
             mDelegationIdleTimeoutHandler = new Handler(mLooper);
@@ -210,6 +230,7 @@
         mDelegationIdleTimeoutRunnable = null;
         mDelegatorPackageName = null;
         mDelegatePackageName = null;
+        mDelegatorFromDefaultHomePackage = false;
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ddb32fe..a0bc7c2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2085,7 +2085,7 @@
                     new ArrayMap<>();
             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
-                    methodList, directBootAwareness, mSettings.getEnabledInputMethodNames());
+                    methodList, directBootAwareness);
             settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
         }
         // filter caller's access to input methods
@@ -2713,10 +2713,10 @@
     }
 
     @AnyThread
-    void schedulePrepareStylusHandwritingDelegation(
+    void schedulePrepareStylusHandwritingDelegation(@UserIdInt int userId,
             @NonNull String delegatePackageName, @NonNull String delegatorPackageName) {
         mHandler.obtainMessage(
-                MSG_PREPARE_HANDWRITING_DELEGATION,
+                MSG_PREPARE_HANDWRITING_DELEGATION, userId, 0 /* unused */,
                 new Pair<>(delegatePackageName, delegatorPackageName)).sendToTarget();
     }
 
@@ -3433,7 +3433,8 @@
             Slog.w(TAG, "prepareStylusHandwritingDelegation() fail");
             throw new IllegalArgumentException("Delegator doesn't match Uid");
         }
-        schedulePrepareStylusHandwritingDelegation(delegatePackageName, delegatorPackageName);
+        schedulePrepareStylusHandwritingDelegation(
+                userId, delegatePackageName, delegatorPackageName);
     }
 
     @Override
@@ -3441,13 +3442,14 @@
             @NonNull IInputMethodClient client,
             @UserIdInt int userId,
             @NonNull String delegatePackageName,
-            @NonNull String delegatorPackageName) {
+            @NonNull String delegatorPackageName,
+            @InputMethodManager.HandwritingDelegateFlags int flags) {
         if (!isStylusHandwritingEnabled(mContext, userId)) {
             Slog.w(TAG, "Can not accept stylus handwriting delegation. Stylus handwriting"
                     + " pref is disabled for user: " + userId);
             return false;
         }
-        if (!verifyDelegator(client, delegatePackageName, delegatorPackageName)) {
+        if (!verifyDelegator(client, delegatePackageName, delegatorPackageName, flags)) {
             return false;
         }
 
@@ -3471,14 +3473,20 @@
     private boolean verifyDelegator(
             @NonNull IInputMethodClient client,
             @NonNull String delegatePackageName,
-            @NonNull String delegatorPackageName) {
+            @NonNull String delegatorPackageName,
+            @InputMethodManager.HandwritingDelegateFlags int flags) {
         if (!verifyClientAndPackageMatch(client, delegatePackageName)) {
             Slog.w(TAG, "Delegate package does not belong to the same user. Ignoring"
                     + " startStylusHandwriting");
             return false;
         }
         synchronized (ImfLock.class) {
-            if (!delegatorPackageName.equals(mHwController.getDelegatorPackageName())) {
+            boolean homeDelegatorAllowed =
+                    (flags & InputMethodManager.HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED)
+                            != 0;
+            if (!delegatorPackageName.equals(mHwController.getDelegatorPackageName())
+                    && !(homeDelegatorAllowed
+                            && mHwController.isDelegatorFromDefaultHomePackage())) {
                 Slog.w(TAG,
                         "Delegator package does not match. Ignoring startStylusHandwriting");
                 return false;
@@ -4200,7 +4208,7 @@
                     new ArrayMap<>();
             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
-                    methodList, DirectBootAwareness.AUTO, mSettings.getEnabledInputMethodNames());
+                    methodList, DirectBootAwareness.AUTO);
             final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap,
                     userId, false);
             settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap,
@@ -4924,9 +4932,10 @@
             }
             case MSG_PREPARE_HANDWRITING_DELEGATION:
                 synchronized (ImfLock.class) {
+                    int userId = msg.arg1;
                     String delegate = (String) ((Pair) msg.obj).first;
                     String delegator = (String) ((Pair) msg.obj).second;
-                    mHwController.prepareStylusHandwritingDelegation(delegate, delegator);
+                    mHwController.prepareStylusHandwritingDelegation(userId, delegate, delegator);
                 }
                 return true;
             case MSG_START_HANDWRITING:
@@ -5025,7 +5034,7 @@
     static void queryInputMethodServicesInternal(Context context,
             @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
             ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
-            @DirectBootAwareness int directBootAwareness, List<String> enabledInputMethodList) {
+            @DirectBootAwareness int directBootAwareness) {
         final Context userAwareContext = context.getUserId() == userId
                 ? context
                 : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
@@ -5058,6 +5067,11 @@
         methodList.ensureCapacity(services.size());
         methodMap.ensureCapacity(services.size());
 
+        // Note: This is a temporary solution for Bug 261723412.  If there is any better solution,
+        // we should remove this data dependency.
+        final List<String> enabledInputMethodList =
+                InputMethodUtils.getEnabledInputMethodIdsForFiltering(context, userId);
+
         filterInputMethodServices(additionalSubtypeMap, methodMap, methodList,
                 enabledInputMethodList, userAwareContext, services);
     }
@@ -5124,8 +5138,7 @@
         mMyPackageMonitor.clearKnownImePackageNamesLocked();
 
         queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
-                mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO,
-                mSettings.getEnabledInputMethodNames());
+                mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO);
 
         // Construct the set of possible IME packages for onPackageChanged() to avoid false
         // negatives when the package state remains to be the same but only the component state is
@@ -5438,47 +5451,21 @@
      */
     @GuardedBy("ImfLock.class")
     private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) {
-        final String imeId = mSettings.getSelectedInputMethodForUser(userId);
-        if (TextUtils.isEmpty(imeId)) {
-            Slog.e(TAG, "No default input method found for userId " + userId);
-            return null;
+        if (userId == mSettings.getCurrentUserId()) {
+            return mMethodMap.get(mSettings.getSelectedInputMethod());
         }
 
-        InputMethodInfo curInputMethodInfo;
-        if (userId == mSettings.getCurrentUserId()
-                && (curInputMethodInfo = mMethodMap.get(imeId)) != null) {
-            // clone the InputMethodInfo before returning.
-            return new InputMethodInfo(curInputMethodInfo);
-        }
-
+        final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+        final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
         final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = new ArrayMap<>();
         AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
-        Context userAwareContext =
-                mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
-
-        final int flags = PackageManager.GET_META_DATA
-                | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                | PackageManager.MATCH_DIRECT_BOOT_AUTO;
-        final List<ResolveInfo> services =
-                userAwareContext.getPackageManager().queryIntentServicesAsUser(
-                        new Intent(InputMethod.SERVICE_INTERFACE),
-                        PackageManager.ResolveInfoFlags.of(flags),
-                        userId);
-        for (ResolveInfo ri : services) {
-            final String imeIdResolved = InputMethodInfo.computeId(ri);
-            if (imeId.equals(imeIdResolved)) {
-                try {
-                    return new InputMethodInfo(
-                            userAwareContext, ri, additionalSubtypeMap.get(imeId));
-                } catch (Exception e) {
-                    Slog.wtf(TAG, "Unable to load input method " + imeId, e);
-                }
-            }
-        }
-        // we didn't find the InputMethodInfo for imeId. This shouldn't happen.
-        Slog.e(TAG, "Error while locating input method info for imeId: " + imeId);
-        return null;
+        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+                methodList, DirectBootAwareness.AUTO);
+        InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId,
+                true /* copyOnWrite */);
+        return methodMap.get(settings.getSelectedInputMethod());
     }
+
     private ArrayMap<String, InputMethodInfo> queryMethodMapForUser(@UserIdInt int userId) {
         final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
         final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
@@ -5486,8 +5473,7 @@
                 new ArrayMap<>();
         AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
         queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
-                methodMap, methodList, DirectBootAwareness.AUTO,
-                mSettings.getEnabledInputMethodNames());
+                methodMap, methodList, DirectBootAwareness.AUTO);
         return methodMap;
     }
 
@@ -6511,8 +6497,7 @@
                                 new ArrayMap<>();
                         AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
                         queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
-                                methodMap, methodList, DirectBootAwareness.AUTO,
-                                mSettings.getEnabledInputMethodNames());
+                                methodMap, methodList, DirectBootAwareness.AUTO);
                         final InputMethodSettings settings = new InputMethodSettings(mContext,
                                 methodMap, userId, false);
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index c97d8e2..984ae1f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -330,17 +330,11 @@
 
         @Nullable
         private String getString(@NonNull String key, @Nullable String defaultValue) {
-            return getStringForUser(key, defaultValue, mCurrentUserId);
-        }
-
-        @Nullable
-        private String getStringForUser(
-                @NonNull String key, @Nullable String defaultValue, @UserIdInt int userId) {
             final String result;
             if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
                 result = mCopyOnWriteDataStore.get(key);
             } else {
-                result = Settings.Secure.getStringForUser(mResolver, key, userId);
+                result = Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId);
             }
             return result != null ? result : defaultValue;
         }
@@ -756,16 +750,6 @@
             return imi;
         }
 
-        @Nullable
-        String getSelectedInputMethodForUser(@UserIdInt int userId) {
-            final String imi =
-                    getStringForUser(Settings.Secure.DEFAULT_INPUT_METHOD, null, userId);
-            if (DEBUG) {
-                Slog.d(TAG, "getSelectedInputMethodForUserStr: " + imi);
-            }
-            return imi;
-        }
-
         void putDefaultVoiceInputMethod(String imeId) {
             if (DEBUG) {
                 Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
@@ -1029,6 +1013,44 @@
     }
 
     /**
+     * Returns a list of enabled IME IDs to address Bug 261723412.
+     *
+     * <p>This is a temporary workaround until we come up with a better solution. Do not use this
+     * for anything other than Bug 261723412.</p>
+     *
+     * @param context {@link Context} object to query secure settings.
+     * @param userId User ID to query about.
+     * @return A list of enabled IME IDs.
+     */
+    @NonNull
+    static List<String> getEnabledInputMethodIdsForFiltering(@NonNull Context context,
+            @UserIdInt int userId) {
+        final String enabledInputMethodsStr = TextUtils.nullIfEmpty(
+                Settings.Secure.getStringForUser(
+                        context.getContentResolver(),
+                        Settings.Secure.ENABLED_INPUT_METHODS,
+                        userId));
+        if (enabledInputMethodsStr == null) {
+            return List.of();
+        }
+        final TextUtils.SimpleStringSplitter inputMethodSplitter =
+                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+        final TextUtils.SimpleStringSplitter subtypeSplitter =
+                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+        inputMethodSplitter.setString(enabledInputMethodsStr);
+        final ArrayList<String> result = new ArrayList<>();
+        while (inputMethodSplitter.hasNext()) {
+            String nextImsStr = inputMethodSplitter.next();
+            subtypeSplitter.setString(nextImsStr);
+            if (subtypeSplitter.hasNext()) {
+                // The first element is ime id.
+                result.add(subtypeSplitter.next());
+            }
+        }
+        return result;
+    }
+
+    /**
      * Convert the input method ID to a component name
      *
      * @param id A unique ID for this input method.
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 57e424d..49095ce 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -246,7 +246,7 @@
     private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
 
     private static final boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
-            android.security.Flags.fixUnlockedDeviceRequiredKeys();
+            android.security.Flags.fixUnlockedDeviceRequiredKeysV2();
 
     // Duration that LockSettingsService will store the gatekeeper password for. This allows
     // multiple biometric enrollments without prompting the user to enter their password via
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 27df00f..246d68d 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -115,7 +115,9 @@
         mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener);
         mBluetoothRouteController =
                 new AudioPoliciesBluetoothRouteController(
-                        mContext, btAdapter, this::rebuildAvailableRoutes);
+                        mContext, btAdapter, this::rebuildAvailableRoutesAndNotify);
+        // Just build routes but don't notify. The caller may not expect the listener to be invoked
+        // before this constructor has finished executing.
         rebuildAvailableRoutes();
     }
 
@@ -203,7 +205,7 @@
     public synchronized boolean updateVolume(int volume) {
         // TODO: b/305199571 - Optimize so that we only update the volume of the selected route. We
         // don't need to rebuild all available routes.
-        rebuildAvailableRoutes();
+        rebuildAvailableRoutesAndNotify();
         return true;
     }
 
@@ -216,10 +218,15 @@
             AudioAttributes attributes, List<AudioDeviceAttributes> unusedAudioDeviceAttributes) {
         if (attributes.getUsage() == AudioAttributes.USAGE_MEDIA) {
             // We only care about the media usage. Ignore everything else.
-            rebuildAvailableRoutes();
+            rebuildAvailableRoutesAndNotify();
         }
     }
 
+    private synchronized void rebuildAvailableRoutesAndNotify() {
+        rebuildAvailableRoutes();
+        mOnDeviceRouteChangedListener.onDeviceRouteChanged();
+    }
+
     @RequiresPermission(
             anyOf = {
                 Manifest.permission.MODIFY_AUDIO_ROUTING,
@@ -298,7 +305,6 @@
                 .map(MediaRoute2InfoHolder::createForInactiveBluetoothRoute)
                 .forEach(
                         it -> mRouteIdToAvailableDeviceRoutes.put(it.mMediaRoute2Info.getId(), it));
-        mOnDeviceRouteChangedListener.onDeviceRouteChanged();
     }
 
     private MediaRoute2InfoHolder createPlaceholderBuiltinSpeakerRoute() {
@@ -469,7 +475,7 @@
                     // of this, when the user connects a bluetooth device or a wired headset, the
                     // new device becomes the active route, which is the traditional behavior.
                     mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
-                    rebuildAvailableRoutes();
+                    rebuildAvailableRoutesAndNotify();
                     break;
                 }
             }
@@ -484,7 +490,7 @@
         public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
             for (AudioDeviceInfo deviceInfo : removedDevices) {
                 if (AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.contains(deviceInfo.getType())) {
-                    rebuildAvailableRoutes();
+                    rebuildAvailableRoutesAndNotify();
                     break;
                 }
             }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 448f215..ba2cf1d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2861,10 +2861,11 @@
                     ? request.getRemovedInfo().mArgs : null;
             if (args != null) {
                 if (!killApp) {
-                    // If we didn't kill the app, defer the deletion of code/resource files, since
-                    // they may still be in use by the running application. This mitigates problems
-                    // in cases where resources or code is loaded by a new Activity before
-                    // ApplicationInfo changes have propagated to all application threads.
+                    // If we didn't kill the app, defer the deletion of code/resource files,
+                    // since the old code/resource files may still be in use by the running
+                    // application. This mitigates problems and cases where resources or
+                    // code is loaded by a new Activity before ApplicationInfo changes have
+                    // propagated to all application threads.
                     mPm.scheduleDeferredNoKillPostDelete(args);
                 } else {
                     mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 233bf4f..adb6906 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -523,6 +523,9 @@
     private static final String PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE =
             "is_update_ownership_enforcement_available";
 
+    private static final String PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED =
+            "deferred_no_kill_post_delete_delay_ms_extended";
+
     /**
      * The default response for package verification timeout.
      *
@@ -924,7 +927,11 @@
 
     static final int WRITE_USER_PACKAGE_RESTRICTIONS = 30;
 
-    static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
+    private static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
+
+    private static final long DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED =
+            TimeUnit.DAYS.toMillis(1);
+
     private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
     private static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS = 1000;
 
@@ -1288,7 +1295,19 @@
 
     void scheduleDeferredNoKillPostDelete(InstallArgs args) {
         Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
-        mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
+        // If the feature flag is on, retain the old files for a day. Otherwise, delete the old
+        // files after a few seconds.
+        long deleteDelayMillis = DEFERRED_NO_KILL_POST_DELETE_DELAY_MS;
+        if (Flags.improveInstallDontKill()) {
+            deleteDelayMillis = Binder.withCleanCallingIdentity(() -> {
+                return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                        /* name= */ PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED,
+                        /* defaultValue= */ DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED);
+            });
+            Slog.w(TAG, "Delaying the deletion of <" + args.getCodePath() + "> by "
+                    + deleteDelayMillis + "ms or till the next reboot");
+        }
+        mHandler.sendMessageDelayed(message, deleteDelayMillis);
     }
 
     void schedulePruneUnusedStaticSharedLibraries(boolean delay) {
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 6d58d34..8adb566 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -23,10 +23,10 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.RecoverySystem;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -35,6 +35,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.Slogf;
 
 import java.io.File;
 import java.io.IOException;
@@ -43,7 +44,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * Helper class for preparing and destroying user storage
@@ -65,31 +65,37 @@
     /**
      * Prepare storage areas for given user on all mounted devices.
      */
-    void prepareUserData(int userId, int userSerial, int flags) {
+    void prepareUserData(UserInfo userInfo, int flags) {
         synchronized (mInstallLock) {
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             /*
              * Internal storage must be prepared before adoptable storage, since the user's volume
              * keys are stored in their internal storage.
              */
-            prepareUserDataLI(null /* internal storage */, userId, userSerial, flags, true);
+            prepareUserDataLI(null /* internal storage */, userInfo, flags, true);
             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
                 final String volumeUuid = vol.getFsUuid();
                 if (volumeUuid != null) {
-                    prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+                    prepareUserDataLI(volumeUuid, userInfo, flags, true);
                 }
             }
         }
     }
 
-    private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+    private void prepareUserDataLI(String volumeUuid, UserInfo userInfo, int flags,
             boolean allowRecover) {
-        // Prepare storage and verify that serial numbers are consistent; if
-        // there's a mismatch we need to destroy to avoid leaking data
+        final int userId = userInfo.id;
+        final int userSerial = userInfo.serialNumber;
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        final boolean isNewUser = userInfo.lastLoggedInTime == 0;
+        Slogf.d(TAG, "Preparing user data; volumeUuid=%s, userId=%d, flags=0x%x, isNewUser=%s",
+                volumeUuid, userId, flags, isNewUser);
         try {
+            // Prepare CE and/or DE storage.
             storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
 
+            // Ensure that the data directories of a removed user with the same ID are not being
+            // reused.  New users must get fresh data directories, to avoid leaking data.
             if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
                 enforceSerialNumber(getDataUserDeDirectory(volumeUuid, userId), userSerial);
                 if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
@@ -103,9 +109,10 @@
                 }
             }
 
+            // Prepare the app data directories.
             mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
 
-            // CE storage is available after they are prepared.
+            // If applicable, record that the system user's CE storage has been prepared.
             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 &&
                     (userId == UserHandle.USER_SYSTEM)) {
                 String propertyName = "sys.user." + userId + ".ce_available";
@@ -113,20 +120,31 @@
                 SystemProperties.set(propertyName, "true");
             }
         } catch (Exception e) {
-            logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
-                    + " because we failed to prepare: " + e);
-            destroyUserDataLI(volumeUuid, userId, flags);
-
+            // Failed to prepare user data.  For new users, specifically users that haven't ever
+            // been unlocked, destroy the user data, and try again (if not already retried).  This
+            // might be effective at resolving some errors, such as stale directories from a reused
+            // user ID.  Don't auto-destroy data for existing users, since issues with existing
+            // users might be fixable via an OTA without having to wipe the user's data.
+            if (isNewUser) {
+                logCriticalInfo(Log.ERROR, "Destroying user " + userId + " on volume " + volumeUuid
+                        + " because we failed to prepare: " + e);
+                destroyUserDataLI(volumeUuid, userId, flags);
+            } else {
+                logCriticalInfo(Log.ERROR, "Failed to prepare user " + userId + " on volume "
+                        + volumeUuid + ": " + e);
+            }
             if (allowRecover) {
                 // Try one last time; if we fail again we're really in trouble
-                prepareUserDataLI(volumeUuid, userId, userSerial,
-                    flags | StorageManager.FLAG_STORAGE_DE, false);
+                prepareUserDataLI(volumeUuid, userInfo, flags | StorageManager.FLAG_STORAGE_DE,
+                        false);
             } else {
+                // If internal storage of the system user fails to prepare on first boot, then
+                // things are *really* broken, so we might as well reboot to recovery right away.
                 try {
                     Log.wtf(TAG, "prepareUserData failed for user " + userId, e);
-                    if (userId == UserHandle.USER_SYSTEM) {
+                    if (isNewUser && userId == UserHandle.USER_SYSTEM && volumeUuid == null) {
                         RecoverySystem.rebootPromptAndWipeUserData(mContext,
-                                "prepareUserData failed for system user");
+                                "failed to prepare internal storage for system user");
                     }
                 } catch (IOException e2) {
                     throw new RuntimeException("error rebooting into recovery", e2);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b89b4a2..1393121 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -5124,8 +5124,7 @@
             // unlocked.  We do this to ensure that CE storage isn't prepared before the CE key is
             // saved to disk.  This also matches what is done for user 0.
             t.traceBegin("prepareUserData");
-            mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
-                    StorageManager.FLAG_STORAGE_DE);
+            mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_DE);
             t.traceEnd();
 
             t.traceBegin("LSS.createNewUser");
@@ -6387,12 +6386,11 @@
         }
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("onBeforeStartUser-" + userId);
-        final int userSerial = userInfo.serialNumber;
         // Migrate only if build fingerprints mismatch
         boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals(
                 userInfo.lastLoggedInFingerprint);
         t.traceBegin("prepareUserData");
-        mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_DE);
         t.traceEnd();
         t.traceBegin("reconcileAppsData");
         getPackageManagerInternal().reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE,
@@ -6418,14 +6416,13 @@
         if (userInfo == null) {
             return;
         }
-        final int userSerial = userInfo.serialNumber;
         // Migrate only if build fingerprints mismatch
         boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals(
                 userInfo.lastLoggedInFingerprint);
 
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("prepareUserData-" + userId);
-        mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_CE);
         t.traceEnd();
 
         StorageManagerInternal smInternal = LocalServices.getService(StorageManagerInternal.class);
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d0c346a..57f4a5d 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -337,7 +337,8 @@
                     0, // deprecated, used to be durationIncludingSleepMs
                     0, // optimizedPackagesCount
                     0, // packagesDependingOnBootClasspathCount
-                    0); // totalPackagesCount
+                    0, // totalPackagesCount
+                    ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_UNKNOWN);
         }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e817df8..30bce2f4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -22,6 +22,9 @@
 import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.AppOpsManager.OP_TOAST_WINDOW;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -563,6 +566,7 @@
     int mShortPressOnWindowBehavior;
     int mPowerVolUpBehavior;
     boolean mStylusButtonsEnabled = true;
+    boolean mKidsModeEnabled;
     boolean mHasSoftInput = false;
     boolean mUseTvRouting;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
@@ -887,6 +891,9 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.STYLUS_BUTTONS_ENABLED), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.NAV_BAR_KIDS_MODE), false, this,
+                    UserHandle.USER_ALL);
             updateSettings();
         }
 
@@ -2964,12 +2971,44 @@
             mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
                     Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
             mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled);
+
+            final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
+            if (mKidsModeEnabled != kidsModeEnabled) {
+                mKidsModeEnabled = kidsModeEnabled;
+                updateKidsModeSettings();
+            }
         }
         if (updateRotation) {
             updateRotation(true);
         }
     }
 
+    private void updateKidsModeSettings() {
+        if (mKidsModeEnabled) {
+            // Needed since many Kids apps aren't optimised to support both orientations and it
+            // will be hard for kids to understand the app compat mode.
+            // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once
+            //                  possible.
+            if (mContext.getResources().getBoolean(R.bool.config_reverseDefaultRotation)) {
+                mWindowManagerInternal.setOrientationRequestPolicy(
+                        true /* isIgnoreOrientationRequestDisabled */,
+                        new int[]{SCREEN_ORIENTATION_LANDSCAPE,
+                                SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
+                        new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
+                                SCREEN_ORIENTATION_SENSOR_LANDSCAPE});
+            } else {
+                mWindowManagerInternal.setOrientationRequestPolicy(
+                        true /* isIgnoreOrientationRequestDisabled */,
+                        null /* fromOrientations */, null /* toOrientations */);
+            }
+        } else {
+            mWindowManagerInternal.setOrientationRequestPolicy(
+                    false /* isIgnoreOrientationRequestDisabled */,
+                    null /* fromOrientations */, null /* toOrientations */);
+        }
+    }
+
     private DreamManagerInternal getDreamManagerInternal() {
         if (mDreamManagerInternal == null) {
             // If mDreamManagerInternal is null, attempt to re-fetch it.
@@ -3433,7 +3472,7 @@
                 }
                 break;
             case KeyEvent.KEYCODE_DEL:
-            case KeyEvent.KEYCODE_GRAVE:
+            case KeyEvent.KEYCODE_ESCAPE:
                 if (firstDown && event.isMetaPressed()) {
                     logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
                     injectBackGesture(event.getDownTime());
@@ -3932,7 +3971,9 @@
                 }
                 return true;
             case KeyEvent.KEYCODE_ESCAPE:
-                if (down && repeatCount == 0) {
+                if (down
+                        && KeyEvent.metaStateHasNoModifiers(metaState)
+                        && repeatCount == 0) {
                     mContext.closeSystemDialogs();
                 }
                 return true;
@@ -6421,6 +6462,7 @@
                 pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
                 pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
+        pw.print(prefix); pw.print("mKidsModeEnabled="); pw.println(mKidsModeEnabled);
 
         mHapticFeedbackVibrationProvider.dump(prefix, pw);
         mGlobalKeyManager.dump(prefix, pw);
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
index fbad762..fa94b43 100644
--- a/services/core/java/com/android/server/power/LowPowerStandbyController.java
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -33,6 +33,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -146,6 +147,7 @@
             this::onStandbyTimeoutExpired;
     private final LowPowerStandbyControllerInternal mLocalService = new LocalService();
     private final SparseIntArray mUidAllowedReasons = new SparseIntArray();
+    private final List<String> mLowPowerStandbyManagingPackages = new ArrayList<>();
     private final List<StandbyPortsLock> mStandbyPortLocks = new ArrayList<>();
 
     @GuardedBy("mLock")
@@ -370,6 +372,14 @@
                 return;
             }
 
+            List<PackageInfo> manageLowPowerStandbyPackages = mContext.getPackageManager()
+                    .getPackagesHoldingPermissions(new String[]{
+                            Manifest.permission.MANAGE_LOW_POWER_STANDBY
+                    }, PackageManager.MATCH_SYSTEM_ONLY);
+            for (PackageInfo packageInfo : manageLowPowerStandbyPackages) {
+                mLowPowerStandbyManagingPackages.add(packageInfo.packageName);
+            }
+
             mAlarmManager = mContext.getSystemService(AlarmManager.class);
             mPowerManager = mContext.getSystemService(PowerManager.class);
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -756,9 +766,7 @@
             Slog.d(TAG, "notifyEnabledChangedLocked, mIsEnabled=" + mIsEnabled);
         }
 
-        final Intent intent = new Intent(PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        sendExplicitBroadcast(PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
     }
 
     @GuardedBy("mLock")
@@ -772,10 +780,7 @@
             Slog.d(TAG, "notifyPolicyChanged, policy=" + policy);
         }
 
-        final Intent intent = new Intent(
-                PowerManager.ACTION_LOW_POWER_STANDBY_POLICY_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        sendExplicitBroadcast(PowerManager.ACTION_LOW_POWER_STANDBY_POLICY_CHANGED);
     }
 
     private void onStandbyTimeoutExpired() {
@@ -787,6 +792,22 @@
         }
     }
 
+    private void sendExplicitBroadcast(String intentType) {
+        final Intent intent = new Intent(intentType);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+        // Send explicit broadcast to holders of MANAGE_LOW_POWER_STANDBY
+        final Intent privilegedIntent = new Intent(intentType);
+        privilegedIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        for (String packageName : mLowPowerStandbyManagingPackages) {
+            final Intent explicitIntent = new Intent(privilegedIntent);
+            explicitIntent.setPackage(packageName);
+            mContext.sendBroadcastAsUser(explicitIntent, UserHandle.ALL,
+                    Manifest.permission.MANAGE_LOW_POWER_STANDBY);
+        }
+    }
+
     @GuardedBy("mLock")
     private void enqueueNotifyActiveChangedLocked() {
         final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ACTIVE_CHANGED, mIsActive);
@@ -1360,7 +1381,7 @@
         }
 
         final Intent intent = new Intent(PowerManager.ACTION_LOW_POWER_STANDBY_PORTS_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                 Manifest.permission.MANAGE_LOW_POWER_STANDBY);
     }
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 0491c14..b8d26d9 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -286,6 +286,7 @@
     private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
     private int[] mCpuPowerBracketMap;
     private final CpuPowerStatsCollector mCpuPowerStatsCollector;
+    private boolean mPowerStatsCollectorEnabled;
 
     public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
         return mKernelMemoryStats;
@@ -601,6 +602,10 @@
     @SuppressWarnings("GuardedBy")    // errorprone false positive on getProcStateTimeCounter
     @VisibleForTesting
     public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
+        if (mPowerStatsCollectorEnabled) {
+            return;
+        }
+
         ensureKernelSingleUidTimeReaderLocked();
 
         final Uid u = getUidStatsLocked(uid);
@@ -653,8 +658,9 @@
      */
     @SuppressWarnings("GuardedBy")    // errorprone false positive on getProcStateTimeCounter
     public void updateCpuTimesForAllUids() {
-        if (mCpuPowerStatsCollector != null) {
+        if (mPowerStatsCollectorEnabled && mCpuPowerStatsCollector != null) {
             mCpuPowerStatsCollector.schedule();
+            return;
         }
 
         synchronized (BatteryStatsImpl.this) {
@@ -712,7 +718,7 @@
 
     @GuardedBy("this")
     private void ensureKernelSingleUidTimeReaderLocked() {
-        if (mKernelSingleUidTimeReader != null) {
+        if (mPowerStatsCollectorEnabled || mKernelSingleUidTimeReader != null) {
             return;
         }
 
@@ -8213,6 +8219,10 @@
 
         @GuardedBy("mBsi")
         private void ensureMultiStateCounters(long timestampMs) {
+            if (mBsi.mPowerStatsCollectorEnabled) {
+                throw new IllegalStateException("Multi-state counters used in streamlined mode");
+            }
+
             if (mProcStateTimeMs == null) {
                 mProcStateTimeMs =
                         new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
@@ -10511,7 +10521,7 @@
                     mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
                 }
 
-                if (mBsi.trackPerProcStateCpuTimes()) {
+                if (!mBsi.mPowerStatsCollectorEnabled && mBsi.trackPerProcStateCpuTimes()) {
                     mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs, uptimeMs);
 
                     LongArrayMultiStateCounter onBatteryCounter =
@@ -14211,7 +14221,7 @@
             mCpuUidFreqTimeReader.onSystemReady();
         }
         if (mCpuPowerStatsCollector != null) {
-            mCpuPowerStatsCollector.onSystemReady();
+            mCpuPowerStatsCollector.setEnabled(mPowerStatsCollectorEnabled);
         }
         mSystemReady = true;
     }
@@ -15232,6 +15242,15 @@
         return mCpuUidFreqTimeReader.isFastCpuTimesReader();
     }
 
+    /**
+     * Enables or disables the PowerStatsCollector mode.
+     */
+    public void setPowerStatsCollectorEnabled(boolean enabled) {
+        synchronized (this) {
+            mPowerStatsCollectorEnabled = enabled;
+        }
+    }
+
     @GuardedBy("this")
     public void systemServicesReady(Context context) {
         mConstants.startObserving(context.getContentResolver());
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index c05407c..4442845 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -35,7 +35,6 @@
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.PowerStats;
 import com.android.server.LocalServices;
-import com.android.server.power.optimization.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -273,13 +272,6 @@
         mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
     }
 
-    /**
-     * Initializes the collector during the boot sequence.
-     */
-    public void onSystemReady() {
-        setEnabled(Flags.streamlinedBatteryStats());
-    }
-
     private boolean ensureInitialized() {
         if (mIsInitialized) {
             return true;
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f82f08b..2bf7075 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -827,7 +827,7 @@
             }
 
             final boolean trusted;
-            if (android.security.Flags.fixUnlockedDeviceRequiredKeys()) {
+            if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
                 trusted = getUserTrustStateInner(id) == TrustState.TRUSTED;
             } else {
                 trusted = aggregateIsTrusted(id);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 75e6faf..6e9219a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5262,7 +5262,7 @@
 
     boolean canAffectSystemUiFlags() {
         return task != null && task.canAffectSystemUiFlags() && isVisible()
-                && !inPinnedWindowingMode();
+                && !mWaitForEnteringPinnedMode && !inPinnedWindowingMode();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index e6bbd52..c089d10 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -740,6 +740,8 @@
         private final Handler mHandler;
         private final String mName;
 
+        private boolean mInsetsAnimationRunning;
+
         Host(Handler handler, String name) {
             mHandler = handler;
             mName = name;
@@ -841,5 +843,10 @@
         public IBinder getWindowToken() {
             return null;
         }
+
+        @Override
+        public void notifyAnimationRunningStateChanged(boolean running) {
+            mInsetsAnimationRunning = running;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3a711b2..27cc2d6 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -1182,22 +1182,6 @@
         }
     }
 
-    @Override
-    public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
-            @Nullable int[] fromOrientations, @Nullable int[] toOrientations) {
-        enforceTaskPermission("setOrientationRequestPolicy()");
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mService.mWindowManager
-                        .setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled,
-                                fromOrientations, toOrientations);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
         if (!shouldInterceptBackPressedOnRootTask(task)) {
             return false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index ae171a0..808a11d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -993,4 +993,18 @@
      * @param displayId the id of display to check if there is a software navigation bar.
      */
     public abstract boolean hasNavigationBar(int displayId);
+
+    /**
+     * Controls whether the app-requested screen orientation is always respected.
+     *
+     * @param respected If {@code true}, the app requested orientation is always respected.
+     *                  Otherwise, the system might ignore the request due to
+     *                  {@link com.android.server.wm.DisplayArea#getIgnoreOrientationRequest}.
+     * @param fromOrientations The orientations we want to map to the correspondent orientations
+     *                         in toOrientation.
+     * @param toOrientations The orientations we map to the ones in fromOrientations at the same
+     *                       index
+     */
+    public abstract void setOrientationRequestPolicy(boolean respected,
+            int[] fromOrientations, int[] toOrientations);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dd2b48b..0d2c94d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8523,6 +8523,15 @@
                 mImeTargetChangeListener = listener;
             }
         }
+
+        @Override
+        public void setOrientationRequestPolicy(boolean respected,
+                int[] fromOrientations, int[] toOrientations) {
+            synchronized (mGlobalLock) {
+                WindowManagerService.this.setOrientationRequestPolicy(respected,
+                        fromOrientations, toOrientations);
+            }
+        }
     }
 
     private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index afbe352..e5be4d9 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -21,6 +21,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -56,6 +58,7 @@
 
     private static final int TEST_USER_SERIAL = 1000;
     private static final int TEST_USER_ID = 10;
+    private static final UserInfo TEST_USER = new UserInfo();
 
     private TestUserDataPreparer mUserDataPreparer;
 
@@ -72,6 +75,8 @@
 
     @Before
     public void setup() {
+        TEST_USER.id = TEST_USER_ID;
+        TEST_USER.serialNumber = TEST_USER_SERIAL;
         Context ctx = InstrumentationRegistry.getContext();
         FileUtils.deleteContents(ctx.getCacheDir());
         mInstallLock = new Object();
@@ -92,8 +97,7 @@
         userDeDir.mkdirs();
         File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID);
         systemDeDir.mkdirs();
-        mUserDataPreparer
-                .prepareUserData(TEST_USER_ID, TEST_USER_SERIAL, StorageManager.FLAG_STORAGE_DE);
+        mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_DE);
         verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
                 eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
         verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
@@ -110,8 +114,7 @@
         userCeDir.mkdirs();
         File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
         systemCeDir.mkdirs();
-        mUserDataPreparer
-                .prepareUserData(TEST_USER_ID, TEST_USER_SERIAL, StorageManager.FLAG_STORAGE_CE);
+        mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
         verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
                 eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
         verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
@@ -123,6 +126,28 @@
     }
 
     @Test
+    public void testPrepareUserData_forNewUser_destroysOnFailure() throws Exception {
+        TEST_USER.lastLoggedInTime = 0;
+        doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
+                .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL),
+                        eq(StorageManager.FLAG_STORAGE_CE));
+        mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
+        verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
+                eq(StorageManager.FLAG_STORAGE_CE));
+    }
+
+    @Test
+    public void testPrepareUserData_forExistingUser_doesNotDestroyOnFailure() throws Exception {
+        TEST_USER.lastLoggedInTime = System.currentTimeMillis();
+        doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
+                .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL),
+                        eq(StorageManager.FLAG_STORAGE_CE));
+        mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
+        verify(mStorageManagerMock, never()).destroyUserStorage(isNull(String.class),
+                eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE));
+    }
+
+    @Test
     public void testDestroyUserData_De_DoesNotDestroyCe() throws Exception {
         // Add file in CE storage
         File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 9aa6136..6ba7368 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -262,8 +262,7 @@
                 Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data,
-                DisplayManagerFlags flags,
-                Context context) {
+                DisplayManagerFlags flags, Context context) {
             mCapturedChangeListener = clamperChangeListener;
             return mClampers;
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 21e3b34..b39cd04 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -3397,6 +3397,8 @@
                 bOptions.getTemporaryAppAllowlistType());
         assertEquals(PowerExemptionManager.REASON_TIMEZONE_CHANGED,
                 bOptions.getTemporaryAppAllowlistReasonCode());
+        assertEquals(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT,
+                bOptions.getDeliveryGroupPolicy());
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 3364545..918bc5d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1665,7 +1665,8 @@
         enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
                 List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_RED))));
         enqueueBroadcast(makeOrderedBroadcastRecord(timezoneSecond, callerApp,
-                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+                        makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
                 resultToSecond, null));
 
         waitForIdle();
@@ -1681,6 +1682,11 @@
                 anyInt(), anyInt(), any());
 
         // We deliver second broadcast to app
+        timezoneSecond.setClassName(PACKAGE_BLUE, CLASS_BLUE);
+        inOrder.verify(blueThread).scheduleReceiver(
+                argThat(filterAndExtrasEquals(timezoneSecond)), any(), any(),
+                anyInt(), any(), any(), eq(true), eq(false), anyInt(),
+                anyInt(), anyInt(), any());
         timezoneSecond.setClassName(PACKAGE_BLUE, CLASS_GREEN);
         inOrder.verify(blueThread).scheduleReceiver(
                 argThat(filterAndExtrasEquals(timezoneSecond)), any(), any(),
@@ -1797,9 +1803,15 @@
 
         waitForIdle();
 
-        verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
-        verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
-        verifyScheduleRegisteredReceiver(never(), receiverYellowApp, airplane);
+        if (mImpl == Impl.MODERN) {
+            verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
+            verifyScheduleRegisteredReceiver(times(2), receiverBlueApp, airplane);
+            verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
+        } else {
+            verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
+            verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
+            verifyScheduleRegisteredReceiver(never(), receiverYellowApp, airplane);
+        }
     }
 
     @Test
@@ -1830,6 +1842,39 @@
     }
 
     @Test
+    public void testReplacePending_existingDiffReceivers() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+        final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+        final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp);
+        final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp);
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
+                .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+                withPriority(receiverGreen, 5))));
+        enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, List.of(
+                withPriority(receiverGreen, 10),
+                withPriority(receiverBlue, 5))));
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+                withPriority(receiverBlue, 10),
+                withPriority(receiverGreen, 5))));
+
+        waitForIdle();
+
+        verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
+        verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
+        if (mImpl == Impl.MODERN) {
+            verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
+        } else {
+            verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
+        }
+        verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
+    }
+
+    @Test
     public void testIdleAndBarrier() throws Exception {
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
index 8e328ca..0e815d0 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -49,8 +49,11 @@
 import android.app.AlarmManager;
 import android.app.IActivityManager;
 import android.app.IForegroundServiceObserver;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -68,7 +71,7 @@
 import android.test.mock.MockContentResolver;
 import android.util.ArraySet;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -85,9 +88,11 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -145,7 +150,8 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mContextSpy = spy(new BroadcastInterceptingContext(InstrumentationRegistry.getContext()));
+        mContextSpy = spy(new BroadcastInterceptingContext(InstrumentationRegistry
+                .getInstrumentation().getTargetContext()));
         when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock);
         when(mContextSpy.getSystemService(AlarmManager.class)).thenReturn(mAlarmManagerMock);
         when(mContextSpy.getSystemService(UserManager.class)).thenReturn(mUserManagerMock);
@@ -395,26 +401,65 @@
         setLowPowerStandbySupportedConfig(true);
         mController.systemReady();
 
+        TestReceiver receiver = new TestReceiver();
+        mContextSpy.registerReceiver(receiver,
+                new IntentFilter(PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED));
+
         BroadcastInterceptingContext.FutureIntent futureIntent = mContextSpy.nextBroadcastIntent(
                 PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
         mController.setEnabled(false);
         futureIntent.assertNotReceived();
+        assertThat(receiver.receivedCount).isEqualTo(0);
+        receiver.reset();
 
         futureIntent = mContextSpy.nextBroadcastIntent(
                 PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
         mController.setEnabled(true);
         assertThat(futureIntent.get(1, TimeUnit.SECONDS)).isNotNull();
+        assertThat(receiver.receivedCount).isEqualTo(1);
+        receiver.reset();
 
         futureIntent = mContextSpy.nextBroadcastIntent(
                 PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
         mController.setEnabled(true);
         futureIntent.assertNotReceived();
+        assertThat(receiver.receivedCount).isEqualTo(0);
+        receiver.reset();
 
         futureIntent = mContextSpy.nextBroadcastIntent(
                 PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
-
         mController.setEnabled(false);
         assertThat(futureIntent.get(1, TimeUnit.SECONDS)).isNotNull();
+        assertThat(receiver.receivedCount).isEqualTo(1);
+        receiver.reset();
+    }
+
+    @Test
+    public void testLowPowerStandbyEnabled_EnabledChangedExplicitBroadcastSent() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        List<PackageInfo> packagesHoldingPermission = new ArrayList<>();
+
+        when(mPackageManagerMock.getPackagesHoldingPermissions(Mockito.any(),
+                Mockito.anyInt())).thenReturn(packagesHoldingPermission);
+
+        PackageInfo testInfo = new PackageInfo();
+        testInfo.packageName = mContextSpy.getPackageName();
+        packagesHoldingPermission.add(testInfo);
+        mController.systemReady();
+        TestReceiver receiver = new TestReceiver();
+        mContextSpy.registerReceiver(receiver,
+                new IntentFilter(PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED));
+
+        mController.setEnabled(false);
+        assertThat(receiver.receivedCount).isEqualTo(0);
+        receiver.reset();
+
+        mController.setEnabled(true);
+        // Since we added a package manually to the packages that are allowed to
+        // manage LPS, the interceptor should have intercepted two broadcasts, one
+        // implicit via registration and one explicit to the package added above.
+        assertThat(receiver.receivedCount).isEqualTo(2);
+        receiver.reset();
     }
 
     @Test
@@ -906,4 +951,19 @@
         LocalServices.removeServiceForTest(clazz);
         LocalServices.addService(clazz, mock);
     }
+
+    public static class TestReceiver extends BroadcastReceiver {
+        public int receivedCount = 0;
+
+        /**
+         * Resets the count of this receiver
+         */
+        public void reset() {
+            receivedCount = 0;
+        }
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            receivedCount++;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index c7d80ed..8933c6c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertTrue;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -31,6 +32,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.UserProperties;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -219,6 +221,8 @@
                 .isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
         assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
         assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible);
+        compareDrawables(mUserManager.getUserBadge(),
+                Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
 
         // Verify clone user parent
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -335,7 +339,8 @@
                 .isEqualTo(privateProfileUserProperties
                         .isAuthAlwaysRequiredToDisableQuietMode());
         assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
-
+        compareDrawables(mUserManager.getUserBadge(),
+                Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
         // Verify private profile parent
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
         UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
@@ -955,6 +960,8 @@
                 .isEqualTo(userTypeDetails.getBadgeNoBackground());
         assertThat(mUserManager.getUserStatusBarIconResId(userId))
                 .isEqualTo(userTypeDetails.getStatusBarIcon());
+        compareDrawables(mUserManager.getUserBadge(),
+                Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
 
         final int badgeIndex = userInfo.profileBadge;
         assertThat(mUserManager.getUserBadgeColor(userId)).isEqualTo(
@@ -1762,4 +1769,10 @@
                 .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin);
     }
 
+    private void compareDrawables(Drawable actual, Drawable expected){
+        assertEquals(actual.getIntrinsicWidth(), expected.getIntrinsicWidth());
+        assertEquals(actual.getIntrinsicHeight(), expected.getIntrinsicHeight());
+        assertEquals(actual.getLevel(), expected.getLevel());
+    }
+
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 360fdf3..6853c4c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -30,13 +30,13 @@
 import com.android.internal.annotations.Keep;
 import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 @Presubmit
 @MediumTest
 @RunWith(JUnitParamsRunner.class)
@@ -44,6 +44,7 @@
 
     private static final int VENDOR_ID = 0x123;
     private static final int PRODUCT_ID = 0x456;
+    private static final int DEVICE_BUS = 0x789;
     private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
     private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
     private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
@@ -71,8 +72,8 @@
                         KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, ALT_ON},
                 {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK}, KeyboardLogEvent.BACK,
                         KeyEvent.KEYCODE_BACK, 0},
-                {"Meta + `(grave) -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_GRAVE},
-                        KeyboardLogEvent.BACK, KeyEvent.KEYCODE_GRAVE, META_ON},
+                {"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
+                        KeyboardLogEvent.BACK, KeyEvent.KEYCODE_ESCAPE, META_ON},
                 {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
                         KeyboardLogEvent.BACK, KeyEvent.KEYCODE_DPAD_LEFT, META_ON},
                 {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
@@ -291,7 +292,7 @@
     @Before
     public void setUp() {
         setUpPhoneWindowManager(/*supportSettingsUpdate*/ true);
-        mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID);
+        mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID, DEVICE_BUS);
         mPhoneWindowManager.overrideLaunchHome();
         mPhoneWindowManager.overrideSearchKeyBehavior(
                 PhoneWindowManager.SEARCH_BEHAVIOR_TARGET_ACTIVITY);
@@ -311,7 +312,8 @@
             int expectedKey, int expectedModifierState) {
         sendKeyCombination(testKeys, 0 /* duration */);
         mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
-                expectedKey, expectedModifierState, "Failed while executing " + testName);
+                expectedKey, expectedModifierState, DEVICE_BUS,
+                "Failed while executing " + testName);
     }
 
     @Test
@@ -321,7 +323,8 @@
         mPhoneWindowManager.overrideLongPressOnHomeBehavior(longPressOnHomeBehavior);
         sendLongPressKeyCombination(testKeys);
         mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
-                expectedKey, expectedModifierState, "Failed while executing " + testName);
+                expectedKey, expectedModifierState, DEVICE_BUS,
+                "Failed while executing " + testName);
     }
 
     @Test
@@ -333,7 +336,8 @@
         sendKeyCombination(testKeys, 0 /* duration */);
         sendKeyCombination(testKeys, 0 /* duration */);
         mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
-                expectedKey, expectedModifierState, "Failed while executing " + testName);
+                expectedKey, expectedModifierState, DEVICE_BUS,
+                "Failed while executing " + testName);
     }
 
     @Test
@@ -344,6 +348,7 @@
         mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior);
         sendKeyCombination(testKeys, 0 /* duration */);
         mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
-                expectedKey, expectedModifierState, "Failed while executing " + testName);
+                expectedKey, expectedModifierState, DEVICE_BUS,
+                "Failed while executing " + testName);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 7788b33..43c4745 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -483,10 +483,15 @@
         doReturn(mPackageManager).when(mContext).getPackageManager();
     }
 
-    void overrideKeyEventSource(int vendorId, int productId) {
-        InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId(
-                productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType(
-                InputDevice.KEYBOARD_TYPE_ALPHABETIC).build();
+    void overrideKeyEventSource(int vendorId, int productId, int deviceBus) {
+        InputDevice device = new InputDevice.Builder()
+                .setId(1)
+                .setVendorId(vendorId)
+                .setProductId(productId)
+                .setDeviceBus(deviceBus)
+                .setSources(InputDevice.SOURCE_KEYBOARD)
+                .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+                .build();
         doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
         doReturn(device).when(mInputManager).getInputDevice(anyInt());
     }
@@ -682,11 +687,11 @@
     }
 
     void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
-            int expectedKey, int expectedModifierState, String errorMsg) {
+            int expectedKey, int expectedModifierState, int deviceBus, String errorMsg) {
         mTestLooper.dispatchAll();
         verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
                         vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey},
-                        expectedModifierState), description(errorMsg));
+                        expectedModifierState, deviceBus), description(errorMsg));
     }
 
     void assertSwitchToRecent(int persistentId) throws RemoteException {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 5a43498..0608cf4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -362,6 +362,8 @@
         // Ensure a task has moved over.
         ensureTaskPlacement(task, activity);
         assertTrue(task.inPinnedWindowingMode());
+        assertFalse("Entering PiP activity must not affect SysUiFlags",
+                activity.canAffectSystemUiFlags());
 
         // The activity with fixed orientation should not apply letterbox when entering PiP.
         final int requestedOrientation = task.getConfiguration().orientation
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index b098e82..dc8b5a1 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -179,6 +179,31 @@
     private final SparseArray<DetectorSession> mDetectorSessions =
             new SparseArray<>();
 
+    /** Listens to changes to voice activation op. */
+    private final AppOpsManager.OnOpChangedListener mOnOpChangedListener =
+            new AppOpsManager.OnOpChangedListener() {
+                @Override
+                public void onOpChanged(String op, String packageName) {
+                    if (op.equals(AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO)) {
+                        AppOpsManager appOpsManager =
+                                mContext.getSystemService(AppOpsManager.class);
+                        synchronized (mLock) {
+                            int checkOp = appOpsManager.unsafeCheckOpNoThrow(
+                                    AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+                                    mVoiceInteractorIdentity.uid,
+                                    mVoiceInteractorIdentity.packageName);
+                            // Voice activation app op disabled, safely shutdown hotword detection.
+                            if (checkOp == AppOpsManager.MODE_ERRORED) {
+                                Slog.i(TAG, "Shutdown hotword detection service on voice "
+                                        + "activation op disabled.");
+                                safelyShutdownHotwordDetectionOnVoiceActivationDisabledLocked();
+                            }
+                        }
+                    }
+                }
+            };
+
+
     HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
             Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName,
             ComponentName visualQueryDetectionServiceName, int userId,
@@ -216,6 +241,10 @@
 
         mLastRestartInstant = Instant.now();
 
+        AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+        appOpsManager.startWatchingMode(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+                mVoiceInteractorIdentity.packageName, mOnOpChangedListener);
+
         if (mReStartPeriodSeconds <= 0) {
             mCancellationTaskFuture = null;
         } else {
@@ -299,7 +328,11 @@
         }
         if (mAudioFlinger != null) {
             mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
+            mAudioFlinger = null;
         }
+        // Unregister the on op mode changed listener.
+        AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+        appOpsManager.stopWatchingMode(mOnOpChangedListener);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -553,6 +586,51 @@
         }
     }
 
+    /**
+     * Shutdowns down hotword detection service, swallowing exceptions.
+     *
+     * Called when voice activation app-op has been disabled.
+     */
+    @SuppressWarnings("GuardedBy")
+    void safelyShutdownHotwordDetectionOnVoiceActivationDisabledLocked() {
+        Slog.v(TAG, "safelyShutdownHotwordDetectionOnVoiceActivationDisabled");
+        try {
+            clearDebugHotwordLoggingTimeoutLocked();
+            mRemoteExceptionListener = null;
+            runForEachDetectorSessionLocked((session) -> {
+                if (!(session instanceof VisualQueryDetectorSession)) {
+                    // Inform all detector sessions that they got destroyed due to voice activation
+                    // op being disabled.
+                    session.reportErrorLocked(
+                            new HotwordDetectionServiceFailure(
+                                    HotwordDetectionServiceFailure
+                                            .ERROR_CODE_SHUTDOWN_HDS_ON_VOICE_ACTIVATION_OP_DISABLED,
+                                    "Shutdown hotword detection service on voice "
+                                            + "activation op disabled!"));
+                    session.destroyLocked();
+                }
+            });
+
+            // Remove hotword detection sessions.
+            mDetectorSessions.delete(HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
+            mDetectorSessions.delete(HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
+
+            mDebugHotwordLogging = false;
+            unbindHotwordDetectionService();
+            if (mCancellationTaskFuture != null) {
+                mCancellationTaskFuture.cancel(/* mayInterruptIfRunning= */ true);
+            }
+            if (mAudioFlinger != null) {
+                mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
+                mAudioFlinger = null;
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Swallowing error while shutting down hotword detection."
+                    + "Error message: " + e.getMessage());
+        }
+    }
+
+
     static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
         private final HotwordDetectionConnection mHotwordDetectionConnection;
         private final IHotwordRecognitionStatusCallback mExternalCallback;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ede4885..1e68687 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -252,6 +252,7 @@
      *
      * The default value is true.
      */
+    @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
     public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL =
             "additional_settings_caller_id_visibility_bool";
 
@@ -261,6 +262,7 @@
      *
      * The default value is true.
      */
+    @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
     public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
             "additional_settings_call_waiting_visibility_bool";
 
@@ -3349,12 +3351,42 @@
     /**
      * Determines whether we should show a notification when the phone established a data
      * connection in roaming network, to warn users about possible roaming charges.
+     *
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY
      * @hide
      */
     public static final String KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL =
             "show_data_connected_roaming_notification";
 
     /**
+     * Determines what MCCs are exceptions for the value of
+     * {@link #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL}.
+     * An empty list indicates that there are no exceptions.
+     *
+     * @see #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY
+     * @hide
+     */
+    public static final String
+            KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY =
+            "data_connected_roaming_notification_excluded_mccs_string_array";
+
+    /**
+     * Determines what MCC+MNCs are exceptions for the MCCs specified in
+     * {@link #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY}, meaning the
+     * value for the MCC+MNC is {@link #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL}.
+     * An empty list indicates that there are no MNC-specific exceptions.
+     *
+     * @see #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY
+     * @hide
+     */
+    public static final String
+            KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY =
+            "data_connected_roaming_notification_included_mcc_mncs_string_array";
+
+    /**
      * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
      * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
      *
@@ -10336,6 +10368,11 @@
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
+        sDefaults.putStringArray(KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY,
+                new String[0]);
+        sDefaults.putStringArray(
+                KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY,
+                new String[0]);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 // Boundaries: [-140 dBm, -44 dBm]
                 new int[] {
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index b528866..54ceaed 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,6 +35,8 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
+import com.android.internal.telephony.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
@@ -77,9 +80,11 @@
     /** @hide */
     @IntDef(prefix = {"SUGGESTED_ACTION_"},
             value = {
-            SUGGESTED_ACTION_NONE,
-            SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK,
-            SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT
+                SUGGESTED_ACTION_NONE,
+                SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK,
+                SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
+                SUGGESTED_ACTION_TRIGGER_RAT_BLOCK,
+                SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SuggestedAction {}
@@ -109,6 +114,27 @@
     @SystemApi
     public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2;
 
+    /**
+     * Indicates that the IMS registration on current RAT failed multiple times.
+     * The radio shall block the current RAT and search for other available RATs in the
+     * background. If no other RAT is available that meets the carrier requirements, the
+     * radio may remain on the current RAT for internet service. The radio clears all
+     * RATs marked as unavailable if the IMS service is registered to the carrier network.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION)
+    int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3;
+
+    /**
+     * Indicates that the radio clears all RATs marked as unavailable and tries to find
+     * an available RAT that meets the carrier requirements.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION)
+    int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4;
+
     /**@hide*/
     // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
     // and WWAN are more accurate constants.
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 1e5f33f..60b5ce7 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -235,13 +235,17 @@
         }
 
         public int setFrameRate(float frameRate) {
+            return setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+        }
+
+        public int setFrameRate(
+                float frameRate, @Surface.FrameRateCompatibility int compatibility) {
             Log.i(TAG,
                     String.format("Setting frame rate for %s: frameRate=%.2f", mName, frameRate));
 
             int rc = 0;
             try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
-                transaction.setFrameRate(
-                        mSurfaceControl, frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+                transaction.setFrameRate(mSurfaceControl, frameRate, compatibility);
                 transaction.apply();
             }
             return rc;
@@ -668,12 +672,34 @@
         }
     }
 
-    private void testSurfaceControlFrameRateCategoryInternal(int category)
-            throws InterruptedException {
+    private void testSurfaceControlFrameRateCompatibilityInternal(
+            @Surface.FrameRateCompatibility int compatibility) throws InterruptedException {
+        runOneSurfaceTest((TestSurface surface) -> {
+            Log.i(TAG,
+                    "**** Running testSurfaceControlFrameRateCompatibility with compatibility "
+                            + compatibility);
+
+            float expectedFrameRate = getExpectedFrameRateForCompatibility(compatibility);
+            int initialNumEvents = mModeChangedEvents.size();
+            surface.setFrameRate(30.f, compatibility);
+            verifyExactAndStableFrameRate(expectedFrameRate, surface);
+            verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
+        });
+    }
+
+    public void testSurfaceControlFrameRateCompatibility(
+            @Surface.FrameRateCompatibility int compatibility) throws InterruptedException {
+        runTestsWithPreconditions(
+                () -> testSurfaceControlFrameRateCompatibilityInternal(compatibility),
+                "frame rate compatibility=" + compatibility);
+    }
+
+    private void testSurfaceControlFrameRateCategoryInternal(
+            @Surface.FrameRateCategory int category) throws InterruptedException {
         runOneSurfaceTest((TestSurface surface) -> {
             Log.i(TAG, "**** Running testSurfaceControlFrameRateCategory for category " + category);
 
-            float expectedFrameRate = getExpectedFrameRate(category);
+            float expectedFrameRate = getExpectedFrameRateForCategory(category);
             int initialNumEvents = mModeChangedEvents.size();
             surface.setFrameRateCategory(category);
             verifyCompatibleAndStableFrameRate(expectedFrameRate, surface);
@@ -681,7 +707,8 @@
         });
     }
 
-    public void testSurfaceControlFrameRateCategory(int category) throws InterruptedException {
+    public void testSurfaceControlFrameRateCategory(@Surface.FrameRateCategory int category)
+            throws InterruptedException {
         runTestsWithPreconditions(()
                 -> testSurfaceControlFrameRateCategoryInternal(category),
                 "frame rate category=" + category);
@@ -744,7 +771,24 @@
                 "frame rate strategy=" + parentStrategy);
     }
 
-    private float getExpectedFrameRate(int category) {
+    private float getExpectedFrameRateForCompatibility(int compatibility) {
+        assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility "
+                        + compatibility,
+                compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE);
+
+        Display display = getDisplay();
+        Optional<Float> expectedFrameRate = getRefreshRates(display.getMode(), display)
+                                                    .stream()
+                                                    .filter(rate -> rate >= 30.f)
+                                                    .min(Comparator.naturalOrder());
+
+        assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED because no refresh rate "
+                        + "is >= 30",
+                expectedFrameRate.isPresent());
+        return expectedFrameRate.get();
+    }
+
+    private float getExpectedFrameRateForCategory(int category) {
         Display display = getDisplay();
         List<Float> frameRates = getRefreshRates(display.getMode(), display);
 
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index 29f6879..4b56c10 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -81,6 +81,12 @@
     }
 
     @Test
+    public void testSurfaceControlFrameRateCompatibilityGte() throws InterruptedException {
+        GraphicsActivity activity = mActivityRule.getActivity();
+        activity.testSurfaceControlFrameRateCompatibility(Surface.FRAME_RATE_COMPATIBILITY_GTE);
+    }
+
+    @Test
     public void testSurfaceControlFrameRateCategoryHigh() throws InterruptedException {
         GraphicsActivity activity = mActivityRule.getActivity();
         activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_HIGH);
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 44de6a6..9c33576 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -63,6 +63,7 @@
     deviceId: Int,
     vendorId: Int,
     productId: Int,
+    deviceBus: Int,
     languageTag: String,
     layoutType: String
 ): InputDevice =
@@ -75,6 +76,7 @@
         .setExternal(true)
         .setVendorId(vendorId)
         .setProductId(productId)
+        .setDeviceBus(deviceBus)
         .setKeyboardLanguageTag(languageTag)
         .setKeyboardLayoutType(layoutType)
         .build()
@@ -94,6 +96,7 @@
         const val ENGLISH_QWERTY_DEVICE_ID = 4
         const val DEFAULT_VENDOR_ID = 123
         const val DEFAULT_PRODUCT_ID = 456
+        const val DEFAULT_DEVICE_BUS = 789
         const val USER_ID = 4
         const val IME_ID = "ime_id"
         const val PACKAGE_NAME = "KeyboardLayoutManagerTests"
@@ -177,12 +180,14 @@
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
             .thenReturn(inputManager)
 
-        keyboardDevice = createKeyboard(DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, "", "")
-        vendorSpecificKeyboardDevice = createKeyboard(VENDOR_SPECIFIC_DEVICE_ID, 1, 1, "", "")
+        keyboardDevice = createKeyboard(DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID,
+                DEFAULT_DEVICE_BUS, "", "")
+        vendorSpecificKeyboardDevice = createKeyboard(VENDOR_SPECIFIC_DEVICE_ID, 1, 1,
+                1, "", "")
         englishDvorakKeyboardDevice = createKeyboard(ENGLISH_DVORAK_DEVICE_ID, DEFAULT_VENDOR_ID,
-                DEFAULT_PRODUCT_ID, "en", "dvorak")
+                DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak")
         englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID,
-                DEFAULT_PRODUCT_ID, "en", "qwerty")
+                DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty")
         Mockito.`when`(iInputManager.inputDeviceIds)
             .thenReturn(intArrayOf(
                 DEVICE_ID,
@@ -861,7 +866,9 @@
                                 GERMAN_LAYOUT_NAME,
                                 KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
                                 "de-Latn",
-                                LAYOUT_TYPE_QWERTZ))
+                                LAYOUT_TYPE_QWERTZ),
+                            ),
+                        ArgumentMatchers.eq(keyboardDevice.deviceBus),
                 )
             }
         }
@@ -887,7 +894,8 @@
                                 ENGLISH_US_LAYOUT_NAME,
                                 KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
                                 "de-Latn",
-                                LAYOUT_TYPE_QWERTZ))
+                                LAYOUT_TYPE_QWERTZ)),
+                        ArgumentMatchers.eq(keyboardDevice.deviceBus),
                 )
             }
         }
@@ -911,7 +919,9 @@
                                 "Default",
                                 KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
                                 KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
-                                LAYOUT_TYPE_DEFAULT))
+                                LAYOUT_TYPE_DEFAULT),
+                            ),
+                        ArgumentMatchers.eq(keyboardDevice.deviceBus),
                 )
             }
         }
@@ -929,7 +939,8 @@
                         ArgumentMatchers.anyBoolean(),
                         ArgumentMatchers.anyInt(),
                         ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any(ByteArray::class.java)
+                        ArgumentMatchers.any(ByteArray::class.java),
+                        ArgumentMatchers.anyInt(),
                 )
             }, Mockito.times(0))
         }
@@ -972,8 +983,13 @@
     }
 
     private fun createByteArray(
-            expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String,
-            expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray {
+            expectedLanguageTag: String,
+            expectedLayoutType: Int,
+            expectedLayoutName: String,
+            expectedCriteria: Int,
+            expectedImeLanguageTag: String,
+            expectedImeLayoutType: Int
+    ): ByteArray {
         val proto = ProtoOutputStream()
         val keyboardLayoutConfigToken = proto.start(
                 KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
@@ -1001,7 +1017,7 @@
                 KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
                 expectedImeLayoutType
         )
-        proto.end(keyboardLayoutConfigToken);
+        proto.end(keyboardLayoutConfigToken)
         return proto.bytes
     }
 
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
index 33ff09b..89a47b9 100644
--- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -30,6 +30,7 @@
     deviceId: Int,
     vendorId: Int,
     productId: Int,
+    deviceBus: Int,
     languageTag: String?,
     layoutType: String?
 ): InputDevice =
@@ -42,6 +43,7 @@
         .setExternal(true)
         .setVendorId(vendorId)
         .setProductId(productId)
+        .setDeviceBus(deviceBus)
         .setKeyboardLanguageTag(languageTag)
         .setKeyboardLayoutType(layoutType)
         .build()
@@ -67,6 +69,7 @@
         const val DEVICE_ID = 1
         const val DEFAULT_VENDOR_ID = 123
         const val DEFAULT_PRODUCT_ID = 456
+        const val DEFAULT_DEVICE_BUS = 789
     }
 
     @Test
@@ -77,6 +80,7 @@
                     DEVICE_ID,
                     DEFAULT_VENDOR_ID,
                     DEFAULT_PRODUCT_ID,
+                    DEFAULT_DEVICE_BUS,
                     null,
                     null
                 )
@@ -92,6 +96,7 @@
                     DEVICE_ID,
                     DEFAULT_VENDOR_ID,
                     DEFAULT_PRODUCT_ID,
+                    DEFAULT_DEVICE_BUS,
                     null,
                     null
                 )
@@ -107,6 +112,7 @@
                 DEVICE_ID,
                 DEFAULT_VENDOR_ID,
                 DEFAULT_PRODUCT_ID,
+                DEFAULT_DEVICE_BUS,
                 "de-CH",
                 "qwertz"
             )
@@ -135,6 +141,11 @@
             DEFAULT_PRODUCT_ID,
             event.productId
         )
+        assertEquals(
+             "KeyboardConfigurationEvent should pick device bus from provided InputDevice",
+             DEFAULT_DEVICE_BUS,
+             event.deviceBus
+        )
         assertTrue(event.isFirstConfiguration)
 
         assertEquals(
@@ -178,6 +189,7 @@
                 DEVICE_ID,
                 DEFAULT_VENDOR_ID,
                 DEFAULT_PRODUCT_ID,
+                DEFAULT_DEVICE_BUS,
                 "und", // Undefined language tag
                 "azerty"
             )
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 1dfd5c0..d0e5626 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -93,7 +93,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS)
+    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     fun grantCannotActivelyUnlockDevice() {
         // On automotive, trust agents can actively unlock the device.
         assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
@@ -120,7 +120,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS)
+    @RequiresFlagsDisabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     fun grantCouldCauseWrongDeviceLockedStateDueToBug() {
         // On automotive, trust agents can actively unlock the device.
         assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 5a8f828..0121809 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -36,7 +36,8 @@
 class LockStateTrackingRule : TestRule {
     private val context: Context = getApplicationContext()
     private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
-    private val keyguardManager = context.getSystemService(KeyguardManager::class.java) as KeyguardManager
+    private val keyguardManager =
+            context.getSystemService(KeyguardManager::class.java) as KeyguardManager
 
     @Volatile lateinit var trustState: TrustState
         private set
@@ -63,7 +64,7 @@
         wait("not trusted") { trustState.trusted == false }
     }
 
-    // TODO(b/299298338) remove this when removing FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS
+    // TODO(b/299298338) remove this when removing FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2
     fun assertUnlockedButNotReally() {
         wait("device unlocked") { !keyguardManager.isDeviceLocked }
         wait("not trusted") { trustState.trusted == false }
@@ -87,7 +88,7 @@
             trustGrantedMessages: MutableList<String>
         ) {
             Log.d(TAG, "Device became trusted=$enabled")
-            trustState = trustState.copy(trusted=enabled)
+            trustState = trustState.copy(trusted = enabled)
         }
     }
 
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 91e6814..d97dd7c 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -235,10 +235,6 @@
     "Duplicate or conflicting argument found: --in-jar" \
     ""
 
-EXTRA_ARGS="--quiet" run_hoststubgen_for_failure "Conflicting arg" \
-    "Duplicate or conflicting argument found: --quiet" \
-    ""
-
 
 echo "All tests passed"
 exit 0
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index dbcf3a5..4e0cd09 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -239,7 +239,7 @@
             errors: HostStubGenErrors,
             ) {
         log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
-        log.i("Checker is %s", if (enableChecker) "enabled" else "disabled")
+        log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
 
         val start = System.currentTimeMillis()
 
@@ -264,7 +264,7 @@
             }
         }
         val end = System.currentTimeMillis()
-        log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0)
+        log.i("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0)
     }
 
     private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
index 5e71a36..18065ba 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
@@ -15,10 +15,12 @@
  */
 package com.android.hoststubgen
 
-import java.io.OutputStream
-import java.io.PrintStream
+import java.io.BufferedOutputStream
+import java.io.FileOutputStream
+import java.io.PrintWriter
+import java.io.Writer
 
-val log: HostStubGenLogger = HostStubGenLogger()
+val log: HostStubGenLogger = HostStubGenLogger().setConsoleLogLevel(LogLevel.Info)
 
 /** Logging level */
 enum class LogLevel {
@@ -30,15 +32,13 @@
     Debug,
 }
 
-/** Simple logging class. */
-class HostStubGenLogger(
-        private var out: PrintStream = System.out!!,
-        var level: LogLevel = LogLevel.Info,
-) {
-    companion object {
-        private val sNullPrintStream: PrintStream = PrintStream(OutputStream.nullOutputStream())
-    }
-
+/**
+ * Simple logging class.
+ *
+ * By default, it has no printers set. Use [setConsoleLogLevel] or [addFilePrinter] to actually
+ * write log.
+ */
+class HostStubGenLogger {
     private var indentLevel: Int = 0
         get() = field
         set(value) {
@@ -47,6 +47,56 @@
         }
     private var indent: String = ""
 
+    private val printers: MutableList<LogPrinter> = mutableListOf()
+
+    private var consolePrinter: LogPrinter? = null
+
+    private var maxLogLevel = LogLevel.None
+
+    private fun updateMaxLogLevel() {
+        maxLogLevel = LogLevel.None
+
+        printers.forEach {
+            if (maxLogLevel < it.logLevel) {
+                maxLogLevel = it.logLevel
+            }
+        }
+    }
+
+    private fun addPrinter(printer: LogPrinter) {
+        printers.add(printer)
+        updateMaxLogLevel()
+    }
+
+    private fun removePrinter(printer: LogPrinter) {
+        printers.remove(printer)
+        updateMaxLogLevel()
+    }
+
+    fun setConsoleLogLevel(level: LogLevel): HostStubGenLogger {
+        // If there's already a console log printer set, remove it, and then add a new one
+        consolePrinter?.let {
+            removePrinter(it)
+        }
+        val cp = StreamPrinter(level, PrintWriter(System.out))
+        addPrinter(cp)
+        consolePrinter = cp
+
+        return this
+    }
+
+    fun addFilePrinter(level: LogLevel, logFilename: String): HostStubGenLogger {
+        addPrinter(StreamPrinter(level, PrintWriter(BufferedOutputStream(
+            FileOutputStream(logFilename)))))
+
+        return this
+    }
+
+    /** Flush all the printers */
+    fun flush() {
+        printers.forEach { it.flush() }
+    }
+
     fun indent() {
         indentLevel++
     }
@@ -68,92 +118,71 @@
     }
 
     fun isEnabled(level: LogLevel): Boolean {
-        return level.ordinal <= this.level.ordinal
+        return level.ordinal <= maxLogLevel.ordinal
     }
 
-    private fun println(message: String) {
-        out.print(indent)
-        out.println(message)
+    private fun println(level: LogLevel, message: String) {
+        printers.forEach {
+            if (it.logLevel.ordinal >= level.ordinal) {
+                it.println(level, indent, message)
+            }
+        }
+    }
+
+    private fun println(level: LogLevel, format: String, vararg args: Any?) {
+        if (isEnabled(level)) {
+            println(level, String.format(format, *args))
+        }
     }
 
     /** Log an error. */
     fun e(message: String) {
-        if (level.ordinal < LogLevel.Error.ordinal) {
-            return
-        }
-        println(message)
+        println(LogLevel.Error, message)
     }
 
     /** Log an error. */
     fun e(format: String, vararg args: Any?) {
-        if (level.ordinal < LogLevel.Error.ordinal) {
-            return
-        }
-        e(String.format(format, *args))
+        println(LogLevel.Error, format, *args)
     }
 
     /** Log a warning. */
     fun w(message: String) {
-        if (level.ordinal < LogLevel.Warn.ordinal) {
-            return
-        }
-        println(message)
+        println(LogLevel.Warn, message)
     }
 
     /** Log a warning. */
     fun w(format: String, vararg args: Any?) {
-        if (level.ordinal < LogLevel.Warn.ordinal) {
-            return
-        }
-        w(String.format(format, *args))
+        println(LogLevel.Warn, format, *args)
     }
 
     /** Log an info message. */
     fun i(message: String) {
-        if (level.ordinal < LogLevel.Info.ordinal) {
-            return
-        }
-        println(message)
+        println(LogLevel.Info, message)
     }
 
-    /** Log a debug message. */
+    /** Log an info message. */
     fun i(format: String, vararg args: Any?) {
-        if (level.ordinal < LogLevel.Warn.ordinal) {
-            return
-        }
-        i(String.format(format, *args))
+        println(LogLevel.Info, format, *args)
     }
 
     /** Log a verbose message. */
     fun v(message: String) {
-        if (level.ordinal < LogLevel.Verbose.ordinal) {
-            return
-        }
-        println(message)
+        println(LogLevel.Verbose, message)
     }
 
     /** Log a verbose message. */
     fun v(format: String, vararg args: Any?) {
-        if (level.ordinal < LogLevel.Verbose.ordinal) {
-            return
-        }
-        v(String.format(format, *args))
+        println(LogLevel.Verbose, format, *args)
     }
 
     /** Log a debug message. */
     fun d(message: String) {
-        if (level.ordinal < LogLevel.Debug.ordinal) {
-            return
-        }
-        println(message)
+        println(LogLevel.Debug, message)
     }
 
     /** Log a debug message. */
     fun d(format: String, vararg args: Any?) {
-        if (level.ordinal < LogLevel.Warn.ordinal) {
-            return
-        }
-        d(String.format(format, *args))
+        println(LogLevel.Debug, format, *args)
     }
 
     inline fun forVerbose(block: () -> Unit) {
@@ -168,31 +197,65 @@
         }
     }
 
-    /** Return a stream for error. */
-    fun getErrorPrintStream(): PrintStream {
-        if (level.ordinal < LogLevel.Error.ordinal) {
-            return sNullPrintStream
-        }
-
-        // TODO Apply indent
-        return PrintStream(out)
+    /** Return a Writer for a given log level. */
+    fun getWriter(level: LogLevel): Writer {
+        return MultiplexingWriter(level)
     }
 
-    /** Return a stream for verbose messages. */
-    fun getVerbosePrintStream(): PrintStream {
-        if (level.ordinal < LogLevel.Verbose.ordinal) {
-            return sNullPrintStream
+    private inner class MultiplexingWriter(val level: LogLevel) : Writer() {
+        private inline fun forPrinters(callback: (LogPrinter) -> Unit) {
+            printers.forEach {
+                if (it.logLevel.ordinal >= level.ordinal) {
+                    callback(it)
+                }
+            }
         }
-        // TODO Apply indent
-        return PrintStream(out)
+
+        override fun close() {
+            flush()
+        }
+
+        override fun flush() {
+            forPrinters {
+                it.flush()
+            }
+        }
+
+        override fun write(cbuf: CharArray, off: Int, len: Int) {
+            // TODO Apply indent
+            forPrinters {
+                it.write(cbuf, off, len)
+            }
+        }
+    }
+}
+
+private interface LogPrinter {
+    val logLevel: LogLevel
+
+    fun println(logLevel: LogLevel, indent: String, message: String)
+
+    // TODO: This should be removed once MultiplexingWriter starts applying indent, at which point
+    // println() should be used instead.
+    fun write(cbuf: CharArray, off: Int, len: Int)
+
+    fun flush()
+}
+
+private class StreamPrinter(
+    override val logLevel: LogLevel,
+    val out: PrintWriter,
+) : LogPrinter {
+    override fun println(logLevel: LogLevel, indent: String, message: String) {
+        out.print(indent)
+        out.println(message)
     }
 
-    /** Return a stream for debug messages. */
-    fun getInfoPrintStream(): PrintStream {
-        if (level.ordinal < LogLevel.Info.ordinal) {
-            return sNullPrintStream
-        }
-        // TODO Apply indent
-        return PrintStream(out)
+    override fun write(cbuf: CharArray, off: Int, len: Int) {
+        out.write(cbuf, off, len)
     }
-}
\ No newline at end of file
+
+    override fun flush() {
+        out.flush()
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 0ae52af..d2ead18 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -101,9 +101,7 @@
 
         var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
 
-        var logLevel: SetOnce<LogLevel> = SetOnce(LogLevel.Info),
-
-        var cleanUpOnError: SetOnce<Boolean> = SetOnce(true),
+        var cleanUpOnError: SetOnce<Boolean> = SetOnce(false),
 
         var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
         var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
@@ -143,6 +141,11 @@
                 return name
             }
 
+            fun setLogFile(level: LogLevel, filename: String) {
+                log.addFilePrinter(level, filename)
+                log.i("$level log file: $filename")
+            }
+
             while (true) {
                 val arg = ai.nextArgOptional()
                 if (arg == null) {
@@ -161,9 +164,9 @@
                         // TODO: Write help
                         "-h", "--help" -> TODO("Help is not implemented yet")
 
-                        "-v", "--verbose" -> ret.logLevel.set(LogLevel.Verbose)
-                        "-d", "--debug" -> ret.logLevel.set(LogLevel.Debug)
-                        "-q", "--quiet" -> ret.logLevel.set(LogLevel.None)
+                        "-v", "--verbose" -> log.setConsoleLogLevel(LogLevel.Verbose)
+                        "-d", "--debug" -> log.setConsoleLogLevel(LogLevel.Debug)
+                        "-q", "--quiet" -> log.setConsoleLogLevel(LogLevel.None)
 
                         "--in-jar" -> ret.inJar.setNextStringArg().ensureFileExists()
                         "--out-stub-jar" -> ret.outStubJar.setNextStringArg()
@@ -211,7 +214,7 @@
                             ret.keepStaticInitializerAnnotations.addUniqueAnnotationArg()
 
                         "--package-redirect" ->
-                            ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg))
+                            ret.packageRedirects += parsePackageRedirect(nextArg())
 
                         "--annotation-allowed-classes-file" ->
                             ret.annotationAllowedClassesFile.setNextStringArg()
@@ -246,13 +249,15 @@
 
                         "--gen-input-dump-file" -> ret.inputJarDumpFile.setNextStringArg()
 
+                        "--verbose-log" -> setLogFile(LogLevel.Verbose, nextArg())
+                        "--debug-log" -> setLogFile(LogLevel.Debug, nextArg())
+
                         else -> throw ArgumentsException("Unknown option: $arg")
                     }
                 } catch (e: SetOnce.SetMoreThanOnceException) {
                     throw ArgumentsException("Duplicate or conflicting argument found: $arg")
                 }
             }
-            log.w(ret.toString())
 
             if (!ret.inJar.isSet) {
                 throw ArgumentsException("Required option missing: --in-jar")
@@ -377,7 +382,6 @@
               intersectStubJars=$intersectStubJars,
               policyOverrideFile=$policyOverrideFile,
               defaultPolicy=$defaultPolicy,
-              logLevel=$logLevel,
               cleanUpOnError=$cleanUpOnError,
               enableClassChecker=$enableClassChecker,
               enablePreTrace=$enablePreTrace,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
index 38ba0cc..4882c00 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
@@ -17,6 +17,8 @@
 
 package com.android.hoststubgen
 
+import java.io.PrintWriter
+
 const val COMMAND_NAME = "HostStubGen"
 
 /**
@@ -25,13 +27,12 @@
 fun main(args: Array<String>) {
     var success = false
     var clanupOnError = false
+
     try {
         // Parse the command line arguments.
         val options = HostStubGenOptions.parseArgs(args)
         clanupOnError = options.cleanUpOnError.get
 
-        log.level = options.logLevel.get
-
         log.v("HostStubGen started")
         log.v("Options: $options")
 
@@ -39,17 +40,18 @@
         HostStubGen(options).run()
 
         success = true
-    } catch (e: Exception) {
+    } catch (e: Throwable) {
         log.e("$COMMAND_NAME: Error: ${e.message}")
         if (e !is UserErrorException) {
-            e.printStackTrace(log.getErrorPrintStream())
+            e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
         }
         if (clanupOnError) {
-            TODO("clanupOnError is not implemented yet")
+            TODO("Remove output jars here")
         }
+    } finally {
+        log.i("$COMMAND_NAME finished")
+        log.flush()
     }
 
-    log.v("HostStubGen finished")
-
     System.exit(if (success) 0 else 1 )
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index f25e862..96e4a3f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -16,6 +16,7 @@
 package com.android.hoststubgen.visitors
 
 import com.android.hoststubgen.HostStubGenErrors
+import com.android.hoststubgen.LogLevel
 import com.android.hoststubgen.asm.ClassNodes
 import com.android.hoststubgen.asm.getPackageNameFromClassName
 import com.android.hoststubgen.asm.resolveClassName
@@ -229,7 +230,7 @@
         ): ClassVisitor {
             var next = nextVisitor
 
-            val verbosePrinter = PrintWriter(log.getVerbosePrintStream())
+            val verbosePrinter = PrintWriter(log.getWriter(LogLevel.Verbose))
 
             // Inject TraceClassVisitor for debugging.
             if (options.enablePostTrace) {