Merge "Shortcut Helper - Define mapping of key codes to strings / icons" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4536f6f..cd2c9ca 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3524,6 +3524,7 @@
     field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
     field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
     field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
+    field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6; // 0x6
     field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5
     field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
     field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index bd80dc1..69b5222 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -499,32 +499,31 @@
 
     /**
      * Activity Action: Launch an Automatic Zen Rule configuration screen
-     * <p>
-     * Input: Optionally, {@link #EXTRA_AUTOMATIC_RULE_ID}, if the configuration screen for an
+     *
+     * <p> Input: Optionally, {@link #EXTRA_AUTOMATIC_RULE_ID}, if the configuration screen for an
      * existing rule should be displayed. If the rule id is missing or null, apps should display
      * a configuration screen where users can create a new instance of the rule.
-     * <p>
-     * Output: Nothing
-     * <p>
-     *     You can have multiple activities handling this intent, if you support multiple
-     *     {@link AutomaticZenRule rules}. In order for the system to properly display all of your
-     *     rule types so that users can create new instances or configure existing ones, you need
-     *     to add some extra metadata ({@link #META_DATA_AUTOMATIC_RULE_TYPE})
-     *     to your activity tag in your manifest. If you'd like to limit the number of rules a user
-     *     can create from this flow, you can additionally optionally include
-     *     {@link #META_DATA_RULE_INSTANCE_LIMIT}.
      *
-     *     For example,
-     *     &lt;meta-data
-     *         android:name="android.app.zen.automatic.ruleType"
-     *         android:value="@string/my_condition_rule">
-     *     &lt;/meta-data>
-     *     &lt;meta-data
-     *         android:name="android.app.zen.automatic.ruleInstanceLimit"
-     *         android:value="1">
-     *     &lt;/meta-data>
-     * </p>
-     * </p>
+     * <p> Output: Nothing
+     *
+     * <p> You can have multiple activities handling this intent, if you support multiple
+     * {@link AutomaticZenRule rules}. In order for the system to properly display all of your
+     * rule types so that users can create new instances or configure existing ones, you need
+     * to add some extra metadata ({@link #META_DATA_AUTOMATIC_RULE_TYPE})
+     * to your activity tag in your manifest. If you'd like to limit the number of rules a user
+     * can create from this flow, you can additionally optionally include
+     * {@link #META_DATA_RULE_INSTANCE_LIMIT}. For example,
+     *
+     * <pre>
+     *     {@code
+     *     <meta-data
+     *         android:name="android.service.zen.automatic.ruleType"
+     *         android:value="@string/my_condition_rule" />
+     *     <meta-data
+     *         android:name="android.service.zen.automatic.ruleInstanceLimit"
+     *         android:value="1" />
+     *     }
+     * </pre>
      *
      * @see #addAutomaticZenRule(AutomaticZenRule)
      */
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index f9a9da1..f7f842f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -171,7 +171,7 @@
      * @hide
      */
     @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY,
-            POLICY_TYPE_CLIPBOARD})
+            POLICY_TYPE_CLIPBOARD, POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR})
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
     public @interface DynamicPolicyType {}
@@ -263,6 +263,22 @@
     @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
     public static final int POLICY_TYPE_CAMERA = 5;
 
+    /**
+     * Tells the virtual device framework how to handle activity launches that were blocked due to
+     * the current activity policy.
+     *
+     * <ul>
+     *     <li>{@link #DEVICE_POLICY_DEFAULT}: Show UI informing the user of the blocked activity
+     *     launch on the virtual display that the activity was originally launched on.
+     *     <li>{@link #DEVICE_POLICY_CUSTOM}: Does not inform the user of the blocked activity
+     *     launch. The virtual device owner can use this policy together with
+     *     {@link VirtualDeviceManager.ActivityListener#onActivityLaunchBlocked} to provide custom
+     *     experience on the virtual device.
+     * </ul>
+     */
+    @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+    public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6;
+
     private final int mLockState;
     @NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
     @NavigationPolicy
@@ -1174,6 +1190,10 @@
                 mDevicePolicies.delete(POLICY_TYPE_CAMERA);
             }
 
+            if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+                mDevicePolicies.delete(POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR);
+            }
+
             if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE
                     || mAudioRecordingSessionId != AUDIO_SESSION_ID_GENERATE)
                     && mDevicePolicies.get(POLICY_TYPE_AUDIO, DEVICE_POLICY_DEFAULT)
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 065b3d6..b2f333a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -9025,6 +9025,10 @@
 
             final int uid = consumer.getUid();
             final Uid u = uidStats.get(uid);
+            if (u == null) {
+                continue;
+            }
+
             final long rxPackets = u.getNetworkActivityPackets(
                     BatteryStats.NETWORK_MOBILE_RX_DATA, STATS_SINCE_CHARGED);
             final long txPackets = u.getNetworkActivityPackets(
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index a459aaa..2c7b9c0 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -159,6 +159,8 @@
     @UnsupportedAppUsage
     private static UserEnvironment sCurrentUser;
     private static boolean sUserRequired;
+    private static Boolean sLegacyStorageAppOp;
+    private static Boolean sNoIsolatedStorageAppOp;
 
     static {
         initForCurrentUser();
@@ -1459,15 +1461,23 @@
         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
         final String opPackageName = context.getOpPackageName();
 
-        if (appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid,
-                opPackageName) == AppOpsManager.MODE_ALLOWED) {
-            return true;
+        if (sLegacyStorageAppOp == null) {
+            sLegacyStorageAppOp =
+              appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, opPackageName) ==
+                              AppOpsManager.MODE_ALLOWED;
+        }
+        if (sLegacyStorageAppOp) {
+            return sLegacyStorageAppOp;
         }
 
         // Legacy external storage access is granted to instrumentations invoked with
         // "--no-isolated-storage" flag.
-        return appOps.noteOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid,
-                opPackageName) == AppOpsManager.MODE_ALLOWED;
+        if (sNoIsolatedStorageAppOp == null) {
+            sNoIsolatedStorageAppOp =
+              appOps.checkOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid,
+                                    opPackageName) == AppOpsManager.MODE_ALLOWED;
+        }
+        return sNoIsolatedStorageAppOp;
     }
 
     private static boolean isScopedStorageEnforced(boolean defaultScopedStorage,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7926afe..f30a9f5 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -6367,6 +6367,33 @@
                 Settings.Global.DEVICE_DEMO_MODE, 0) > 0;
     }
 
+    private static final String CACHE_KEY_USER_SERIAL_NUMBER_PROPERTY =
+        PropertyInvalidatedCache.createPropertyName(
+            PropertyInvalidatedCache.MODULE_SYSTEM, "user_serial_number");
+
+    private final PropertyInvalidatedCache<Integer, Integer> mUserSerialNumberCache =
+            new PropertyInvalidatedCache<Integer, Integer>(
+                32, CACHE_KEY_USER_SERIAL_NUMBER_PROPERTY) {
+                @Override
+                public Integer recompute(Integer query) {
+                    try {
+                        return mService.getUserSerialNumber(query);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+                @Override
+                public boolean bypass(Integer query) {
+                    return query <= 0;
+                }
+            };
+
+
+    /** @hide */
+    public static final void invalidateUserSerialNumberCache() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_USER_SERIAL_NUMBER_PROPERTY);
+    }
+
     /**
      * Returns a serial number on this device for a given userId. User handles can be recycled
      * when deleting and creating users, but serial numbers are not reused until the device is wiped.
@@ -6376,6 +6403,14 @@
      */
     @UnsupportedAppUsage
     public int getUserSerialNumber(@UserIdInt int userId) {
+        if (android.multiuser.Flags.cacheUserSerialNumber()) {
+            // System user serial number is always 0, and it always exists.
+            // There is no need to call binder for that.
+            if (userId == UserHandle.USER_SYSTEM) {
+               return UserHandle.USER_SERIAL_SYSTEM;
+            }
+            return mUserSerialNumberCache.query(userId);
+        }
         try {
             return mService.getUserSerialNumber(userId);
         } catch (RemoteException re) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 57853e7..94c76929 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13396,7 +13396,19 @@
                 = "enable_freeform_support";
 
         /**
-         * Whether to enable experimental desktop mode on secondary displays.
+         * Whether to override the availability of the desktop mode on the main display of the
+         * device. If on, users can make move an app to the desktop, allowing a freeform windowing
+         * experience.
+         * @hide
+         */
+        @Readable
+        public static final String DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES =
+                "override_desktop_mode_features";
+
+        /**
+         * Whether to enable the legacy freeform support on secondary displays. If enabled, the
+         * SECONDARY_HOME of the launcher is started on any secondary display, allowing for a
+         * desktop experience.
          * @hide
          */
         @Readable
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index d7389ba..be60c25 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -38,3 +38,11 @@
     is_fixed_read_only: true
     bug: "323166383"
 }
+
+flag {
+    name: "perfetto_wm_tracing"
+    namespace: "windowing_tools"
+    description: "Migrate WindowManager tracing to Perfetto"
+    is_fixed_read_only: true
+    bug: "323165543"
+}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 02ea6d4..df2af73 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -29,6 +29,7 @@
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.window.flags.Flags.insetsControlSeq;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -890,7 +891,9 @@
         @InsetsType int visibleTypes = 0;
         @InsetsType int[] cancelledUserAnimationTypes = {0};
         for (int i = 0, size = newState.sourceSize(); i < size; i++) {
-            final InsetsSource source = newState.sourceAt(i);
+            final InsetsSource source = insetsControlSeq()
+                    ? new InsetsSource(newState.sourceAt(i))
+                    : newState.sourceAt(i);
             @InsetsType int type = source.getType();
             @AnimationType int animationType = getAnimationType(type);
             final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId());
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 6a92fd9..c73cbc6 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -28,6 +28,7 @@
 import static android.view.InsetsSourceConsumerProto.TYPE_NUMBER;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.window.flags.Flags.insetsControlSeq;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -410,7 +411,9 @@
 
         // Frame is changing while animating. Keep note of the new frame but keep existing frame
         // until animation is finished.
-        newSource = new InsetsSource(newSource);
+        if (!insetsControlSeq()) {
+            newSource = new InsetsSource(newSource);
+        }
         mPendingFrame = new Rect(newSource.getFrame());
         mPendingVisibleFrame = newSource.getVisibleFrame() != null
                 ? new Rect(newSource.getVisibleFrame())
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cda58e38..1525bd1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -114,6 +114,7 @@
 import static android.view.accessibility.Flags.forceInvertColor;
 import static android.view.accessibility.Flags.reduceWindowContentChangedEventThrottle;
 import static android.view.flags.Flags.addSchandleToVriSurface;
+import static android.view.flags.Flags.disableDrawWakeLock;
 import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix;
 import static android.view.flags.Flags.toolkitFrameRateFunctionEnablingReadOnly;
@@ -149,6 +150,8 @@
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
 import android.app.servertransaction.WindowStateTransactionItem;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -337,6 +340,15 @@
     private static final int LOGTAG_VIEWROOT_DRAW_EVENT = 60004;
 
     /**
+     * This change disables the {@code DRAW_WAKE_LOCK}, an internal wakelock acquired per-frame
+     * duration display DOZE. It was added to allow animation during AOD. This wakelock consumes
+     * battery severely if the animation is too heavy, so, it will be removed.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static final long DISABLE_DRAW_WAKE_LOCK = 349153669L;
+
+    /**
      * Set to false if we do not want to use the multi threaded renderer even though
      * threaded renderer (aka hardware renderering) is used. Note that by disabling
      * this, WindowCallbacks will not fire.
@@ -2459,6 +2471,11 @@
     }
 
     void pokeDrawLockIfNeeded() {
+        // Disable DRAW_WAKE_LOCK starting U. Otherwise, only need to acquire it for DOZE state.
+        if (CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock()) {
+            return;
+        }
+
         if (!Display.isDozeState(mAttachInfo.mDisplayState)) {
             // Only need to acquire wake lock for DOZE state.
             return;
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 199a69a..5b1c7d5 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -256,6 +256,21 @@
             "ignore_relayout_auth_pending";
 
     /**
+     * Fixes to handle apps relaying out, and causing problems for autofill.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_ENABLE_RELAYOUT = "enable_relayout";
+
+    /**
+     * Enable relative location of views for fingerprinting for relayout.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_ENABLE_RELATIVE_LOCATION_FOR_RELAYOUT =
+            "enable_relative_location_for_relayout";
+
+    /**
      * Bugfix flag, Autofill should only fill in value from current session.
      *
      * See frameworks/base/services/autofill/bugfixes.aconfig#fill_fields_from_current_session_only
@@ -543,6 +558,22 @@
                 false);
     }
 
+    /** @hide */
+    public static boolean enableRelayoutFixes() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_AUTOFILL,
+                DEVICE_CONFIG_ENABLE_RELAYOUT,
+                true);
+    }
+
+    /** @hide */
+    public static boolean enableRelativeLocationForRelayout() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_AUTOFILL,
+                DEVICE_CONFIG_ENABLE_RELATIVE_LOCATION_FOR_RELAYOUT,
+                false);
+    }
+
     /** @hide **/
     public static boolean shouldFillFieldsFromCurrentSessionOnly() {
         return DeviceConfig.getBoolean(
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 0d4c556..515ed0e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -748,7 +748,16 @@
 
     // Controls logic around apps changing some properties of their views when activity loses
     // focus due to autofill showing biometric activity, password manager, or password breach check.
-    private boolean mRelayoutFix;
+    // Deprecated. TODO: Remove it after ramp of new solution.
+    private boolean mRelayoutFixDeprecated;
+
+    // Controls logic around apps changing some properties of their views when activity loses
+    // focus due to autofill showing biometric activity, password manager, or password breach check.
+    private final boolean mRelayoutFix;
+
+    // Controls logic around apps changing some properties of their views when activity loses
+    // focus due to autofill showing biometric activity, password manager, or password breach check.
+    private final boolean mRelativePositionForRelayout;
 
     // Indicates whether the credman integration is enabled.
     private final boolean mIsCredmanIntegrationEnabled;
@@ -978,11 +987,31 @@
         mShouldIncludeInvisibleViewInAssistStructure =
                 AutofillFeatureFlags.shouldIncludeInvisibleViewInAssistStructure();
 
-        mRelayoutFix = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending();
+        mRelayoutFixDeprecated = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending();
+        mRelayoutFix = AutofillFeatureFlags.enableRelayoutFixes();
+        mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
         mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
     }
 
     /**
+     * Whether to apply relayout fixes.
+     *
+     * @hide
+     */
+    public boolean isRelayoutFixEnabled() {
+        return mRelayoutFix;
+    }
+
+    /**
+     * Whether to use relative positions and locations of the views for disambiguation.
+     *
+     * @hide
+     */
+    public boolean isRelativePositionForRelayoutEnabled() {
+        return mRelativePositionForRelayout;
+    }
+
+    /**
      * Whether to apply heuristic check on important views before triggering fill request
      *
      * @hide
@@ -1779,7 +1808,7 @@
                 }
                 return;
             }
-            if (mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION) {
+            if (mRelayoutFixDeprecated && mState == STATE_PENDING_AUTHENTICATION) {
                 if (sVerbose) {
                     Log.v(TAG, "notifyViewVisibilityChanged(): ignoring in auth pending mode");
                 }
@@ -2917,7 +2946,7 @@
             Intent fillInIntent, boolean authenticateInline) {
         synchronized (mLock) {
             if (sessionId == mSessionId) {
-                if (mRelayoutFix) {
+                if (mRelayoutFixDeprecated) {
                     mState = STATE_PENDING_AUTHENTICATION;
                 }
                 final AutofillClient client = getClient();
@@ -3778,7 +3807,7 @@
 
     @GuardedBy("mLock")
     private boolean isPendingAuthenticationLocked() {
-        return mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION;
+        return mRelayoutFixDeprecated && mState == STATE_PENDING_AUTHENTICATION;
     }
 
     @GuardedBy("mLock")
@@ -4322,6 +4351,13 @@
                         addToSet(mInvisibleDialogTrackedIds, id);
                     }
                 }
+            } else {
+                if (sDebug) {
+                    // isClientVisibleForAutofillLocked() is checking whether
+                    // activity has stopped under the hood
+                    Log.d(TAG, "notifyViewVisibilityChangedLocked(): ignoring "
+                            + "view visibility change since activity has stopped");
+                }
             }
 
             if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 4d4e4af..f570a9a 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -89,4 +89,12 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "disable_draw_wake_lock"
+    namespace: "wear_frameworks"
+    description: "Disable Draw Wakelock starting U."
+    bug: "331698645"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 8cd2a3e..68e33c6 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -181,3 +181,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "windowing_sdk"
+    name: "per_user_display_window_settings"
+    description: "Whether to store display window settings per user to avoid conflicts"
+    bug: "346668297"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index 6420620..a3fcfad 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -72,6 +72,7 @@
             UserShortcutType.TRIPLETAP,
             UserShortcutType.TWOFINGER_DOUBLETAP,
             UserShortcutType.QUICK_SETTINGS,
+            UserShortcutType.GESTURE
     })
     public @interface UserShortcutType {
         int DEFAULT = 0;
@@ -81,6 +82,7 @@
         int TRIPLETAP = 1 << 2;
         int TWOFINGER_DOUBLETAP = 1 << 3;
         int QUICK_SETTINGS = 1 << 4;
+        int GESTURE = 1 << 5;
         // LINT.ThenChange(:shortcut_type_array)
     }
 
@@ -95,6 +97,7 @@
             UserShortcutType.TRIPLETAP,
             UserShortcutType.TWOFINGER_DOUBLETAP,
             UserShortcutType.QUICK_SETTINGS,
+            UserShortcutType.GESTURE
             // LINT.ThenChange(:shortcut_type_intdef)
     };
 
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
index c8d6194..01cbb55 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
@@ -20,6 +20,7 @@
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
 import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityButtonLongPressStatus;
@@ -66,6 +67,9 @@
                 NAV_BAR_MODE_GESTURAL == getResources().getInteger(
                         com.android.internal.R.integer.config_navBarInteractionMode);
 
+        final int targetType = (isGestureNavigateEnabled
+                && android.provider.Flags.a11yStandaloneGestureEnabled()) ? GESTURE : SOFTWARE;
+
         if (isGestureNavigateEnabled) {
             final TextView promptPrologue = findViewById(R.id.accessibility_button_prompt_prologue);
             promptPrologue.setText(isTouchExploreOn
@@ -78,7 +82,7 @@
                     : R.string.accessibility_gesture_instructional_text);
         }
 
-        mTargets.addAll(getTargets(this, SOFTWARE));
+        mTargets.addAll(getTargets(this, targetType));
 
         final GridView gridview = findViewById(R.id.accessibility_button_chooser_grid);
         gridview.setAdapter(new ButtonTargetAdapter(mTargets));
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 6256dbc..44c7543 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -18,7 +18,6 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getInstalledTargets;
 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
 import static com.android.internal.accessibility.util.AccessibilityUtils.isUserSetupCompleted;
@@ -213,10 +212,7 @@
         final boolean isEditMenuMode =
                 mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT;
         final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title;
-        final int editDialogTitleId =
-                mShortcutType == SOFTWARE
-                        ? R.string.accessibility_edit_shortcut_menu_button_title
-                        : R.string.accessibility_edit_shortcut_menu_volume_title;
+        final int editDialogTitleId = R.string.accessibility_edit_shortcut_menu_volume_title;
 
         mMenuDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId));
         mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
index ec90dd2..a753110 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.accessibility.dialog;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings;
@@ -197,6 +198,9 @@
     @VisibleForTesting
     public static boolean isRecognizedShortcutType(@UserShortcutType int shortcutType) {
         int mask = SOFTWARE | HARDWARE;
+        if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            mask = mask | GESTURE;
+        }
         return (shortcutType != 0 && (shortcutType & mask) == shortcutType);
     }
 }
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 8e18f84..9d66461 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -23,6 +23,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
@@ -247,6 +248,8 @@
                 } else {
                     return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
                 }
+            case GESTURE:
+                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
             case HARDWARE:
                 return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
             case QUICK_SETTINGS:
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 6b0ca9f..48f86ff 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -166,6 +166,8 @@
         switch (type) {
             case UserShortcutType.SOFTWARE:
                 return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
+            case UserShortcutType.GESTURE:
+                return Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
             case UserShortcutType.HARDWARE:
                 return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
             case UserShortcutType.TRIPLETAP:
@@ -190,6 +192,7 @@
     public static int convertToType(String key) {
         return switch (key) {
             case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE;
+            case Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS -> UserShortcutType.GESTURE;
             case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS;
             case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE;
             case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED ->
diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java
similarity index 94%
rename from core/java/com/android/internal/protolog/common/ProtoLog.java
rename to core/java/com/android/internal/protolog/ProtoLog.java
index 8149cd5..0118c05 100644
--- a/core/java/com/android/internal/protolog/common/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/ProtoLog.java
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.internal.protolog.common;
+package com.android.internal.protolog;
+
+import com.android.internal.protolog.common.IProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogLevel;
 
 /**
  * ProtoLog API - exposes static logging methods. Usage of this API is similar
@@ -35,7 +39,9 @@
  * Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool
  * during build.
  */
+// LINT.IfChange
 public class ProtoLog {
+// LINT.ThenChange(frameworks/base/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt)
 
     // Needs to be set directly otherwise the protologtool tries to transform the method call
     public static boolean REQUIRE_PROTOLOGTOOL = true;
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index e44c727..565d584 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -62,7 +62,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        android:layout_toStartOf="@id/expand_button"
+        android:layout_toStartOf="@id/notification_buttons_column"
         android:layout_alignWithParentIfMissing="true"
         android:clipChildren="false"
         android:gravity="center_vertical"
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index e983427..61c7a8c 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -424,4 +424,11 @@
     <bool name="config_dropboxmanager_persistent_logging_enabled">false</bool>
     <java-symbol type="bool" name="config_dropboxmanager_persistent_logging_enabled" />
 
+    <!-- Telephony satellite gateway class name for handling carrier roaming to satellite is using ESOS messaging. -->
+    <string name="config_satellite_carrier_roaming_esos_provisioned_class" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_carrier_roaming_esos_provisioned_class" />
+
+    <!-- Telephony satellite gateway intent for handling carrier roaming to satellite is using ESOS messaging. -->
+    <string name="config_satellite_carrier_roaming_esos_provisioned_intent_action" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_carrier_roaming_esos_provisioned_intent_action" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f696e872..6b71f97 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -722,9 +722,6 @@
     <!-- label for screenshot item in power menu [CHAR LIMIT=24]-->
     <string name="global_action_screenshot">Screenshot</string>
 
-    <!-- description for mandatory biometrics prompt -->
-    <string name="identity_check_biometric_prompt_description">This is needed since Identity Check is on</string>
-
     <!-- Take bug report menu title [CHAR LIMIT=30] -->
     <string name="bugreport_title">Bug report</string>
     <!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7a51abc..7d50d22 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1927,7 +1927,6 @@
   <java-symbol type="string" name="global_action_voice_assist" />
   <java-symbol type="string" name="global_action_assist" />
   <java-symbol type="string" name="global_action_screenshot" />
-  <java-symbol type="string" name="identity_check_biometric_prompt_description" />
   <java-symbol type="string" name="invalidPuk" />
   <java-symbol type="string" name="lockscreen_carrier_default" />
   <java-symbol type="style" name="Animation.LockScreen" />
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
index 389b677..f01ac6f 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
@@ -16,15 +16,22 @@
 
 package com.android.internal.accessibility.dialog;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Flags;
+
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.accessibility.common.ShortcutConstants;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -32,9 +39,13 @@
 
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityTargetTest {
+    @Rule
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private static final int[] EXPECTED_TYPES = { HARDWARE, SOFTWARE };
+    private static final int[] EXPECTED_TYPES_GESTURE = { HARDWARE, SOFTWARE, GESTURE };
 
     @Test
+    @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
     public void isRecognizedShortcutType_expectedType_isTrue() {
         for (int type : EXPECTED_TYPES) {
             assertThat(AccessibilityTarget.isRecognizedShortcutType(type)).isTrue();
@@ -42,6 +53,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
     public void isRecognizedShortcutType_notExpectedType_isFalse() {
         for (int type: ShortcutConstants.USER_SHORTCUT_TYPES) {
             if (IntStream.of(EXPECTED_TYPES).noneMatch(x -> x == type)) {
@@ -49,4 +61,28 @@
             }
         }
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void isRecognizedShortcutType_expectedType_gestureIncluded_isTrue() {
+        for (int type : EXPECTED_TYPES_GESTURE) {
+            if (!AccessibilityTarget.isRecognizedShortcutType(type)) {
+                throw new AssertionError(
+                        "Shortcut type " + type + " should be recognized");
+            }
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void isRecognizedShortcutType_notExpectedType_gestureIncluded_isFalse() {
+        for (int type: ShortcutConstants.USER_SHORTCUT_TYPES) {
+            if (IntStream.of(EXPECTED_TYPES_GESTURE).noneMatch(x -> x == type)) {
+                if (AccessibilityTarget.isRecognizedShortcutType(type)) {
+                    throw new AssertionError(
+                            "Shortcut type " + type + " should not be recognized");
+                }
+            }
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
index 928fce9..8bebc62 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
@@ -20,6 +20,8 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
 
 import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -99,8 +101,7 @@
     public void getShortcutTargets_softwareShortcutNoService_emptyResult() {
         assertThat(
                 ShortcutUtils.getShortcutTargetsFromSettings(
-                        mContext,
-                        ShortcutConstants.UserShortcutType.SOFTWARE, mDefaultUserId)
+                        mContext, SOFTWARE, mDefaultUserId)
         ).isEmpty();
     }
 
@@ -114,13 +115,21 @@
     }
 
     @Test
+    public void getShortcutTargets_gestureShortcutNoService_emptyResult() {
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContext, GESTURE, mDefaultUserId)
+        ).isEmpty();
+    }
+
+    @Test
     public void getShortcutTargets_softwareShortcut1Service_return1Service() {
         setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
         setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
 
         assertThat(
                 ShortcutUtils.getShortcutTargetsFromSettings(
-                        mContext, ShortcutConstants.UserShortcutType.SOFTWARE,
+                        mContext, SOFTWARE,
                         mDefaultUserId)
         ).containsExactlyElementsIn(ONE_COMPONENT);
     }
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index c573cf4a..a115c65 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -40,6 +40,7 @@
         <permission name="android.permission.MASTER_CLEAR"/>
         <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
         <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
+        <permission name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
         <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
@@ -92,6 +93,5 @@
         <permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" />
         <permission name="android.permission.CONTROL_UI_TRACING" />
         <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
-        <permission name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
     </privapp-permissions>
 </permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index f9a6caf..612b387 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -17,6 +17,7 @@
 package androidx.window.extensions.embedding;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
@@ -29,6 +30,7 @@
 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishPrimaryWithSecondary;
 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishSecondaryWithPrimary;
 
+import android.annotation.ColorInt;
 import android.app.Activity;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.Intent;
@@ -391,13 +393,22 @@
         if (splitAttributes == null) {
             return TaskFragmentAnimationParams.DEFAULT;
         }
+        final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes);
+        TaskFragmentAnimationParams.Builder builder = new TaskFragmentAnimationParams.Builder();
+        if (animationBackgroundColor != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
+            builder.setAnimationBackgroundColor(animationBackgroundColor);
+        }
+        // TODO(b/293658614): Allow setting custom open/close/changeAnimationResId.
+        return builder.build();
+    }
+
+    @ColorInt
+    private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) {
+        int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR;
         final AnimationBackground animationBackground = splitAttributes.getAnimationBackground();
         if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) {
-            return new TaskFragmentAnimationParams.Builder()
-                    .setAnimationBackgroundColor(colorBackground.getColor())
-                    .build();
-        } else {
-            return TaskFragmentAnimationParams.DEFAULT;
+            animationBackgroundColor = colorBackground.getColor();
         }
+        return animationBackgroundColor;
     }
 }
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 25d3067..1e68241 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -88,7 +88,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) transform-protolog-calls " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
         "--loggroups-jar $(location :wm_shell_protolog-groups) " +
         "--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " +
@@ -107,7 +107,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
         "--loggroups-jar $(location :wm_shell_protolog-groups) " +
         "--viewer-config-type json " +
@@ -124,7 +124,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
         "--loggroups-jar $(location :wm_shell_protolog-groups) " +
         "--viewer-config-type proto " +
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/OWNERS b/libs/WindowManager/Shell/multivalentScreenshotTests/OWNERS
new file mode 100644
index 0000000..dc11241
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/OWNERS
@@ -0,0 +1,4 @@
+atsjenk@google.com
+liranb@google.com
+madym@google.com
+mpodolian@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
index eb28881..027b28e 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
index eb28881..027b28e 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 9e1440d..ae60d8b 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -27,7 +27,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
 import com.android.wm.shell.common.bubbles.BubbleBarLocation
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 327e205..5e67333 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -32,7 +32,7 @@
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.launcher3.icons.BubbleIconFactory
 import com.android.wm.shell.Flags
 import com.android.wm.shell.R
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index ace2c13..935d129 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -27,7 +27,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner
 import com.android.wm.shell.bubbles.DeviceConfig
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
index 4876f32..92084e4 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
@@ -16,9 +16,13 @@
 
 package com.android.wm.shell.shared;
 
+import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.SystemProperties;
+import android.provider.Settings;
+import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -29,6 +33,8 @@
  */
 public class DesktopModeStatus {
 
+    private static final String TAG = "DesktopModeStatus";
+
     /**
      * Flag to indicate whether task resizing is veiled.
      */
@@ -98,11 +104,12 @@
             "persist.wm.debug.desktop_max_task_limit", DEFAULT_MAX_TASK_LIMIT);
 
     /**
-     * Return {@code true} if desktop windowing is enabled. Only to be used for testing. Callers
-     * should use {@link #canEnterDesktopMode(Context)} to query the state of desktop windowing.
+     * Return {@code true} if desktop windowing flag is enabled. Only to be used for testing.
+     * Callers should use {@link #canEnterDesktopMode(Context)} to query the state of desktop
+     * windowing.
      */
     @VisibleForTesting
-    public static boolean isEnabled() {
+    public static boolean isDesktopModeFlagEnabled() {
         return Flags.enableDesktopWindowingMode();
     }
 
@@ -120,7 +127,7 @@
      */
     public static boolean useWindowShadow(boolean isFocusedWindow) {
         return USE_WINDOW_SHADOWS
-            || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow);
+                || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow);
     }
 
     /**
@@ -154,10 +161,36 @@
     }
 
     /**
+     * Return {@code true} if desktop mode dev option should be shown on current device
+     */
+    public static boolean canShowDesktopModeDevOption(@NonNull Context context) {
+        return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption();
+    }
+
+    /** Returns if desktop mode dev option should be enabled if there is no user override. */
+    public static boolean shouldDevOptionBeEnabledByDefault() {
+        return isDesktopModeFlagEnabled();
+    }
+
+    /**
      * Return {@code true} if desktop mode is enabled and can be entered on the current device.
      */
     public static boolean canEnterDesktopMode(@NonNull Context context) {
-        return (!enforceDeviceRestrictions() || isDesktopModeSupported(context)) && isEnabled();
+        if (!isDeviceEligibleForDesktopMode(context)) return false;
+
+        // If dev option has ever been manually toggled by the user, return its value
+        // TODO(b/348193756) : Move the logic for DW override based on toggle overides to a common
+        //  infrastructure and add caching for the computation
+        int defaultOverrideState = -1;
+        int toggleState = Settings.Global.getInt(context.getContentResolver(),
+                DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, defaultOverrideState);
+        if (toggleState != defaultOverrideState) {
+            Log.d(TAG, "Using Desktop mode dev option overridden state");
+            return toggleState != 0;
+        }
+
+        // Return Desktop windowing flag value
+        return isDesktopModeFlagEnabled();
     }
 
     /**
@@ -181,4 +214,11 @@
         return DESKTOP_DENSITY_OVERRIDE >= DESKTOP_DENSITY_MIN
                 && DESKTOP_DENSITY_OVERRIDE <= DESKTOP_DENSITY_MAX;
     }
+
+    /**
+     * Return {@code true} if desktop mode is unrestricted and is supported in the device.
+     */
+    private static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
+        return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index ef9bf00..514307f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -19,7 +19,7 @@
 import com.android.internal.protolog.LegacyProtoLogImpl;
 import com.android.internal.protolog.common.ILogger;
 import com.android.internal.protolog.common.IProtoLog;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
index 2e5448a..b9bf136 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
@@ -29,7 +29,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.sysui.ShellInit;
 
 import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 5143d41..9f01316 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -38,7 +38,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.sysui.ShellInit;
 
 import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index bebfa90..ebdea1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -52,7 +52,7 @@
 import android.window.TaskOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 7041ea3..ece0271 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -59,7 +59,7 @@
 import android.window.IOnBackInvokedCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.view.AppearanceRegion;
 import com.android.wm.shell.R;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index c9d3dbd..4f04c5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -48,7 +48,7 @@
 import com.android.internal.jank.Cuj
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.internal.policy.SystemBarUtils
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.animation.Interpolators
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 381914a5..103a654 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -49,7 +49,7 @@
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.policy.SystemBarUtils;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
index c738ce5..e266e2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -28,7 +28,7 @@
 import android.window.BackNavigationInfo
 import com.android.internal.R
 import com.android.internal.policy.TransitionAnimation
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import com.android.wm.shell.shared.annotations.ShellMainThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 2aefc64..7dbbb04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -48,7 +48,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index c853301..e36f6e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -84,7 +84,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.CollectionUtils;
 import com.android.launcher3.icons.BubbleIconFactory;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 761e025..4e6c517 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -35,7 +35,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubbles.DismissReason;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index c7ccd50..f7a5c27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -67,7 +67,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.AlphaOptimizedButton;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 18e04d1..bf98ef8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -42,7 +42,7 @@
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 2382545..0cf187b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -29,7 +29,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.bubbles.BubbleBarLocation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 09bec8c..f93f19d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -78,7 +78,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 0b66bcb..c79d9c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -35,7 +35,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.taskview.TaskView;
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
index 1375684..9429c9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
@@ -29,7 +29,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
index b7107f0..d4f53ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
@@ -28,7 +28,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 /**
  * Handles {@link MotionEvent}s for bubbles that begin in the nav bar area
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
index aa4129a..fbef6b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
@@ -38,7 +38,7 @@
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.bubbles.BubbleExpandedView;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index e261d92..f792392 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -28,7 +28,7 @@
 import android.window.WindowContainerTransactionCallback;
 import android.window.WindowOrganizer;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.transition.LegacyTransitions;
 
 import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java
index 43c92ca..43f9cb98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java
@@ -32,7 +32,7 @@
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.sysui.ShellInit;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 58007b5..8e026f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -27,7 +27,7 @@
 import android.util.Size;
 import android.view.Gravity;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 7ceaaea..64a1b0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -32,7 +32,7 @@
 import android.util.Size;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.TriConsumer;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java
index c421dec..b9c698e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java
@@ -26,7 +26,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 3e9366f..dcf84d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -30,7 +30,7 @@
 import android.util.Pair
 import android.util.TypedValue
 import android.window.TaskSnapshot
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.Flags
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import kotlin.math.abs
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 2234041..3ad60e7 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
@@ -54,7 +54,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
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 8ced76f..d3c349f 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
@@ -59,7 +59,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.animation.Interpolators;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index e710560..a67dee3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -35,7 +35,7 @@
 import androidx.core.util.putAll
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.InstanceIdSequence
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 217b1d3..1bf1259 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -19,6 +19,8 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.TaskInfo
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
 import android.content.pm.ActivityInfo.isFixedOrientationLandscape
 import android.content.pm.ActivityInfo.isFixedOrientationPortrait
 import android.content.res.Configuration.ORIENTATION_LANDSCAPE
@@ -105,7 +107,7 @@
  * Calculates the largest size that can fit in a given area while maintaining a specific aspect
  * ratio.
  */
-private fun maximumSizeMaintainingAspectRatio(
+fun maximumSizeMaintainingAspectRatio(
     taskInfo: RunningTaskInfo,
     targetArea: Size,
     aspectRatio: Float
@@ -114,7 +116,8 @@
     val targetWidth = targetArea.width
     val finalHeight: Int
     val finalWidth: Int
-    if (isFixedOrientationPortrait(taskInfo.topActivityInfo!!.screenOrientation)) {
+    // Get orientation either through top activity or task's orientation
+    if (taskInfo.hasPortraitTopActivity()) {
         val tempWidth = (targetHeight / aspectRatio).toInt()
         if (tempWidth <= targetWidth) {
             finalHeight = targetHeight
@@ -137,7 +140,7 @@
 }
 
 /** Calculates the aspect ratio of an activity from its fullscreen bounds. */
-private fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
+fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
     if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
         val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxWidth
         val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxHeight
@@ -171,3 +174,41 @@
         desiredSize.height + heightOffset
     )
 }
+
+/**
+ * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the
+ * entire screen, as area can be offset by left and top start.
+ */
+fun centerInArea(desiredSize: Size, areaBounds: Rect, leftStart: Int, topStart: Int): Rect {
+    val heightOffset = (areaBounds.height() - desiredSize.height) / 2
+    val widthOffset = (areaBounds.width() - desiredSize.width) / 2
+
+    val newLeft = leftStart + widthOffset
+    val newTop = topStart + heightOffset
+    val newRight = newLeft + desiredSize.width
+    val newBottom = newTop + desiredSize.height
+
+    return Rect(newLeft, newTop, newRight, newBottom)
+}
+
+fun TaskInfo.hasPortraitTopActivity(): Boolean {
+    val topActivityScreenOrientation =
+        topActivityInfo?.screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED
+    val appBounds = configuration.windowConfiguration.appBounds
+
+    return when {
+        // First check if activity has portrait screen orientation
+        topActivityScreenOrientation != SCREEN_ORIENTATION_UNSPECIFIED -> {
+            isFixedOrientationPortrait(topActivityScreenOrientation)
+        }
+
+        // Then check if the activity is portrait when letterboxed
+        appCompatTaskInfo.topActivityBoundsLetterboxed -> appCompatTaskInfo.isTopActivityPillarboxed
+
+        // Then check if the activity is portrait
+        appBounds != null -> appBounds.height() > appBounds.width()
+
+        // Otherwise just take the orientation of the task
+        else -> isFixedOrientationPortrait(configuration.orientation)
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 5813f85..d8e8c57 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -36,6 +36,7 @@
 import android.graphics.Region
 import android.os.IBinder
 import android.os.SystemProperties
+import android.util.Size
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
@@ -649,13 +650,21 @@
     fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) {
         val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
 
-        val stableBounds = Rect()
-        displayLayout.getStableBounds(stableBounds)
+        val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
         val destinationBounds = Rect()
-        if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) {
-            // The desktop task is currently occupying the whole stable bounds. If the bounds
-            // before the task was toggled to stable bounds were saved, toggle the task to those
-            // bounds. Otherwise, toggle to the default bounds.
+
+        val isMaximized = if (taskInfo.isResizeable) {
+            currentTaskBounds == stableBounds
+        } else {
+            currentTaskBounds.width() == stableBounds.width()
+                    || currentTaskBounds.height() == stableBounds.height()
+        }
+
+        if (isMaximized) {
+            // The desktop task is at the maximized width and/or height of the stable bounds.
+            // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
+            // Otherwise, toggle to the default bounds.
             val taskBoundsBeforeMaximize =
                 desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
             if (taskBoundsBeforeMaximize != null) {
@@ -670,9 +679,20 @@
         } else {
             // Save current bounds so that task can be restored back to original bounds if necessary
             // and toggle to the stable bounds.
-            val taskBounds = taskInfo.configuration.windowConfiguration.bounds
-            desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, taskBounds)
-            destinationBounds.set(stableBounds)
+            desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)
+
+            if (taskInfo.isResizeable) {
+                // if resizable then expand to entire stable bounds (full display minus insets)
+                destinationBounds.set(stableBounds)
+            } else {
+                // if non-resizable then calculate max bounds according to aspect ratio
+                val activityAspectRatio = calculateAspectRatio(taskInfo)
+                val newSize = maximumSizeMaintainingAspectRatio(taskInfo,
+                    Size(stableBounds.width(), stableBounds.height()), activityAspectRatio)
+                val newBounds = centerInArea(
+                    newSize, stableBounds, stableBounds.left, stableBounds.top)
+                destinationBounds.set(newBounds)
+            }
         }
 
         val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
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 a4813a3..b3c3a3d 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
@@ -59,7 +59,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
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 b1882fc..9c7476d 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
@@ -66,7 +66,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
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 5df83be..910175e 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,7 +20,6 @@
 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 android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 
@@ -56,7 +55,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
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 41a50b1..3bedef2 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
@@ -30,7 +30,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index 2ccadb8..b6b49a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -38,7 +38,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index 31214eb..ffcfe644 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -25,7 +25,7 @@
 import android.window.IGlobalDragListener
 import android.window.IUnhandledDragCallback
 import androidx.annotation.VisibleForTesting
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import java.util.function.Consumer
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index b48aee5..1641668 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -25,7 +25,7 @@
 import android.util.SparseArray;
 import android.view.SurfaceControl;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 2626e73..d2ceb67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -27,7 +27,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index cd478e5..333c75f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -49,7 +49,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 0a3c15b..dc449d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -37,7 +37,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.pip.PipUtils;
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 3fae370..6a6e2ec 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
@@ -73,7 +73,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.animation.Interpolators;
@@ -636,6 +636,13 @@
             return;
         }
 
+        // bail early if leash is null
+        if (mLeash == null) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "exitPip: leash is null");
+            return;
+        }
+
         final Rect destinationBounds = new Rect(getExitDestinationBounds());
         final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index f3a8fbf..e5633de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -63,7 +63,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index a7c47f9..8d36db9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -41,7 +41,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
@@ -127,8 +127,10 @@
 
     /**
      * Called when the Shell wants to start resizing Pip transition/animation.
+     *
+     * @param duration the suggested duration for resize animation.
      */
-    public void startResizeTransition(WindowContainerTransaction wct) {
+    public void startResizeTransition(WindowContainerTransaction wct, int duration) {
         // Default implementation does nothing.
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 0169e8c..c7369a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -32,7 +32,7 @@
 import android.view.ViewRootImpl;
 import android.view.WindowManagerGlobal;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.pip.PipBoundsState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 448d4f5..d1d8275 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -59,7 +59,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayChangeController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index f6cab48..d1978c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -28,7 +28,7 @@
 import android.view.InputChannel;
 import android.view.InputEvent;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 15342be..c189642 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -59,7 +59,7 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index f5bd006..df3803d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -34,7 +34,7 @@
 import android.graphics.Rect;
 import android.os.Debug;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.common.FloatingContentCoordinator;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index d8ac8e9..9c4e723 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -48,7 +48,7 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
index 5d858fa..cb82db6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
@@ -23,7 +23,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index 6b890c4..50d22ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -33,7 +33,7 @@
 import android.content.Context;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.pip.PipMediaController;
 import com.android.wm.shell.common.pip.PipUtils;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java
index 0221db8..eb7a10c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBackgroundView.java
@@ -28,7 +28,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index 72c0cd7..188c35f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -33,7 +33,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 8a215b4..1afb470 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -26,7 +26,7 @@
 import android.os.Handler;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index b6a7c56..0ed5079 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -39,7 +39,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
index 977aad4..327ceef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
@@ -27,7 +27,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.TvWindowMenuActionButton;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 6b5bdd2..e74870d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -37,7 +37,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.pip.PipMenuController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
index adc03cf..eabf1b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
@@ -39,7 +39,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 
 import java.util.Arrays;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 4a767ef..c7704f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -50,7 +50,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.widget.LinearLayoutManager;
 import com.android.internal.widget.RecyclerView;
 import com.android.wm.shell.R;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 54e162b..ce50792 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -33,7 +33,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ImageUtils;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.pip.PipMediaController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index ca0d61f..7a0e669 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -62,7 +62,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 5c561fe..88f9e4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -47,9 +47,17 @@
     @Nullable
     private Runnable mAnimationEndCallback;
     private RectEvaluator mRectEvaluator;
+
+    // Bounds relative to which scaling/cropping must be done.
     private final Rect mBaseBounds = new Rect();
+
+    // Bounds to animate from.
     private final Rect mStartBounds = new Rect();
+
+    // Target bounds.
     private final Rect mEndBounds = new Rect();
+
+    // Bounds updated by the evaluator as animator is running.
     private final Rect mAnimatedRect = new Rect();
     private final float mDelta;
 
@@ -84,7 +92,6 @@
         addListener(this);
         addUpdateListener(this);
         setEvaluator(mRectEvaluator);
-        // TODO: change this
         setDuration(duration);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 6e36a32..9cfe162 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -32,7 +32,7 @@
 import android.view.ViewRootImpl;
 import android.view.WindowManagerGlobal;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.pip.PipBoundsState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index fc0d36d..06adad6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -36,7 +36,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.Preconditions;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -352,6 +352,7 @@
         mPipBoundsAlgorithm.dump(pw, innerPrefix);
         mPipBoundsState.dump(pw, innerPrefix);
         mPipDisplayLayoutState.dump(pw, innerPrefix);
+        mPipTransitionState.dump(pw, innerPrefix);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
index b757b00..ffda56d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
@@ -28,7 +28,7 @@
 import android.view.InputChannel;
 import android.view.InputEvent;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
index 42b8e9f..c54e4cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
@@ -59,7 +59,7 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 495cd00..e277a8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip2.phone;
 
+import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY;
 import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_NO_BOUNCY;
 import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
 import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
@@ -25,6 +26,7 @@
 import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
 import static com.android.wm.shell.pip2.phone.PipMenuView.ANIM_TYPE_DISMISS;
 import static com.android.wm.shell.pip2.phone.PipMenuView.ANIM_TYPE_NONE;
+import static com.android.wm.shell.pip2.phone.PipTransition.ANIMATING_BOUNDS_CHANGE_DURATION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -35,7 +37,8 @@
 import android.os.Debug;
 import android.view.SurfaceControl;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.util.Preconditions;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -45,6 +48,7 @@
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipPerfHintController;
 import com.android.wm.shell.common.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip2.animation.PipResizeAnimator;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
 
@@ -62,6 +66,7 @@
         PipTransitionState.PipTransitionStateChangedListener {
     private static final String TAG = "PipMotionHelper";
     private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change";
+    private static final String ANIMATING_BOUNDS_CHANGE = "animating_bounds_change";
     private static final boolean DEBUG = false;
 
     private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
@@ -113,7 +118,7 @@
 
     /** SpringConfig to use for fling-then-spring animations. */
     private final PhysicsAnimator.SpringConfig mSpringConfig =
-            new PhysicsAnimator.SpringConfig(700f, DAMPING_RATIO_NO_BOUNCY);
+            new PhysicsAnimator.SpringConfig(300f, DAMPING_RATIO_LOW_BOUNCY);
 
     /** SpringConfig used for animating into the dismiss region, matches the one in
      * {@link MagnetizedObject}. */
@@ -152,9 +157,16 @@
     private boolean mDismissalPending = false;
 
     /**
-     * Set to true if bounds change transition has been scheduled from PipMotionHelper.
+     * Set to true if bounds change transition has been scheduled from PipMotionHelper
+     * after animating is over.
      */
-    private boolean mWaitingForBoundsChangeTransition = false;
+    private boolean mWaitingForFlingTransition = false;
+
+    /**
+     * Set to true if bounds change transition has been scheduled from PipMotionHelper,
+     * and if the animation is supposed to run while transition is playing.
+     */
+    private boolean mWaitingToPlayBoundsChangeTransition = false;
 
     /**
      * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
@@ -634,6 +646,9 @@
         // The physics animation ended, though we may not necessarily be done animating, such as
         // when we're still dragging after moving out of the magnetic target.
         if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) {
+            // Update the earlier estimate on bounds we are animating towards, since physics
+            // animator is non-deterministic.
+            setAnimatingToBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
             // do not schedule resize if PiP is dismissing, which may cause app re-open to
             // mBounds instead of its normal bounds.
             Bundle extra = new Bundle();
@@ -673,6 +688,11 @@
      * Directly resizes the PiP to the given {@param bounds}.
      */
     private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
+        if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
+            // Do not carry out any resizing if we are dragging or physics animator is running.
+            return;
+        }
+
         if (DEBUG) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                     "%s: resizeAndAnimatePipUnchecked: toBounds=%s"
@@ -682,10 +702,11 @@
 
         // Intentionally resize here even if the current bounds match the destination bounds.
         // This is so all the proper callbacks are performed.
-
-        // mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration,
-        //         TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND, null /* updateBoundsCallback */);
-        // setAnimatingToBounds(toBounds);
+        setAnimatingToBounds(toBounds);
+        Bundle extra = new Bundle();
+        extra.putBoolean(ANIMATING_BOUNDS_CHANGE, true);
+        extra.putInt(ANIMATING_BOUNDS_CHANGE_DURATION, duration);
+        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
     }
 
     @Override
@@ -694,7 +715,11 @@
             @Nullable Bundle extra) {
         switch (newState) {
             case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
-                if (!extra.getBoolean(FLING_BOUNDS_CHANGE)) break;
+                mWaitingForFlingTransition = extra.getBoolean(FLING_BOUNDS_CHANGE);
+                mWaitingToPlayBoundsChangeTransition = extra.getBoolean(ANIMATING_BOUNDS_CHANGE);
+                if (!mWaitingForFlingTransition && !mWaitingToPlayBoundsChangeTransition) {
+                    break;
+                }
 
                 if (mPipBoundsState.getBounds().equals(
                         mPipBoundsState.getMotionBoundsState().getBoundsInMotion())) {
@@ -709,30 +734,30 @@
                     break;
                 }
 
-                // If touch is turned off and we are in a fling animation, schedule a transition.
-                mWaitingForBoundsChangeTransition = true;
+                // Delay config until the end, if we are animating after scheduling the transition.
                 mPipScheduler.scheduleAnimateResizePip(
-                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+                        mPipBoundsState.getMotionBoundsState().getAnimatingToBounds(),
+                        mWaitingToPlayBoundsChangeTransition,
+                        extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
+                                PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION));
                 break;
             case PipTransitionState.CHANGING_PIP_BOUNDS:
-                if (!mWaitingForBoundsChangeTransition) break;
-
-                // If bounds change transition was scheduled from this class, handle leash updates.
-                mWaitingForBoundsChangeTransition = false;
                 SurfaceControl.Transaction startTx = extra.getParcelable(
                         PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+                SurfaceControl.Transaction finishTx = extra.getParcelable(
+                        PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
                 Rect destinationBounds = extra.getParcelable(
                         PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
-                startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
-                        destinationBounds.left, destinationBounds.top);
-                startTx.apply();
+                final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
+                        PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
 
-                // All motion operations have actually finished, so make bounds cache updates.
-                settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
-                cleanUpHighPerfSessionMaybe();
-
-                // Signal that the transition is done - should update transition state by default.
-                mPipScheduler.scheduleFinishResizePip(false /* configAtEnd */);
+                if (mWaitingForFlingTransition) {
+                    mWaitingForFlingTransition = false;
+                    handleFlingTransition(startTx, finishTx, destinationBounds);
+                } else if (mWaitingToPlayBoundsChangeTransition) {
+                    mWaitingToPlayBoundsChangeTransition = false;
+                    startResizeAnimation(startTx, finishTx, destinationBounds, duration);
+                }
                 break;
             case PipTransitionState.EXITING_PIP:
                 // We need to force finish any local animators if about to leave PiP, to avoid
@@ -740,9 +765,46 @@
                 if (!mPipBoundsState.getMotionBoundsState().isInMotion()) break;
                 cancelPhysicsAnimation();
                 settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
+                break;
         }
     }
 
+    private void handleFlingTransition(SurfaceControl.Transaction startTx,
+            SurfaceControl.Transaction finishTx, Rect destinationBounds) {
+        startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
+                destinationBounds.left, destinationBounds.top);
+        startTx.apply();
+
+        // All motion operations have actually finished, so make bounds cache updates.
+        settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
+        cleanUpHighPerfSessionMaybe();
+
+        // Signal that the transition is done - should update transition state by default.
+        mPipScheduler.scheduleFinishResizePip(false /* configAtEnd */);
+    }
+
+    private void startResizeAnimation(SurfaceControl.Transaction startTx,
+            SurfaceControl.Transaction finishTx, Rect destinationBounds, int duration) {
+        SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+        Preconditions.checkState(pipLeash != null,
+                "No leash cached by mPipTransitionState=" + mPipTransitionState);
+
+        startTx.setWindowCrop(pipLeash, mPipBoundsState.getBounds().width(),
+                mPipBoundsState.getBounds().height());
+
+        PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
+                startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
+                destinationBounds, duration, 0f /* angle */);
+        animator.setAnimationEndCallback(() -> {
+            mPipBoundsState.setBounds(destinationBounds);
+            // All motion operations have actually finished, so make bounds cache updates.
+            cleanUpHighPerfSessionMaybe();
+            // Signal that we are done with resize transition
+            mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
+        });
+        animator.start();
+    }
+
     private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) {
         if (!animatingAfter) {
             // The physics animation ended, though we may not necessarily be done animating, such as
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 33e80bd..5b0ca18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -16,6 +16,7 @@
 package com.android.wm.shell.pip2.phone;
 
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.wm.shell.pip2.phone.PipTransition.ANIMATING_BOUNDS_CHANGE_DURATION;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -535,7 +536,8 @@
                 mWaitingForBoundsChangeTransition = true;
 
                 // Schedule PiP resize transition, but delay any config updates until very end.
-                mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds, true /* configAtEnd */);
+                mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds,
+                        true /* configAtEnd */, PINCH_RESIZE_SNAP_DURATION);
                 break;
             case PipTransitionState.CHANGING_PIP_BOUNDS:
                 if (!mWaitingForBoundsChangeTransition) break;
@@ -550,12 +552,15 @@
                         PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
                 SurfaceControl.Transaction finishTx = extra.getParcelable(
                         PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
+                final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
+                        PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
+
                 startTx.setWindowCrop(pipLeash, mPipBoundsState.getBounds().width(),
                         mPipBoundsState.getBounds().height());
 
                 PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
                         startTx, finishTx, mPipBoundsState.getBounds(), mStartBoundsAfterRelease,
-                        mLastResizeBounds, PINCH_RESIZE_SNAP_DURATION, mAngle);
+                        mLastResizeBounds, duration, mAngle);
                 animator.setAnimationEndCallback(() -> {
                     // All motion operations have actually finished, so make bounds cache updates.
                     mUpdateResizeBoundsCallback.accept(mLastResizeBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 9c1e321..ac670cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -33,7 +33,7 @@
 import androidx.annotation.Nullable;
 import androidx.core.content.ContextCompat;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipUtils;
@@ -162,6 +162,18 @@
      * @param configAtEnd true if we are delaying config updates until the transition ends.
      */
     public void scheduleAnimateResizePip(Rect toBounds, boolean configAtEnd) {
+        scheduleAnimateResizePip(toBounds, configAtEnd,
+                PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
+    }
+
+    /**
+     * Animates resizing of the pinned stack given the duration.
+     *
+     * @param configAtEnd true if we are delaying config updates until the transition ends.
+     * @param duration    the suggested duration to run the animation; the component responsible
+     *                    for running the animator will get this as an extra.
+     */
+    public void scheduleAnimateResizePip(Rect toBounds, boolean configAtEnd, int duration) {
         if (mPipTransitionState.mPipTaskToken == null || !mPipTransitionState.isInPip()) {
             return;
         }
@@ -170,7 +182,7 @@
         if (configAtEnd) {
             wct.deferConfigToTransitionEnd(mPipTransitionState.mPipTaskToken);
         }
-        mPipTransitionController.startResizeTransition(wct);
+        mPipTransitionController.startResizeTransition(wct, duration);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 56a465a..0c4ed26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -51,7 +51,7 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
@@ -713,15 +713,13 @@
         }
     }
 
-    private void animateToMaximizedState(Runnable callback) {
-        Rect maxMovementBounds = new Rect();
+    private void animateToMaximizedState() {
         Rect maxBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x,
                 mPipBoundsState.getMaxSize().y);
-        mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, maxMovementBounds,
-                mIsImeShowing ? mImeHeight : 0);
+
         mSavedSnapFraction = mMotionHelper.animateToExpandedState(maxBounds,
-                mPipBoundsState.getMovementBounds(), maxMovementBounds,
-                callback);
+                getMovementBounds(mPipBoundsState.getBounds()),
+                getMovementBounds(maxBounds), null /* callback */);
     }
 
     private void animateToNormalSize(Runnable callback) {
@@ -729,22 +727,20 @@
         mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
 
         final Size minMenuSize = mMenuController.getEstimatedMinMenuSize();
-        final Rect normalBounds = mPipBoundsState.getNormalBounds();
-        final Rect destBounds = mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds,
-                minMenuSize);
-        Rect restoredMovementBounds = new Rect();
-        mPipBoundsAlgorithm.getMovementBounds(destBounds,
-                mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
-        mSavedSnapFraction = mMotionHelper.animateToExpandedState(destBounds,
-                mPipBoundsState.getMovementBounds(), restoredMovementBounds, callback);
+        final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
+        final Rect normalBounds = new Rect(0, 0, defaultSize.getWidth(), defaultSize.getHeight());
+        final Rect adjustedNormalBounds = mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(
+                normalBounds, minMenuSize);
+
+        mSavedSnapFraction = mMotionHelper.animateToExpandedState(adjustedNormalBounds,
+                getMovementBounds(mPipBoundsState.getBounds()),
+                getMovementBounds(adjustedNormalBounds), callback /* callback */);
     }
 
     private void animateToUnexpandedState(Rect restoreBounds) {
-        Rect restoredMovementBounds = new Rect();
-        mPipBoundsAlgorithm.getMovementBounds(restoreBounds,
-                mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
         mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
-                restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */);
+                getMovementBounds(restoreBounds),
+                getMovementBounds(mPipBoundsState.getBounds()), false /* immediate */);
         mSavedSnapFraction = -1f;
     }
 
@@ -919,10 +915,6 @@
                     && mMenuState != MENU_STATE_FULL) {
                 // If using pinch to zoom, double-tap functions as resizing between max/min size
                 if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
-                    final boolean toExpand = mPipBoundsState.getBounds().width()
-                            < mPipBoundsState.getMaxSize().x
-                            && mPipBoundsState.getBounds().height()
-                            < mPipBoundsState.getMaxSize().y;
                     if (mMenuController.isMenuVisible()) {
                         mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
                     }
@@ -934,7 +926,7 @@
                     // actually toggle to the size chosen
                     if (nextSize == PipDoubleTapHelper.SIZE_SPEC_MAX) {
                         mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
-                        animateToMaximizedState(null);
+                        animateToMaximizedState();
                     } else if (nextSize == PipDoubleTapHelper.SIZE_SPEC_DEFAULT) {
                         mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
                         animateToNormalSize(null);
@@ -1045,7 +1037,9 @@
 
     private Rect getMovementBounds(Rect curBounds) {
         Rect movementBounds = new Rect();
-        mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds,
+        Rect insetBounds = new Rect();
+        mPipBoundsAlgorithm.getInsetBounds(insetBounds);
+        mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
                 movementBounds, mIsImeShowing ? mImeHeight : 0);
         return movementBounds;
     }
@@ -1091,6 +1085,7 @@
             case PipTransitionState.ENTERED_PIP:
                 onActivityPinned();
                 mTouchState.setAllowInputEvents(true);
+                mTouchState.reset();
                 break;
             case PipTransitionState.EXITED_PIP:
                 mTouchState.setAllowInputEvents(false);
@@ -1101,6 +1096,7 @@
                 break;
             case PipTransitionState.CHANGED_PIP_BOUNDS:
                 mTouchState.setAllowInputEvents(true);
+                mTouchState.reset();
                 break;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java
index d093f1e..bb8d4ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchState.java
@@ -23,7 +23,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 57dc5f9..683d30d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -71,6 +71,9 @@
     static final String PIP_START_TX = "pip_start_tx";
     static final String PIP_FINISH_TX = "pip_finish_tx";
     static final String PIP_DESTINATION_BOUNDS = "pip_dest_bounds";
+    static final String ANIMATING_BOUNDS_CHANGE_DURATION =
+            "animating_bounds_change_duration";
+    static final int BOUNDS_CHANGE_JUMPCUT_DURATION = 0;
 
     /**
      * The fixed start delay in ms when fading out the content overlay from bounds animation.
@@ -87,7 +90,7 @@
     private final PipTransitionState mPipTransitionState;
 
     //
-    // Transition tokens
+    // Transition caches
     //
 
     @Nullable
@@ -96,6 +99,8 @@
     private IBinder mExitViaExpandTransition;
     @Nullable
     private IBinder mResizeTransition;
+    private int mBoundsChangeDuration = BOUNDS_CHANGE_JUMPCUT_DURATION;
+
 
     //
     // Internal state and relevant cached info
@@ -152,11 +157,12 @@
     }
 
     @Override
-    public void startResizeTransition(WindowContainerTransaction wct) {
+    public void startResizeTransition(WindowContainerTransaction wct, int duration) {
         if (wct == null) {
             return;
         }
         mResizeTransition = mTransitions.startTransition(TRANSIT_RESIZE_PIP, wct, this);
+        mBoundsChangeDuration = duration;
     }
 
     @Nullable
@@ -272,6 +278,10 @@
         extra.putParcelable(PIP_START_TX, startTransaction);
         extra.putParcelable(PIP_FINISH_TX, finishTransaction);
         extra.putParcelable(PIP_DESTINATION_BOUNDS, pipChange.getEndAbsBounds());
+        if (mBoundsChangeDuration > BOUNDS_CHANGE_JUMPCUT_DURATION) {
+            extra.putInt(ANIMATING_BOUNDS_CHANGE_DURATION, mBoundsChangeDuration);
+            mBoundsChangeDuration = BOUNDS_CHANGE_JUMPCUT_DURATION;
+        }
 
         mFinishCallback = finishCallback;
         mPipTransitionState.setState(PipTransitionState.CHANGING_PIP_BOUNDS, extra);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 9d599ca..29272be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -29,6 +29,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -62,6 +63,8 @@
  * and throw an <code>IllegalStateException</code> otherwise.</p>
  */
 public class PipTransitionState {
+    private static final String TAG = PipTransitionState.class.getSimpleName();
+
     public static final int UNDEFINED = 0;
 
     // State for Launcher animating the swipe PiP to home animation.
@@ -190,8 +193,9 @@
                     "No extra bundle for " + stateToString(state) + " state.");
         }
         if (mState != state) {
-            dispatchPipTransitionStateChanged(mState, state, extra);
+            final int prevState = mState;
             mState = state;
+            dispatchPipTransitionStateChanged(prevState, mState, extra);
         }
     }
 
@@ -319,4 +323,11 @@
         return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)",
                 stateToString(mState), mInSwipePipToHomeTransition);
     }
+
+    /** Dumps internal state. */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mState=" + stateToString(mState));
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index ad298dc..814eaae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -43,7 +43,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
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 c67cf1d..e46625d 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
@@ -63,7 +63,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index 64e26db..1cbb8bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -23,7 +23,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SyncTransactionQueue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index f5fbae5..27fd309 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -24,7 +24,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SyncTransactionQueue;
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 dd219d3..b4941a5 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
@@ -73,7 +73,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 0541a02..b3dab85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -46,7 +46,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitDecorManager;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
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 45eff4a..d9e9776 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
@@ -119,7 +119,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 0f3d6ca..1076eca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -45,7 +45,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
index 1d8a8d5..e0f6394 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
@@ -36,7 +36,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.split.SplitScreenConstants;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 2b12a22..90eb22f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -74,7 +74,7 @@
 import com.android.internal.graphics.palette.Quantizer;
 import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
 import com.android.internal.policy.PhoneWindow;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.common.TransactionPool;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
index e552e6c..08211ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
@@ -53,7 +53,7 @@
 import android.window.StartingWindowInfo;
 import android.window.StartingWindowRemovalInfo;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 3353c7b..97a695f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -43,7 +43,7 @@
 import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 8fc54ed..6e084d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -48,7 +48,7 @@
 import android.window.StartingWindowInfo;
 import android.window.TaskSnapshot;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.view.BaseIWindow;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 72fc8686..2036d9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -35,7 +35,7 @@
 
 import android.window.StartingWindowInfo;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
index 2e6ddc3..aa9f15c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
@@ -18,7 +18,7 @@
 
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index 5ced1fb..0202b6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -40,7 +40,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index 2e2f569..3a68009 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -25,7 +25,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 
 import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 8ee1efa..4f4b809 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -39,7 +39,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
 import com.android.wm.shell.common.split.SplitScreenUtils;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index c33fb80..c8921d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -28,7 +28,7 @@
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9db153f..73b32a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -103,7 +103,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.common.DisplayController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
index 89b0e25..978b8da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
@@ -28,7 +28,7 @@
 import android.view.WindowManager;
 import android.window.IWindowContainerTransactionCallback;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 /**
  * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index 7a42236..8cc7f21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -35,7 +35,7 @@
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 69c4167..c5dc668 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -30,7 +30,7 @@
 import android.window.WindowAnimationState;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 9fc6702..391c5fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -30,7 +30,7 @@
 import android.window.TransitionInfo;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index d686046..6013a1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -39,7 +39,7 @@
 
 import androidx.annotation.BinderThread;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.TransitionUtil;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 2047b5a..a5f071a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -54,7 +54,7 @@
 
 import com.android.internal.R;
 import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.TransitionUtil;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index f6e38da..ec6802d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -75,7 +75,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 7c2ba45..88bfebf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -33,7 +33,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt
index 564e716..ee6c81a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt
@@ -18,10 +18,10 @@
 
 import android.util.Log
 import com.android.internal.protolog.common.IProtoLogGroup
-import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.ProtoLog
 
 /**
- * Log messages using an API similar to [com.android.internal.protolog.common.ProtoLog]. Useful for
+ * Log messages using an API similar to [com.android.internal.protolog.ProtoLog]. Useful for
  * logging from Kotlin classes as ProtoLog does not have support for Kotlin.
  *
  * All messages are logged to logcat if logging is enabled for that [IProtoLogGroup].
@@ -29,42 +29,42 @@
 // TODO(b/168581922): remove once ProtoLog adds support for Kotlin
 class KtProtoLog {
     companion object {
-        /** @see [com.android.internal.protolog.common.ProtoLog.d] */
+        /** @see [com.android.internal.protolog.ProtoLog.d] */
         fun d(group: IProtoLogGroup, messageString: String, vararg args: Any) {
             if (group.isLogToLogcat) {
                 Log.d(group.tag, String.format(messageString, *args))
             }
         }
 
-        /** @see [com.android.internal.protolog.common.ProtoLog.v] */
+        /** @see [com.android.internal.protolog.ProtoLog.v] */
         fun v(group: IProtoLogGroup, messageString: String, vararg args: Any) {
             if (group.isLogToLogcat) {
                 Log.v(group.tag, String.format(messageString, *args))
             }
         }
 
-        /** @see [com.android.internal.protolog.common.ProtoLog.i] */
+        /** @see [com.android.internal.protolog.ProtoLog.i] */
         fun i(group: IProtoLogGroup, messageString: String, vararg args: Any) {
             if (group.isLogToLogcat) {
                 Log.i(group.tag, String.format(messageString, *args))
             }
         }
 
-        /** @see [com.android.internal.protolog.common.ProtoLog.w] */
+        /** @see [com.android.internal.protolog.ProtoLog.w] */
         fun w(group: IProtoLogGroup, messageString: String, vararg args: Any) {
             if (group.isLogToLogcat) {
                 Log.w(group.tag, String.format(messageString, *args))
             }
         }
 
-        /** @see [com.android.internal.protolog.common.ProtoLog.e] */
+        /** @see [com.android.internal.protolog.ProtoLog.e] */
         fun e(group: IProtoLogGroup, messageString: String, vararg args: Any) {
             if (group.isLogToLogcat) {
                 Log.e(group.tag, String.format(messageString, *args))
             }
         }
 
-        /** @see [com.android.internal.protolog.common.ProtoLog.wtf] */
+        /** @see [com.android.internal.protolog.ProtoLog.wtf] */
         fun wtf(group: IProtoLogGroup, messageString: String, vararg args: Any) {
             if (group.isLogToLogcat) {
                 Log.wtf(group.tag, String.format(messageString, *args))
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS
new file mode 100644
index 0000000..482aaab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS
@@ -0,0 +1 @@
+per-file KtProtolog.kt = file:platform/development:/tools/winscope/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 180e4f9..a05dbf8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -73,7 +73,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.Cuj;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index d902444..a3616f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -55,7 +55,7 @@
 import android.view.WindowManagerGlobal;
 import android.window.InputTransferToken;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index 35ed8de..7873a85 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -65,7 +65,7 @@
         setup {
             standardAppHelper.launchViaIntent(
                 wmHelper,
-                YouTubeAppHelper.getYoutubeVideoIntent("HPcEAtoXXLA"),
+                YouTubeAppHelper.getYoutubeVideoIntent("3KtWfp0UopM"),
                 ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "")
             )
             standardAppHelper.waitForVideoPlaying()
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
index 879034f..5c539a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
@@ -73,7 +73,7 @@
         setup {
             standardAppHelper.launchViaIntent(
                 wmHelper,
-                YouTubeAppHelper.getYoutubeVideoIntent("HPcEAtoXXLA"),
+                YouTubeAppHelper.getYoutubeVideoIntent("3KtWfp0UopM"),
                 ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "")
             )
             standardAppHelper.enterFullscreen()
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
index c671fbe..b5a6d83 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -48,6 +48,8 @@
     fun setup() {
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
+        // TODO: b/349075982 - Remove once launcher rotation and checks are stable.
+        tapl.setExpectedRotationCheckEnabled(false)
 
         SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index 51a20ee..a2df22c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -26,7 +26,7 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 0e53e10..0bcbe13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -193,7 +193,7 @@
             .strictness(Strictness.LENIENT)
             .spyStatic(DesktopModeStatus::class.java)
             .startMocking()
-    whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
+    whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(true)
     doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
     shellInit = spy(ShellInit(testExecutor))
@@ -264,7 +264,7 @@
 
   @Test
   fun instantiate_flagOff_doNotAddInitCallback() {
-    whenever(DesktopModeStatus.isEnabled()).thenReturn(false)
+    whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(false)
     clearInvocations(shellInit)
 
     createController()
@@ -1916,6 +1916,26 @@
   }
 
   @Test
+  fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
+    val bounds = Rect(0, 0, 200, 100)
+    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+      topActivityInfo = ActivityInfo().apply {
+        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+        configuration.windowConfiguration.appBounds = bounds
+      }
+      isResizeable = false
+    }
+
+    // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
+    val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)
+
+    controller.toggleDesktopTaskSize(task)
+    // Assert bounds set to stable bounds
+    val wct = getLatestToggleResizeDesktopTaskWct()
+    assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+  }
+
+  @Test
   fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
     val bounds = Rect(0, 0, 100, 100)
     val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
@@ -1942,6 +1962,46 @@
   }
 
   @Test
+  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualWidth() {
+    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
+      isResizeable = false
+    }
+
+    // Maximize
+    controller.toggleDesktopTaskSize(task)
+    task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left,
+      boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom)
+
+    // Restore
+    controller.toggleDesktopTaskSize(task)
+
+    // Assert bounds set to last bounds before maximize
+    val wct = getLatestToggleResizeDesktopTaskWct()
+    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+  }
+
+  @Test
+  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualHeight() {
+    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
+    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
+      isResizeable = false
+    }
+
+    // Maximize
+    controller.toggleDesktopTaskSize(task)
+    task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left,
+      STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom)
+
+    // Restore
+    controller.toggleDesktopTaskSize(task)
+
+    // Assert bounds set to last bounds before maximize
+    val wct = getLatestToggleResizeDesktopTaskWct()
+    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
+  }
+
+  @Test
   fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
     val boundsBeforeMaximize = Rect(0, 0, 100, 100)
     val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index 86aded7..ac5aeec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -27,6 +27,9 @@
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.window.WindowContainerToken
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
 import com.android.wm.shell.common.DisplayController
@@ -37,6 +40,7 @@
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertTrue
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -45,6 +49,7 @@
 import org.mockito.Mockito.any
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.quality.Strictness
 
 /**
  * Tests for [DragPositioningCallbackUtility].
@@ -82,9 +87,13 @@
     @Rule
     val setFlagsRule = SetFlagsRule()
 
+    private lateinit var mockitoSession: StaticMockitoSession
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java).startMocking()
 
         whenever(taskToken.asBinder()).thenReturn(taskBinder)
         whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
@@ -105,6 +114,11 @@
         whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
     }
 
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
     @Test
     fun testChangeBoundsDoesNotChangeHeightWhenLessThanMin() {
         val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
@@ -252,7 +266,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() {
-        whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) }
         initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
         val startingPoint =
             PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
@@ -275,7 +289,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() {
-        whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) }
         initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
         val startingPoint =
             PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
@@ -361,7 +375,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun testChangeBoundsInDesktopMode_windowSizeExceedsStableBounds_shouldBeLimitedToDisplaySize() {
-        whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) }
         val startingPoint =
             PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
                 OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e134c23..124f1f0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7007,6 +7007,23 @@
     }
 
     /**
+     * Check whether a user can mute this stream type from a given UI element.
+     *
+     * <p>Only useful for volume controllers.
+     *
+     * @param streamType type of stream to check if it's mutable from UI
+     *
+     * @hide
+     */
+    public boolean isStreamMutableByUi(int streamType) {
+        try {
+            return getService().isStreamMutableByUi(streamType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Only useful for volume controllers.
      * @hide
      */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c8b9da5..08cc126 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -305,6 +305,8 @@
 
     boolean isStreamAffectedByMute(int streamType);
 
+    boolean isStreamMutableByUi(int streamType);
+
     void disableSafeMediaVolume(String callingPackage);
 
     oneway void lowerVolumeToRs1(String callingPackage);
diff --git a/packages/PackageInstaller/res/layout/install_content_view.xml b/packages/PackageInstaller/res/layout/install_content_view.xml
index 524a88a..affcca1 100644
--- a/packages/PackageInstaller/res/layout/install_content_view.xml
+++ b/packages/PackageInstaller/res/layout/install_content_view.xml
@@ -34,7 +34,7 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
+        style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
         android:text="@string/message_staging" />
 
     <ProgressBar
@@ -57,7 +57,7 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
+        style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
         android:text="@string/installing" />
 
     <ProgressBar
@@ -74,7 +74,7 @@
       android:id="@+id/install_confirm_question"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_confirm_question"
       android:visibility="invisible"
       android:scrollbars="vertical" />
@@ -83,7 +83,7 @@
       android:id="@+id/install_confirm_question_update"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_confirm_question_update"
       android:visibility="invisible"
       android:scrollbars="vertical" />
@@ -92,7 +92,7 @@
       android:id="@+id/install_success"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_done"
       android:visibility="invisible" />
 
@@ -100,7 +100,7 @@
       android:id="@+id/install_failed"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_failed"
       android:visibility="invisible" />
 
@@ -108,7 +108,7 @@
       android:id="@+id/install_failed_blocked"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_failed_blocked"
       android:visibility="invisible" />
 
@@ -116,7 +116,7 @@
       android:id="@+id/install_failed_conflict"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_failed_conflict"
       android:visibility="invisible" />
 
@@ -124,7 +124,7 @@
       android:id="@+id/install_failed_incompatible"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_failed_incompatible"
       android:visibility="invisible" />
 
@@ -132,7 +132,7 @@
       android:id="@+id/install_failed_invalid_apk"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      style="@android:style/TextAppearance.Material.Subhead"
+      style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
       android:text="@string/install_failed_invalid_apk"
       android:visibility="invisible" />
 
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
index 434e333..d5c5d8b 100644
--- a/packages/PackageInstaller/res/layout/uninstall_content_view.xml
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -22,32 +22,32 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
-  <LinearLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:theme="?android:attr/alertDialogTheme"
-      android:orientation="vertical"
-      android:paddingTop="8dp"
-      android:paddingStart="?android:attr/dialogPreferredPadding"
-      android:paddingEnd="?android:attr/dialogPreferredPadding"
-      android:clipToPadding="false">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/alertDialogTheme"
+        android:orientation="vertical"
+        android:paddingTop="8dp"
+        android:paddingStart="?android:attr/dialogPreferredPadding"
+        android:paddingEnd="?android:attr/dialogPreferredPadding"
+        android:clipToPadding="false">
 
-      <TextView
-          android:id="@+id/message"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          style="@android:style/TextAppearance.Material.Subhead" />
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle" />
 
-      <CheckBox
-          android:id="@+id/keepData"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:layout_marginTop="8dp"
-          android:layout_marginStart="-8dp"
-          android:paddingLeft="8sp"
-          android:visibility="gone"
-          style="@android:style/TextAppearance.Material.Subhead" />
+        <CheckBox
+            android:id="@+id/keepData"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:layout_marginStart="-8dp"
+            android:paddingStart="8sp"
+            android:paddingEnd="0sp"
+            android:visibility="gone"
+            style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle" />
 
-  </LinearLayout>
+    </LinearLayout>
 </ScrollView>
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index a5b82b3..37588ad 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -19,7 +19,6 @@
 
     <style name="Theme.AlertDialogActivity"
         parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
-        <item name="alertDialogStyle">@style/AlertDialog</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowAnimationStyle">@null</item>
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index f5af510..aa48712 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -19,7 +19,6 @@
 
     <style name="Theme.AlertDialogActivity"
         parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
-        <item name="alertDialogStyle">@style/AlertDialog</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowAnimationStyle">@null</item>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 2e9b7b4..a23df64 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -97,6 +97,7 @@
         private set
     private var callingUid = Process.INVALID_UID
     private var originatingUid = Process.INVALID_UID
+    private var originatingUidFromSessionInfo = Process.INVALID_UID
     private var callingPackage: String? = null
     private var sessionStager: SessionStager? = null
     private lateinit var intent: Intent
@@ -136,17 +137,25 @@
 
         callingPackage = callerInfo.packageName
 
-        if (sessionId != SessionInfo.INVALID_ID) {
-            val sessionInfo: SessionInfo? = packageInstaller.getSessionInfo(sessionId)
-            callingPackage = sessionInfo?.getInstallerPackageName()
-            callingAttributionTag = sessionInfo?.getInstallerAttributionTag()
-        }
-
         // Uid of the source package, coming from ActivityManager
         callingUid = callerInfo.uid
         if (callingUid == Process.INVALID_UID) {
             Log.e(LOG_TAG, "Could not determine the launching uid.")
         }
+
+        originatingUidFromSessionInfo = callingUid
+        val sessionInfo: SessionInfo? =
+            if (sessionId != SessionInfo.INVALID_ID)
+                packageInstaller.getSessionInfo(sessionId)
+            else null
+        if (sessionInfo != null) {
+            callingPackage = sessionInfo.installerPackageName
+            callingAttributionTag = sessionInfo.installerAttributionTag
+            if (sessionInfo.originatingUid != Process.INVALID_UID) {
+                originatingUidFromSessionInfo = sessionInfo.originatingUid
+            }
+        }
+
         val sourceInfo: ApplicationInfo? = getSourceInfo(callingPackage)
         // Uid of the source package, with a preference to uid from ApplicationInfo
         originatingUid = sourceInfo?.uid ?: callingUid
@@ -651,7 +660,12 @@
     private fun getUpdateMessage(pkgInfo: PackageInfo, userActionReason: Int): String? {
         if (isAppUpdating(pkgInfo)) {
             val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo)
-            val requestedUpdateOwnerLabel = getApplicationLabel(callingPackage)
+
+            val originatingPackageNameFromSessionInfo =
+                getPackageNameForUid(context, originatingUidFromSessionInfo, callingPackage)
+            val requestedUpdateOwnerLabel =
+                getApplicationLabel(originatingPackageNameFromSessionInfo)
+
             if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
                 && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
             ) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 27c386e..cfd74d4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1010,10 +1010,10 @@
     <!-- UI debug setting: force allow on external summary [CHAR LIMIT=150] -->
     <string name="force_resizable_activities_summary">Make all activities resizable for multi-window, regardless of manifest values.</string>
 
-    <!-- UI debug setting: enable freeform window support [CHAR LIMIT=50] -->
-    <string name="enable_freeform_support">Enable freeform windows</string>
-    <!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] -->
-    <string name="enable_freeform_support_summary">Enable support for experimental freeform windows.</string>
+    <!-- UI debug setting: enable legacy freeform window support [CHAR LIMIT=50] -->
+    <string name="enable_freeform_support">Enable freeform windows (legacy)</string>
+    <!-- UI debug setting: enable legacy freeform window support summary [CHAR LIMIT=150] -->
+    <string name="enable_freeform_support_summary">Enable support for experimental legacy freeform windows.</string>
 
     <!-- Local (desktop) backup password menu title [CHAR LIMIT=25] -->
     <string name="local_backup_password_title">Desktop backup password</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 837c682..c88c4c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -182,7 +182,7 @@
             minVolume = getMinVolume(audioStream),
             maxVolume = audioManager.getStreamMaxVolume(audioStream.value),
             volume = audioManager.getStreamVolume(audioStream.value),
-            isAffectedByMute = audioManager.isStreamAffectedByMute(audioStream.value),
+            isAffectedByMute = audioManager.isStreamMutableByUi(audioStream.value),
             isAffectedByRingerMode = audioManager.isStreamAffectedByRingerMode(audioStream.value),
             isMuted = audioManager.isStreamMute(audioStream.value),
         )
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d54236e..54f0d7a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1189,6 +1189,8 @@
 
         synchronized (mLock) {
             if (getSyncDisabledModeConfigLocked() != SYNC_DISABLED_MODE_NONE) {
+                Slog.v(LOG_TAG, "did not write settings for prefix '"
+                                + prefix + "' because sync is disabled");
                 return SET_ALL_RESULT_DISABLED;
             }
             final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 5f8e933..411decd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -177,6 +177,7 @@
                     Settings.Global.DESK_UNDOCK_SOUND,
                     Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
                     Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+                    Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
                     Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
                     Settings.Global.DEVELOPMENT_FORCE_RTL,
                     Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1b9a09d..666d939 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -280,6 +280,9 @@
     <!-- to adjust volume in volume panel -->
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 
+    <!-- to get bluetooth audio device category -->
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
+
     <!-- to access ResolverRankerServices -->
     <uses-permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" />
 
@@ -373,8 +376,6 @@
     <!-- Listen to (dis-)connection of external displays and enable / disable them. -->
     <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
 
-    <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
-
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 0731616..f5153e1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1122,3 +1122,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "translucent_occluding_activity_fix"
+  namespace: "systemui"
+  description: "Fixes occlusion animation for transluent activities"
+  bug: "303010980"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index b6e4e9b..c14ee62 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -22,6 +22,7 @@
 import android.app.TaskInfo
 import android.app.WindowConfiguration
 import android.content.ComponentName
+import android.graphics.Color
 import android.graphics.Matrix
 import android.graphics.Rect
 import android.graphics.RectF
@@ -53,6 +54,7 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.systemui.Flags.activityTransitionUseLargestWindow
+import com.android.systemui.Flags.translucentOccludingActivityFix
 import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
 import com.android.wm.shell.shared.IShellTransitions
 import com.android.wm.shell.shared.ShellTransitions
@@ -991,7 +993,12 @@
                     controller.createAnimatorState()
                 }
             val windowBackgroundColor =
-                window.taskInfo?.let { callback.getBackgroundColor(it) } ?: window.backgroundColor
+                if (translucentOccludingActivityFix() && window.isTranslucent) {
+                    Color.TRANSPARENT
+                } else {
+                    window.taskInfo?.let { callback.getBackgroundColor(it) }
+                        ?: window.backgroundColor
+                }
 
             // TODO(b/184121838): We should somehow get the top and bottom radius of the window
             // instead of recomputing isExpandingFullyAbove here.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 4eae14b3..07898b0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -108,7 +108,12 @@
     private val draggingItemLayoutInfo: LazyGridItemInfo?
         get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
 
-    internal fun onDragStart(offset: Offset, contentOffset: Offset) {
+    /**
+     * Called when dragging is initiated.
+     *
+     * @return {@code True} if dragging a grid item, {@code False} otherwise.
+     */
+    internal fun onDragStart(offset: Offset, contentOffset: Offset): Boolean {
         state.layoutInfo.visibleItemsInfo
             .filter { item -> contentListState.isItemEditable(item.index) }
             // grid item offset is based off grid content container so we need to deduct
@@ -118,7 +123,10 @@
                 dragStartPointerOffset = offset - this.offset.toOffset()
                 draggingItemIndex = index
                 draggingItemInitialOffset = this.offset.toOffset()
+                return true
             }
+
+        return false
     }
 
     internal fun onDragInterrupted() {
@@ -216,8 +224,9 @@
                     dragDropState.onDrag(offset = offset)
                 },
                 onDragStart = { offset ->
-                    dragDropState.onDragStart(offset, contentOffset)
-                    viewModel.onReorderWidgetStart()
+                    if (dragDropState.onDragStart(offset, contentOffset)) {
+                        viewModel.onReorderWidgetStart()
+                    }
                 },
                 onDragEnd = {
                     dragDropState.onDragInterrupted()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 22566e7..9c9e6c6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -34,6 +34,8 @@
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -56,7 +58,6 @@
  *   must have entries in this map.
  * @param modifier A modifier.
  */
-@OptIn(ExperimentalComposeUiApi::class)
 @Composable
 fun SceneContainer(
     viewModel: SceneContainerViewModel,
@@ -66,8 +67,6 @@
 ) {
     val coroutineScope = rememberCoroutineScope()
     val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle()
-    val currentDestinations by
-        viewModel.currentDestinationScenes(coroutineScope).collectAsStateWithLifecycle()
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
@@ -88,20 +87,19 @@
         onDispose { viewModel.setTransitionState(null) }
     }
 
+    val userActionsBySceneKey: Map<SceneKey, Map<UserAction, UserActionResult>> =
+        sceneByKey.values.associate { scene ->
+            val userActions by scene.destinationScenes.collectAsStateWithLifecycle(emptyMap())
+            val resolvedUserActions = viewModel.resolveSceneFamilies(userActions)
+            scene.key to resolvedUserActions
+        }
+
     Box(
         modifier = Modifier.fillMaxSize(),
     ) {
         SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
             sceneByKey.forEach { (sceneKey, composableScene) ->
-                scene(
-                    key = sceneKey,
-                    userActions =
-                        if (sceneKey == currentSceneKey) {
-                            currentDestinations
-                        } else {
-                            viewModel.resolveSceneFamilies(composableScene.destinationScenes.value)
-                        },
-                ) {
+                scene(key = sceneKey, userActions = checkNotNull(userActionsBySceneKey[sceneKey])) {
                     with(composableScene) {
                         this@scene.Content(
                             modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index fa79ea0..54e0725 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1284,6 +1284,41 @@
     }
 
     @Test
+    public void onAodDownAndDownTouchReceived() throws RemoteException {
+        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+                0L);
+        final TouchProcessorResult processorResultDown =
+                new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+                        -1 /* pointerId */, touchData);
+
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+        // WHEN fingerprint is requested because of AOD interrupt
+        // GIVEN there's been an AoD interrupt
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+        mScreenObserver.onScreenTurnedOn();
+        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
+        mFgExecutor.runAllReady();
+
+        // and an ACTION_DOWN is received and touch is within sensor
+        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+                processorResultDown);
+        MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
+        mBiometricExecutor.runAllReady();
+        firstDownEvent.recycle();
+
+        // THEN the touch is only processed once
+        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
+                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
+                anyBoolean());
+    }
+
+    @Test
     public void onTouch_pilferPointerWhenAltBouncerShowing()
             throws RemoteException {
         final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index fe683e07..dcc9c7a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -47,6 +47,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
+@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
 @RunWith(AndroidJUnit4::class)
 class CommunalDreamStartableTest : SysuiTestCase() {
     private val kosmos = testKosmos()
@@ -76,7 +77,7 @@
         testScope.runTest {
             keyguardRepository.setDreaming(false)
             powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
-            whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true)
+            whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
             runCurrent()
 
             verify(dreamManager, never()).startDream()
@@ -92,7 +93,7 @@
         testScope.runTest {
             keyguardRepository.setDreaming(false)
             powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
-            whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true)
+            whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
             runCurrent()
 
             verify(dreamManager, never()).startDream()
@@ -118,7 +119,7 @@
             keyguardRepository.setDreaming(false)
             powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
             // Not eligible to dream
-            whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(false)
+            whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(false)
             transition(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB)
 
             verify(dreamManager, never()).startDream()
@@ -129,7 +130,7 @@
         testScope.runTest {
             keyguardRepository.setDreaming(true)
             powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
-            whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true)
+            whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
             transition(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB)
 
             verify(dreamManager, never()).startDream()
@@ -140,7 +141,7 @@
         testScope.runTest {
             keyguardRepository.setDreaming(true)
             powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
-            whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true)
+            whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
 
             // Verify we do not trigger dreaming for any other state besides glanceable hub
             for (state in KeyguardState.entries) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index fbe2c2e..cf14547 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -451,24 +451,6 @@
             }
         }
 
-    @Test
-    fun transitionFromDozingToGlanceableHub_forcesCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Blank)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.DOZING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    testScope = this
-                )
-
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
     private fun TestScope.updateDocked(docked: Boolean) =
         with(kosmos) {
             runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 0792a50..612f2e7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -34,14 +34,12 @@
 
 import android.os.PowerManager
 import android.platform.test.annotations.EnableFlags
-import android.service.dream.dreamManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
-import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -66,10 +64,8 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
-import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -124,66 +120,6 @@
 
     @Test
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToLockscreen_onWakeup_canDream_glanceableHubAvailable() =
-        testScope.runTest {
-            whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
-            kosmos.setCommunalAvailable(true)
-            runCurrent()
-
-            powerInteractor.setAwakeForTest()
-            runCurrent()
-
-            // If dreaming is possible and communal is available, then we should transition to
-            // GLANCEABLE_HUB when waking up.
-            assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.DOZING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                )
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToLockscreen_onWakeup_canNotDream_glanceableHubAvailable() =
-        testScope.runTest {
-            whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(false)
-            kosmos.setCommunalAvailable(true)
-            runCurrent()
-
-            powerInteractor.setAwakeForTest()
-            runCurrent()
-
-            // If dreaming is NOT possible but communal is available, then we should transition to
-            // LOCKSCREEN when waking up.
-            assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.DOZING,
-                    to = KeyguardState.LOCKSCREEN,
-                )
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToLockscreen_onWakeup_canNDream_glanceableHubNotAvailable() =
-        testScope.runTest {
-            whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
-            kosmos.setCommunalAvailable(false)
-            runCurrent()
-
-            powerInteractor.setAwakeForTest()
-            runCurrent()
-
-            // If dreaming is possible but communal is NOT available, then we should transition to
-            // LOCKSCREEN when waking up.
-            assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.DOZING,
-                    to = KeyguardState.LOCKSCREEN,
-                )
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
         testScope.runTest {
             kosmos.fakeCommunalSceneRepository.setTransitionState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
index 9ce2e0f..c33e2a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -92,7 +92,8 @@
             runCurrent()
 
             assertThat(states()).isNotEmpty()
-            assertThat(states().first().label).isEqualTo(testTileData)
+            assertThat(states().last()).isNotNull()
+            assertThat(states().last()!!.label).isEqualTo(testTileData)
             verify(qsTileLogger).logInitialRequest(eq(tileConfig.tileSpec))
         }
 
@@ -196,6 +197,7 @@
             qsTileLogger,
             FakeSystemClock(),
             testCoroutineDispatcher,
+            testCoroutineDispatcher,
             scope.backgroundScope,
         )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
index 6066d24..7955f2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
@@ -256,6 +256,7 @@
             qsTileLogger,
             FakeSystemClock(),
             testCoroutineDispatcher,
+            testCoroutineDispatcher,
             scope.backgroundScope,
         )
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index cb4d96f..39b3662 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -133,7 +133,6 @@
                 sceneInteractor = sceneInteractor,
                 falsingInteractor = kosmos.falsingInteractor,
                 powerInteractor = kosmos.powerInteractor,
-                scenes = kosmos.scenes,
             )
             .apply { setTransitionState(transitionState) }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 5c30379..ea95aab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -28,10 +28,8 @@
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.fakeScenes
 import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.sceneKeys
-import com.android.systemui.scene.scenes
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.testKosmos
@@ -66,7 +64,6 @@
                 sceneInteractor = sceneInteractor,
                 falsingInteractor = kosmos.falsingInteractor,
                 powerInteractor = kosmos.powerInteractor,
-                scenes = kosmos.scenes,
             )
     }
 
@@ -217,23 +214,4 @@
 
             assertThat(isVisible).isFalse()
         }
-
-    @Test
-    fun currentDestinationScenes_onlyTheCurrentSceneIsCollected() =
-        testScope.runTest {
-            val unused by collectLastValue(underTest.currentDestinationScenes(backgroundScope))
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            kosmos.fakeScenes.forEach { scene ->
-                fakeSceneDataSource.changeScene(toScene = scene.key)
-                runCurrent()
-                assertThat(currentScene).isEqualTo(scene.key)
-
-                assertThat(scene.isDestinationScenesBeingCollected).isTrue()
-                kosmos.fakeScenes
-                    .filter { it.key != scene.key }
-                    .forEach { otherScene ->
-                        assertThat(otherScene.isDestinationScenesBeingCollected).isFalse()
-                    }
-            }
-        }
 }
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 177ba598..212dae2 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -233,6 +233,7 @@
     <item type="id" name="smart_space_barrier_bottom" />
     <item type="id" name="small_clock_guideline_top" />
     <item type="id" name="weather_clock_date_and_icons_barrier_bottom" />
+    <item type="id" name="weather_clock_bc_smartspace_bottom" />
     <item type="id" name="accessibility_actions_view" />
 
     <!-- Privacy dialog -->
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4b07402..56de5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -38,7 +38,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ad142a8..9d3c6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -558,7 +558,12 @@
                 Log.w(TAG, "onTouch down received without a preceding up");
             }
             mActivePointerId = MotionEvent.INVALID_POINTER_ID;
-            mOnFingerDown = false;
+
+            // It's possible on some devices to get duplicate touches from both doze and the
+            // normal touch listener. Don't reset the down in this case to avoid duplicate downs
+            if (!mIsAodInterruptActive) {
+                mOnFingerDown = false;
+            }
         } else if (!DeviceEntryUdfpsRefactor.isEnabled()) {
             if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
                     && !mAlternateBouncerInteractor.isVisibleState())
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index e31f1ad..88c3f9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -18,7 +18,6 @@
 
 import android.provider.Settings
 import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -92,8 +91,8 @@
         keyguardTransitionInteractor.startedKeyguardTransitionStep
             .mapLatest(::determineSceneAfterTransition)
             .filterNotNull()
-            .onEach { (nextScene, nextTransition) ->
-                communalSceneInteractor.changeScene(nextScene, nextTransition)
+            .onEach { nextScene ->
+                communalSceneInteractor.changeScene(nextScene, CommunalTransitionKeys.SimpleFade)
             }
             .launchIn(applicationScope)
 
@@ -189,7 +188,7 @@
 
     private suspend fun determineSceneAfterTransition(
         lastStartedTransition: TransitionStep,
-    ): Pair<SceneKey, TransitionKey>? {
+    ): SceneKey? {
         val to = lastStartedTransition.to
         val from = lastStartedTransition.from
         val docked = dockManager.isDocked
@@ -202,27 +201,22 @@
                 // underneath the hub is shown. When launching activities over lockscreen, we only
                 // change scenes once the activity launch animation is finished, so avoid
                 // changing the scene here.
-                Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
+                CommunalScenes.Blank
             }
             to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> {
                 // When transitioning to the hub from an occluded state, fade out the hub without
                 // doing any translation.
-                Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
+                CommunalScenes.Communal
             }
             // Transitioning to Blank scene when entering the edit mode will be handled separately
             // with custom animations.
             to == KeyguardState.GONE && !communalInteractor.editModeOpen.value ->
-                Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
+                CommunalScenes.Blank
             !docked && !KeyguardState.deviceIsAwakeInState(to) -> {
                 // If the user taps the screen and wakes the device within this timeout, we don't
                 // want to dismiss the hub
                 delay(AWAKE_DEBOUNCE_DELAY)
-                Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
-            }
-            from == KeyguardState.DOZING && to == KeyguardState.GLANCEABLE_HUB -> {
-                // Make sure the communal hub is showing (immediately, not fading in) when
-                // transitioning from dozing to hub.
-                Pair(CommunalScenes.Communal, CommunalTransitionKeys.Immediately)
+                CommunalScenes.Blank
             }
             else -> null
         }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 1404340..af7ecf6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.ComposeLockscreen
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
@@ -62,6 +63,9 @@
 
         // CommunalHub dependencies
         communalHub dependsOn MigrateClocksToBlueprint.token
+
+        // DualShade dependencies
+        DualShade.token dependsOn SceneContainerFlag.getMainAconfigFlag()
     }
 
     private inline val politeNotifications
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index c1de381..1e4fb4f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -51,14 +51,10 @@
 import android.database.ContentObserver;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.Flags;
 import android.media.AudioManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -861,29 +857,6 @@
             if (ActivityManager.isUserAMonkey()) {
                 return;
             }
-            if (Flags.mandatoryBiometrics()
-                    && requestBiometricAuthenticationForMandatoryBiometrics()) {
-                launchBiometricPromptForMandatoryBiometrics(
-                        new BiometricPrompt.AuthenticationCallback() {
-                            @Override
-                            public void onAuthenticationError(int errorCode,
-                                    CharSequence errString) {
-                                super.onAuthenticationError(errorCode, errString);
-                            }
-
-                            @Override
-                            public void onAuthenticationSucceeded(
-                                    BiometricPrompt.AuthenticationResult result) {
-                                super.onAuthenticationSucceeded(result);
-                                shutDown();
-                            }
-                        });
-            } else {
-                shutDown();
-            }
-        }
-
-        private void shutDown() {
             mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
             // shutdown by making sure radio and power are handled accordingly.
             mWindowManagerFuncs.shutdown();
@@ -2288,35 +2261,6 @@
     }
 
     @VisibleForTesting
-    void launchBiometricPromptForMandatoryBiometrics(
-            BiometricPrompt.AuthenticationCallback authenticationCallback) {
-        final CancellationSignal cancellationSignal = new CancellationSignal();
-        final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext)
-                .setAllowedAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)
-                .setUseDefaultTitle()
-                .setDescription(mContext.getString(
-                        R.string.identity_check_biometric_prompt_description))
-                .setNegativeButton(mContext.getString(R.string.cancel), mContext.getMainExecutor(),
-                        (dialog, which) -> cancellationSignal.cancel())
-                .setAllowBackgroundAuthentication(true)
-                .build();
-        biometricPrompt.authenticate(cancellationSignal, mContext.getMainExecutor(),
-                authenticationCallback);
-    }
-
-    private boolean requestBiometricAuthenticationForMandatoryBiometrics() {
-        final BiometricManager biometricManager =
-                (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
-        if (biometricManager == null) {
-            Log.e(TAG, "Biometric Manager is null.");
-            return false;
-        }
-        final int status = biometricManager.canAuthenticate(
-                BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
-        return status == BiometricManager.BIOMETRIC_SUCCESS;
-    }
-
-    @VisibleForTesting
     static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
             ColorExtractor.OnColorsChangedListener {
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 73347ad..1ea5d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -42,6 +42,7 @@
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
 import static com.android.systemui.Flags.refactorGetCurrentUser;
+import static com.android.systemui.Flags.translucentOccludingActivityFix;
 import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
 
 import android.animation.Animator;
@@ -1036,6 +1037,17 @@
                                 (int) (fullWidth - initialWidth) /* left */,
                                 fullWidth /* right */,
                                 mWindowCornerRadius, mWindowCornerRadius);
+                    } else if (translucentOccludingActivityFix()
+                            && mOccludingRemoteAnimationTarget != null
+                            && mOccludingRemoteAnimationTarget.isTranslucent) {
+                        // Animating in a transparent window looks really weird. Just let it be
+                        // fullscreen and the app can do an internal animation if it wants to.
+                        return new TransitionAnimator.State(
+                                0,
+                                fullHeight,
+                                0,
+                                fullWidth,
+                                0f, 0f);
                     } else {
                         final float initialHeight = fullHeight / 2f;
                         final float initialWidth = fullWidth / 2f;
@@ -1399,6 +1411,11 @@
     private final Lazy<DreamViewModel> mDreamViewModel;
     private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModel;
     private RemoteAnimationTarget mRemoteAnimationTarget;
+
+    /**
+     * The most recent RemoteAnimationTarget provided for an occluding activity animation.
+     */
+    private RemoteAnimationTarget mOccludingRemoteAnimationTarget;
     private boolean mShowCommunalWhenUnoccluding = false;
 
     private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager;
@@ -3941,6 +3958,13 @@
         public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+            // Save mRemoteAnimationTarget for reference in the animation controller. Needs to be
+            // called prior to super.onAnimationStart() since that's the call that eventually asks
+            // the animation controller to configure the animation state.
+            if (apps.length > 0) {
+                mOccludingRemoteAnimationTarget = apps[0];
+            }
+
             super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
 
             mInteractionJankMonitor.begin(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 506a18d..b423ed9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -17,10 +17,8 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.animation.ValueAnimator
-import android.app.DreamManager
 import com.android.app.animation.Interpolators
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -53,10 +51,8 @@
     keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     private val communalInteractor: CommunalInteractor,
-    private val communalSceneInteractor: CommunalSceneInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
     val deviceEntryRepository: DeviceEntryRepository,
-    private val dreamManager: DreamManager,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.DOZING,
@@ -123,8 +119,7 @@
                 .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
                 .sample(
                     keyguardInteractor.isKeyguardOccluded,
-                    communalInteractor.isCommunalAvailable,
-                    communalSceneInteractor.isIdleOnCommunal,
+                    communalInteractor.isIdleOnCommunal,
                     canTransitionToGoneOnWake,
                     keyguardInteractor.primaryBouncerShowing,
                 )
@@ -132,7 +127,6 @@
                     (
                         _,
                         occluded,
-                        isCommunalAvailable,
                         isIdleOnCommunal,
                         canTransitionToGoneOnWake,
                         primaryBouncerShowing) ->
@@ -147,10 +141,6 @@
                             KeyguardState.OCCLUDED
                         } else if (isIdleOnCommunal) {
                             KeyguardState.GLANCEABLE_HUB
-                        } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
-                            // This case handles tapping the power button to transition through
-                            // dream -> off -> hub.
-                            KeyguardState.GLANCEABLE_HUB
                         } else {
                             KeyguardState.LOCKSCREEN
                         }
@@ -169,8 +159,7 @@
             powerInteractor.detailedWakefulness
                 .filterRelevantKeyguardStateAnd { it.isAwake() }
                 .sample(
-                    communalInteractor.isCommunalAvailable,
-                    communalSceneInteractor.isIdleOnCommunal,
+                    communalInteractor.isIdleOnCommunal,
                     keyguardInteractor.biometricUnlockState,
                     canTransitionToGoneOnWake,
                     keyguardInteractor.primaryBouncerShowing,
@@ -178,7 +167,6 @@
                 .collect {
                     (
                         _,
-                        isCommunalAvailable,
                         isIdleOnCommunal,
                         biometricUnlockState,
                         canDismissLockscreen,
@@ -200,10 +188,6 @@
                                 KeyguardState.PRIMARY_BOUNCER
                             } else if (isIdleOnCommunal) {
                                 KeyguardState.GLANCEABLE_HUB
-                            } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
-                                // This case handles tapping the power button to transition through
-                                // dream -> off -> hub.
-                                KeyguardState.GLANCEABLE_HUB
                             } else {
                                 KeyguardState.LOCKSCREEN
                             },
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 4f75e6f..22ab82b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -249,7 +249,7 @@
     val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
 
     /** Keyguard can be clipped at the top as the shade is dragged */
-    val topClippingBounds: Flow<Int?> =
+    val topClippingBounds: Flow<Int?> by lazy {
         combineTransform(
                 keyguardTransitionInteractor
                     .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
@@ -263,6 +263,7 @@
                 }
             }
             .distinctUntilChanged()
+    }
 
     /** Last point that [KeyguardRootView] view was tapped */
     val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index f2821a0..e063380 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -37,6 +37,10 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.clocks.AodClockBurnInModel
 import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.util.ui.value
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
 object KeyguardClockViewBinder {
@@ -99,15 +103,20 @@
 
                 launch {
                     if (!MigrateClocksToBlueprint.isEnabled) return@launch
-                    viewModel.isAodIconsVisible.collect {
-                        viewModel.currentClock.value?.let {
-                            if (
-                                viewModel.isLargeClockVisible.value && it.config.useCustomClockScene
-                            ) {
-                                blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
+                    combine(
+                            viewModel.hasAodIcons,
+                            rootViewModel.isNotifIconContainerVisible.map { it.value }
+                        ) { hasIcon, isVisible ->
+                            hasIcon && isVisible
+                        }
+                        .distinctUntilChanged()
+                        .collect { _ ->
+                            viewModel.currentClock.value?.let {
+                                if (it.config.useCustomClockScene) {
+                                    blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
+                                }
                             }
                         }
-                    }
                 }
 
                 launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 34a1da5..0637bba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.plugins.clocks.ClockFaceLayout
 import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
+import com.android.systemui.util.ui.value
 import dagger.Lazy
 import javax.inject.Inject
 
@@ -70,6 +71,7 @@
     private val rootViewModel: KeyguardRootViewModel,
 ) : KeyguardSection() {
     override fun addViews(constraintLayout: ConstraintLayout) {}
+
     override fun bindData(constraintLayout: ConstraintLayout) {
         if (!MigrateClocksToBlueprint.isEnabled) {
             return
@@ -121,35 +123,39 @@
     private fun getTargetClockFace(clock: ClockController): ClockFaceLayout =
         if (keyguardClockViewModel.isLargeClockVisible.value) clock.largeClock.layout
         else clock.smallClock.layout
+
     private fun getNonTargetClockFace(clock: ClockController): ClockFaceLayout =
         if (keyguardClockViewModel.isLargeClockVisible.value) clock.smallClock.layout
         else clock.largeClock.layout
 
     fun constrainWeatherClockDateIconsBarrier(constraints: ConstraintSet) {
         constraints.apply {
-            if (keyguardClockViewModel.isAodIconsVisible.value) {
+            createBarrier(
+                R.id.weather_clock_bc_smartspace_bottom,
+                Barrier.BOTTOM,
+                getDimen(ENHANCED_SMARTSPACE_HEIGHT),
+                (custR.id.weather_clock_time)
+            )
+            if (
+                rootViewModel.isNotifIconContainerVisible.value.value &&
+                    keyguardClockViewModel.hasAodIcons.value
+            ) {
                 createBarrier(
                     R.id.weather_clock_date_and_icons_barrier_bottom,
                     Barrier.BOTTOM,
                     0,
-                    *intArrayOf(sharedR.id.bc_smartspace_view, R.id.aod_notification_icon_container)
+                    *intArrayOf(
+                        R.id.aod_notification_icon_container,
+                        R.id.weather_clock_bc_smartspace_bottom
+                    )
                 )
             } else {
-                if (smartspaceViewModel.bcSmartspaceVisibility.value == VISIBLE) {
-                    createBarrier(
-                        R.id.weather_clock_date_and_icons_barrier_bottom,
-                        Barrier.BOTTOM,
-                        0,
-                        (sharedR.id.bc_smartspace_view)
-                    )
-                } else {
-                    createBarrier(
-                        R.id.weather_clock_date_and_icons_barrier_bottom,
-                        Barrier.BOTTOM,
-                        getDimen(ENHANCED_SMARTSPACE_HEIGHT),
-                        (R.id.lockscreen_clock_view)
-                    )
-                }
+                createBarrier(
+                    R.id.weather_clock_date_and_icons_barrier_bottom,
+                    Barrier.BOTTOM,
+                    0,
+                    *intArrayOf(R.id.weather_clock_bc_smartspace_bottom)
+                )
             }
         }
     }
@@ -198,6 +204,7 @@
     companion object {
         private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
         private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
+
         fun getDimen(context: Context, name: String): Int {
             val res = context.packageManager.getResourcesForApplication(context.packageName)
             val id = res.getIdentifier(name, "dimen", context.packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 4128c52..5cf100e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -34,8 +34,8 @@
     alternateBouncerInteractor: AlternateBouncerInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
-    val canShowAlternateBouncer: Flow<Boolean> = alternateBouncerInteractor.canShowAlternateBouncer
-
+    private val deviceSupportsAlternateBouncer: Flow<Boolean> =
+        alternateBouncerInteractor.alternateBouncerSupported
     private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
         keyguardTransitionInteractor
             .transitionValue(KeyguardState.ALTERNATE_BOUNCER)
@@ -43,8 +43,8 @@
             .distinctUntilChanged()
 
     val alternateBouncerWindowRequired: Flow<Boolean> =
-        canShowAlternateBouncer.flatMapLatest { canShowAlternateBouncer ->
-            if (canShowAlternateBouncer) {
+        deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer ->
+            if (deviceSupportsAlternateBouncer) {
                 isTransitioningToOrFromOrShowingAlternateBouncer
             } else {
                 flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index f5c521a..573b75e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.ui.SystemBarUtilsProxy
 import javax.inject.Inject
@@ -50,7 +49,6 @@
     keyguardClockInteractor: KeyguardClockInteractor,
     @Application private val applicationScope: CoroutineScope,
     aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
-    notifsKeyguardInteractor: NotificationsKeyguardInteractor,
     @get:VisibleForTesting val shadeInteractor: ShadeInteractor,
     private val systemBarUtils: SystemBarUtilsProxy,
     configurationInteractor: ConfigurationInteractor,
@@ -90,14 +88,13 @@
                 currentClock?.let { clock ->
                     val face = if (isLargeClock) clock.largeClock else clock.smallClock
                     face.config.hasCustomWeatherDataDisplay
-                }
-                    ?: false
+                } ?: false
             }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
-                initialValue = currentClock.value?.largeClock?.config?.hasCustomWeatherDataDisplay
-                        ?: false
+                initialValue =
+                    currentClock.value?.largeClock?.config?.hasCustomWeatherDataDisplay ?: false
             )
 
     val clockShouldBeCentered: StateFlow<Boolean> =
@@ -109,15 +106,14 @@
 
     // To translate elements below smartspace in weather clock to avoid overlapping between date
     // element in weather clock and aod icons
-    val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map {
-        it.visibleIcons.isNotEmpty()
-    }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible ->
-        hasIcons && visible
-    }.stateIn(
-            scope = applicationScope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = false
-        )
+    val hasAodIcons: StateFlow<Boolean> =
+        aodNotificationIconViewModel.icons
+            .map { it.visibleIcons.isNotEmpty() }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false
+            )
 
     val currentClockLayout: StateFlow<ClockLayout> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index e7c2a45..bda0069 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -17,6 +17,7 @@
 package com.android.systemui.media;
 
 import android.annotation.Nullable;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -123,9 +124,13 @@
                 boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig)
                 throws RemoteException {
             if (LOGD) {
-                Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
-                        + Binder.getCallingUid() + ")");
+                Log.d(TAG, "play(token=" + token + ", uri=" + uri
+                        + ", uid=" + Binder.getCallingUid()
+                        + ") uriUserId=" + ContentProvider.getUserIdFromUri(uri)
+                        + " callingUserId=" + Binder.getCallingUserHandle().getIdentifier());
             }
+            enforceUriUserId(uri);
+
             Client client;
             synchronized (mClients) {
                 client = mClients.get(token);
@@ -207,6 +212,7 @@
 
         @Override
         public String getTitle(Uri uri) {
+            enforceUriUserId(uri);
             final UserHandle user = Binder.getCallingUserHandle();
             return Ringtone.getTitle(getContextForUser(user), uri,
                     false /*followSettingsUri*/, false /*allowRemote*/);
@@ -239,6 +245,25 @@
             }
             throw new SecurityException("Uri is not ringtone, alarm, or notification: " + uri);
         }
+
+        /**
+         * Must be called from the Binder calling thread.
+         * Ensures caller is from the same userId as the content they're trying to access.
+         * @param uri the URI to check
+         * @throws SecurityException when non-system call or userId in uri differs from the
+         *                           caller's userId
+         */
+        private void enforceUriUserId(Uri uri) throws SecurityException {
+            final int uriUserId = ContentProvider.getUserIdFromUri(uri);
+            final int callerUserId = Binder.getCallingUserHandle().getIdentifier();
+            // for a non-system call, verify the URI to play belongs to the same user as the caller
+            if (UserHandle.isApp(Binder.getCallingUid()) && uriUserId != callerUserId) {
+                throw new SecurityException("Illegal access to uri=" + uri
+                        + " content associated with user=" + uriUserId
+                        + ", request originates from user=" + callerUserId);
+            }
+        }
+
     };
 
     private Context getContextForUser(UserHandle user) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
index 4c21080..8c75cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.base.viewmodel
 
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.UiBackground
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
@@ -61,6 +62,7 @@
         private val qsTileConfigProvider: QSTileConfigProvider,
         private val systemClock: SystemClock,
         @Background private val backgroundDispatcher: CoroutineDispatcher,
+        @UiBackground private val uiBackgroundDispatcher: CoroutineDispatcher,
         private val customTileComponentBuilder: CustomTileComponent.Builder,
     ) : QSTileViewModelFactory<CustomTileDataModel> {
 
@@ -86,6 +88,7 @@
                 qsTileLogger,
                 systemClock,
                 backgroundDispatcher,
+                uiBackgroundDispatcher,
                 component.coroutineScope(),
             )
         }
@@ -106,6 +109,7 @@
         private val qsTileConfigProvider: QSTileConfigProvider,
         private val systemClock: SystemClock,
         @Background private val backgroundDispatcher: CoroutineDispatcher,
+        @UiBackground private val uiBackgroundDispatcher: CoroutineDispatcher,
         private val coroutineScopeFactory: QSTileCoroutineScopeFactory,
     ) : QSTileViewModelFactory<T> {
 
@@ -136,6 +140,7 @@
                 qsTileLogger,
                 systemClock,
                 backgroundDispatcher,
+                uiBackgroundDispatcher,
                 coroutineScopeFactory.create(),
             )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 8782524..9e84f01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -40,6 +40,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancel
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -59,7 +60,9 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformLatest
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /**
  * Provides a hassle-free way to implement new tiles according to current System UI architecture
@@ -81,6 +84,7 @@
     private val qsTileLogger: QSTileLogger,
     private val systemClock: SystemClock,
     private val backgroundDispatcher: CoroutineDispatcher,
+    uiBackgroundDispatcher: CoroutineDispatcher,
     private val tileScope: CoroutineScope,
 ) : QSTileViewModel, Dumpable {
 
@@ -93,18 +97,16 @@
 
     private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow()
 
-    override val state: SharedFlow<QSTileState> =
+    override val state: StateFlow<QSTileState?> =
         tileData
             .map { data ->
-                mapper().map(config, data).also { state ->
-                    qsTileLogger.logStateUpdate(spec, state, data)
-                }
+                withContext(uiBackgroundDispatcher) { mapper().map(config, data) }
+                    .also { state -> qsTileLogger.logStateUpdate(spec, state, data) }
             }
-            .flowOn(backgroundDispatcher)
-            .shareIn(
+            .stateIn(
                 tileScope,
                 SharingStarted.WhileSubscribed(),
-                replay = 1,
+                null,
             )
     override val isAvailable: StateFlow<Boolean> =
         users
@@ -147,26 +149,26 @@
 
     private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
         users
-            .flatMapLatest { user ->
-                val updateTriggers =
-                    merge(
-                            userInputFlow(user),
-                            forceUpdates
-                                .map { DataUpdateTrigger.ForceUpdate }
-                                .onEach { qsTileLogger.logForceUpdate(spec) },
-                        )
-                        .onStart {
-                            emit(DataUpdateTrigger.InitialRequest)
-                            qsTileLogger.logInitialRequest(spec)
-                        }
-                        .shareIn(tileScope, SharingStarted.WhileSubscribed())
-                tileDataInteractor()
-                    .tileData(user, updateTriggers)
-                    // combine makes sure updateTriggers is always listened even if
-                    // tileDataInteractor#tileData doesn't flatMapLatest on it
-                    .combine(updateTriggers) { data, _ -> data }
-                    .cancellable()
-                    .flowOn(backgroundDispatcher)
+            .transformLatest { user ->
+                coroutineScope {
+                    val updateTriggers: Flow<DataUpdateTrigger> =
+                        merge(
+                                userInputFlow(user),
+                                forceUpdates
+                                    .map { DataUpdateTrigger.ForceUpdate }
+                                    .onEach { qsTileLogger.logForceUpdate(spec) },
+                            )
+                            .onStart { qsTileLogger.logInitialRequest(spec) }
+                            .stateIn(this, SharingStarted.Eagerly, DataUpdateTrigger.InitialRequest)
+                    tileDataInteractor()
+                        .tileData(user, updateTriggers)
+                        // combine makes sure updateTriggers is always listened even if
+                        // tileDataInteractor#tileData doesn't transformLatest on it
+                        .combine(updateTriggers) { data, _ -> data }
+                        .cancellable()
+                        .flowOn(backgroundDispatcher)
+                        .collect { emit(it) }
+                }
             }
             .distinctUntilChanged()
             .shareIn(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
index 1e8ce58..233e913 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
@@ -30,11 +30,9 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
 
 /** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */
 class QRCodeScannerTileDataInteractor
@@ -66,11 +64,6 @@
             }
             .onStart { emit(generateModel()) }
             .flowOn(bgCoroutineContext)
-            .stateIn(
-                scope,
-                SharingStarted.WhileSubscribed(),
-                QRCodeScannerTileModel.TemporarilyUnavailable
-            )
 
     override fun availability(user: UserHandle): Flow<Boolean> =
         flowOf(qrController.isCameraAvailable)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index ae6c014..30247c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -54,18 +54,21 @@
             resources: Resources,
             theme: Theme,
             config: QSTileUIConfig,
-            build: Builder.() -> Unit
+            builder: Builder.() -> Unit
         ): QSTileState {
             val iconDrawable = resources.getDrawable(config.iconRes, theme)
             return build(
                 { Icon.Loaded(iconDrawable, null) },
                 resources.getString(config.labelRes),
-                build,
+                builder,
             )
         }
 
-        fun build(icon: () -> Icon?, label: CharSequence, build: Builder.() -> Unit): QSTileState =
-            Builder(icon, label).apply(build).build()
+        fun build(
+            icon: () -> Icon?,
+            label: CharSequence,
+            builder: Builder.() -> Unit
+        ): QSTileState = Builder(icon, label).apply { builder() }.build()
     }
 
     enum class ActivationState(val legacyState: Int) {
@@ -107,7 +110,9 @@
 
     sealed interface SideViewIcon {
         data class Custom(val icon: Icon) : SideViewIcon
+
         data object Chevron : SideViewIcon
+
         data object None : SideViewIcon
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index 226e2fa0..b1b0001 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles.viewmodel
 
 import android.os.UserHandle
-import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
 
 /**
@@ -31,7 +30,7 @@
 interface QSTileViewModel {
 
     /** State of the tile to be shown by the view. */
-    val state: SharedFlow<QSTileState>
+    val state: StateFlow<QSTileState?>
 
     val config: QSTileConfig
 
@@ -62,9 +61,7 @@
 }
 
 /**
- * Returns the immediate state of the tile or null if the state haven't been collected yet. Favor
- * reactive consumption over the [currentState], because there is no guarantee that current value
- * would be available at any time.
+ * Returns the immediate state of the tile or null if the state haven't been collected yet.
  */
 val QSTileViewModel.currentState: QSTileState?
-    get() = state.replayCache.lastOrNull()
+    get() = state.value
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 7be13e0..ba0a8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -38,6 +38,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collectIndexed
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
@@ -168,6 +169,7 @@
                 if (listeningClients.size == 1) {
                     stateJob =
                         qsTileViewModel.state
+                            .filterNotNull()
                             .map { mapState(context, it, qsTileViewModel.config) }
                             .onEach { legacyState ->
                                 synchronized(callbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
index 64f1fd3..00b7e61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
@@ -17,12 +17,11 @@
 package com.android.systemui.qs.tiles.viewmodel
 
 import android.os.UserHandle
-import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
 
 object StubQSTileViewModel : QSTileViewModel {
 
-    override val state: SharedFlow<QSTileState>
+    override val state: StateFlow<QSTileState?>
         get() = error("Don't call stubs")
 
     override val config: QSTileConfig
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index d380251..a28222e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -26,17 +26,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state for the scene container. */
 @SysUISingleton
@@ -46,7 +41,6 @@
     private val sceneInteractor: SceneInteractor,
     private val falsingInteractor: FalsingInteractor,
     private val powerInteractor: PowerInteractor,
-    scenes: Set<@JvmSuppressWildcards Scene>,
 ) {
     /**
      * Keys of all scenes in the container.
@@ -62,25 +56,6 @@
     /** Whether the container is visible. */
     val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
 
-    private val destinationScenesBySceneKey =
-        scenes.associate { scene ->
-            scene.key to scene.destinationScenes.flatMapLatestConflated { replaceSceneFamilies(it) }
-        }
-
-    fun currentDestinationScenes(
-        scope: CoroutineScope,
-    ): StateFlow<Map<UserAction, UserActionResult>> {
-        return currentScene
-            .flatMapLatestConflated { currentSceneKey ->
-                checkNotNull(destinationScenesBySceneKey[currentSceneKey])
-            }
-            .stateIn(
-                scope = scope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = emptyMap(),
-            )
-    }
-
     /**
      * Binds the given flow so the system remembers it.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 48c89f8..5e91786 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -32,6 +32,7 @@
 import android.content.pm.PackageManager.PERMISSION_GRANTED
 import android.database.ContentObserver
 import android.hardware.display.AmbientDisplayConfiguration
+import android.os.Bundle
 import android.os.Handler
 import android.os.PowerManager
 import android.os.SystemProperties
@@ -368,6 +369,11 @@
             PendingIntent.FLAG_IMMUTABLE
         )
 
+        // Replace "System UI" app name with "Android System"
+        val bundle = Bundle()
+        bundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                context.getString(com.android.internal.R.string.android_system_label))
+
         val builder =
             Notification.Builder(context, NotificationChannels.ALERTS)
                 .setTicker(titleStr)
@@ -375,9 +381,11 @@
                 .setContentText(textStr)
                 .setSmallIcon(com.android.systemui.res.R.drawable.ic_settings)
                 .setCategory(Notification.CATEGORY_SYSTEM)
+                .setTimeoutAfter(/* one day in ms */ 24 * 60 * 60 * 1000L)
                 .setAutoCancel(true)
                 .addAction(android.R.drawable.button_onoff_indicator_off, actionStr, pendingIntent)
                 .setContentIntent(pendingIntent)
+                .addExtras(bundle)
 
         notificationManager.notify(SystemMessage.NOTE_ADAPTIVE_NOTIFICATIONS, builder.build())
         hasSeenEdu = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 3d8090d..aced0be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.phone.fragment;
 
+import static com.android.systemui.statusbar.phone.fragment.StatusBarVisibilityModel.createHiddenModel;
+
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.Fragment;
@@ -46,6 +48,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
@@ -205,6 +208,13 @@
     private boolean mTransitionFromLockscreenToDreamStarted = false;
 
     /**
+     * True if the current scene allows the home status bar (aka this status bar) to be shown, and
+     * false if the current scene should never show the home status bar. Only used if the scene
+     * container is enabled.
+     */
+    private boolean mHomeStatusBarAllowedByScene = true;
+
+    /**
      * True if there's an active ongoing activity that should be showing a chip and false otherwise.
      */
     private boolean mHasOngoingActivity;
@@ -522,6 +532,13 @@
                     mHasOngoingActivity = hasOngoingActivity;
                     updateStatusBarVisibilities(/* animate= */ true);
                 }
+
+                @Override
+                public void onIsHomeStatusBarAllowedBySceneChanged(
+                        boolean isHomeStatusBarAllowedByScene) {
+                    mHomeStatusBarAllowedByScene = isHomeStatusBarAllowedByScene;
+                    updateStatusBarVisibilities(/* animate= */ true);
+                }
     };
 
     @Override
@@ -580,17 +597,22 @@
         boolean headsUpVisible =
                 mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
 
-        if (!mKeyguardStateController.isLaunchTransitionFadingAway()
-                && !mKeyguardStateController.isKeyguardFadingAway()
-                && shouldHideStatusBar()
-                && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
-                        && headsUpVisible)) {
-            // Hide everything
-            return new StatusBarVisibilityModel(
-                    /* showClock= */ false,
-                    /* showNotificationIcons= */ false,
-                    /* showOngoingActivityChip= */ false,
-                    /* showSystemInfo= */ false);
+        if (SceneContainerFlag.isEnabled()) {
+            // With the scene container, only use the value calculated by the view model to
+            // determine if the status bar needs hiding.
+            if (!mHomeStatusBarAllowedByScene) {
+                return createHiddenModel();
+            }
+        } else {
+            // Without the scene container, use our old, mildly-hacky logic to determine if the
+            // status bar needs hiding.
+            if (!mKeyguardStateController.isLaunchTransitionFadingAway()
+                    && !mKeyguardStateController.isKeyguardFadingAway()
+                    && shouldHideStatusBar()
+                    && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+                    && headsUpVisible)) {
+                return createHiddenModel();
+            }
         }
 
         boolean showClock = externalModel.getShowClock() && !headsUpVisible;
@@ -698,7 +720,7 @@
     }
 
     private void hideEndSideContent(boolean animate) {
-        if (!animate) {
+        if (!animate || !mAnimationsEnabled) {
             mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER);
         } else {
             mEndSideAlphaController.animateToAlpha(/*alpha*/ 0f, SOURCE_OTHER, FADE_OUT_DURATION,
@@ -707,7 +729,7 @@
     }
 
     private void showEndSideContent(boolean animate) {
-        if (!animate) {
+        if (!animate || !mAnimationsEnabled) {
             mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER);
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
index fe24fae..9255e63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
@@ -36,6 +36,17 @@
             return createModelFromFlags(DISABLE_NONE, DISABLE2_NONE)
         }
 
+        /** Creates a model that hides every piece of the status bar. */
+        @JvmStatic
+        fun createHiddenModel(): StatusBarVisibilityModel {
+            return StatusBarVisibilityModel(
+                showClock = false,
+                showNotificationIcons = false,
+                showOngoingActivityChip = false,
+                showSystemInfo = false,
+            )
+        }
+
         /**
          * Given a set of disabled flags, converts them into the correct visibility statuses.
          *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 9449659..62bd8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -349,6 +349,7 @@
                     false
                 }
             }
+            .flowOn(bgDispatcher)
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val carrierId =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index de36e40..ae1898b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -136,6 +137,14 @@
                         }
                     }
                 }
+
+                if (SceneContainerFlag.isEnabled) {
+                    launch {
+                        viewModel.isHomeStatusBarAllowedByScene.collect {
+                            listener.onIsHomeStatusBarAllowedBySceneChanged(it)
+                        }
+                    }
+                }
             }
         }
     }
@@ -259,4 +268,10 @@
 
     /** Called when the status of the ongoing activity chip (active or not active) has changed. */
     fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean)
+
+    /**
+     * Called when the scene state has changed such that the home status bar is newly allowed or no
+     * longer allowed. See [CollapsedStatusBarViewModel.isHomeStatusBarAllowedByScene].
+     */
+    fun onIsHomeStatusBarAllowedBySceneChanged(isHomeStatusBarAllowedByScene: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index 95d9248..d6c3834 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -24,6 +24,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -65,6 +68,12 @@
     val ongoingActivityChip: StateFlow<OngoingActivityChipModel>
 
     /**
+     * True if the current scene can show the home status bar (aka this status bar), and false if
+     * the current scene should never show the home status bar.
+     */
+    val isHomeStatusBarAllowedByScene: StateFlow<Boolean>
+
+    /**
      * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
      * status bar and navigation icons dim. In this mode, a notification dot appears where the
      * notification icons would appear if they would be shown outside of this mode.
@@ -83,6 +92,8 @@
     private val lightsOutInteractor: LightsOutInteractor,
     private val notificationsInteractor: ActiveNotificationsInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    sceneInteractor: SceneInteractor,
+    sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
     ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
     @Application coroutineScope: CoroutineScope,
 ) : CollapsedStatusBarViewModel {
@@ -99,6 +110,20 @@
 
     override val ongoingActivityChip = ongoingActivityChipsViewModel.chip
 
+    override val isHomeStatusBarAllowedByScene: StateFlow<Boolean> =
+        combine(
+                sceneInteractor.currentScene,
+                sceneContainerOcclusionInteractor.invisibleDueToOcclusion,
+            ) { currentScene, isOccluded ->
+                // All scenes have their own status bars, so we should only show the home status bar
+                // if we're not in a scene. The one exception: If the scene is occluded, then the
+                // occluding app needs to show the status bar. (Fullscreen apps actually won't show
+                // the status bar but that's handled with the rest of our fullscreen app logic,
+                // which lives elsewhere.)
+                currentScene == Scenes.Gone || isOccluded
+            }
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
+
     override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> =
         if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
             emptyFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
index d10554f..b836016 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
@@ -20,13 +20,16 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Tracing
+import com.android.systemui.dagger.qualifiers.UiBackground
 import dagger.Module
 import dagger.Provides
+import java.util.concurrent.Executor
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DelicateCoroutinesApi
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.newFixedThreadPoolContext
 import kotlinx.coroutines.plus
 
@@ -83,4 +86,25 @@
     ): CoroutineContext {
         return bgCoroutineDispatcher + tracingCoroutineContext
     }
+
+    /** Coroutine dispatcher for background operations on for UI. */
+    @Provides
+    @SysUISingleton
+    @UiBackground
+    @Deprecated(
+        "Use @UiBackground CoroutineContext instead",
+        ReplaceWith("uiBgCoroutineContext()", "kotlin.coroutines.CoroutineContext")
+    )
+    fun uiBgDispatcher(@UiBackground uiBgExecutor: Executor): CoroutineDispatcher =
+        uiBgExecutor.asCoroutineDispatcher()
+
+    @Provides
+    @UiBackground
+    @SysUISingleton
+    fun uiBgCoroutineContext(
+        @Tracing tracingCoroutineContext: CoroutineContext,
+        @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher,
+    ): CoroutineContext {
+        return uiBgCoroutineDispatcher + tracingCoroutineContext
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index aee441a..c45f98e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -567,7 +567,7 @@
             streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
             final StreamState ss = streamStateW(stream);
-            ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
+            ss.muteSupported = mAudio.isStreamMutableByUi(stream);
             ss.name = STREAMS.get(stream);
             checkRoutedToBluetoothW(stream);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index cb61534..8457bdb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -51,7 +51,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index 64c162f..639b53b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -46,6 +46,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleCoroutineScope;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
@@ -159,7 +160,9 @@
             ArgumentCaptor<LifecycleObserver> observerCaptor =
                     ArgumentCaptor.forClass(LifecycleObserver.class);
             verify(mLifecycleRegistry, atLeast(1)).addObserver(observerCaptor.capture());
-            mLifecycleObservers.addAll(observerCaptor.getAllValues());
+            mLifecycleObservers.addAll(observerCaptor.getAllValues().stream().filter(
+                    lifecycleObserver -> !(lifecycleObserver instanceof LifecycleCoroutineScope))
+                    .toList());
 
             updateLifecycle(Lifecycle.State.RESUMED);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index b58eb49..e2cca38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -23,7 +23,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -34,20 +33,13 @@
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
-import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.Flags;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.testing.TestableLooper;
@@ -96,7 +88,6 @@
 import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -111,9 +102,6 @@
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class GlobalActionsDialogLiteTest extends SysuiTestCase {
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
     private GlobalActionsDialogLite mGlobalActionsDialogLite;
 
     @Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs;
@@ -153,7 +141,6 @@
     @Mock private DialogTransitionAnimator mDialogTransitionAnimator;
     @Mock private SelectedUserInteractor mSelectedUserInteractor;
     @Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
-    @Mock private BiometricManager mBiometricManager;
     @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
 
     private TestableLooper mTestableLooper;
@@ -170,13 +157,10 @@
         when(mUserContextProvider.getUserContext()).thenReturn(mContext);
         when(mResources.getConfiguration()).thenReturn(
                 getContext().getResources().getConfiguration());
-        when(mBiometricManager.canAuthenticate(anyInt())).thenReturn(
-                BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
 
         mGlobalSettings = new FakeGlobalSettings();
         mSecureSettings = new FakeSettings();
         mInteractor = mKosmos.getGlobalActionsInteractor();
-        mContext.addMockSystemService(Context.BIOMETRIC_SERVICE, mBiometricManager);
 
         mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
                 mWindowManagerFuncs,
@@ -567,35 +551,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
-    public void requestBiometricAuth_whenShutDownShortPressAndMandatoryBiometricsActive() {
-        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
-        ArgumentCaptor<BiometricPrompt.AuthenticationCallback>
-                authenticationCallbackArgumentCaptor = ArgumentCaptor.forClass(
-                        BiometricPrompt.AuthenticationCallback.class);
-
-        when(mBiometricManager.canAuthenticate(
-                BiometricManager.Authenticators.MANDATORY_BIOMETRICS)).thenReturn(
-                        BiometricManager.BIOMETRIC_SUCCESS);
-        doNothing().when(mGlobalActionsDialogLite).launchBiometricPromptForMandatoryBiometrics(
-                any());
-
-        GlobalActionsDialogLite.ShutDownAction shutDownAction =
-                mGlobalActionsDialogLite.new ShutDownAction();
-        shutDownAction.onPress();
-
-        verify(mGlobalActionsDialogLite).launchBiometricPromptForMandatoryBiometrics(
-                authenticationCallbackArgumentCaptor.capture());
-
-        BiometricPrompt.AuthenticationCallback authenticationCallback =
-                authenticationCallbackArgumentCaptor.getValue();
-        authenticationCallback.onAuthenticationSucceeded(null);
-
-        verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
-        verify(mWindowManagerFuncs).shutdown();
-    }
-
-    @Test
     public void testShouldLogLockdownPress() {
         GlobalActionsDialogLite.LockDownAction lockDownAction =
                 mGlobalActionsDialogLite.new LockDownAction();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 6398a5a..c1bd378 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -19,26 +19,22 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
-import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.policy.keyguardStateController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.whenever
+import org.junit.runners.JUnit4
 
 @ExperimentalCoroutinesApi
 @RunWith(AndroidJUnit4::class)
@@ -54,35 +50,13 @@
     fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
         testScope.runTest {
             mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-            val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
-            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsUdfps()
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepToLockscreen(0f, TransitionState.STARTED),
-                    stepToLockscreen(.4f),
-                    stepToLockscreen(1f, TransitionState.FINISHED),
-                ),
-                testScope,
-            )
-            assertThat(canShowAlternateBouncer).isTrue()
-            transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromLockscreenToAlternateBouncer(.4f),
-                    stepFromLockscreenToAlternateBouncer(.6f),
-                ),
-                testScope,
-            )
-            assertThat(canShowAlternateBouncer).isTrue()
-            assertThat(alternateBouncerWindowRequired).isTrue()
-
-            transitionRepository.sendTransitionSteps(
-                listOf(
                     stepFromAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromAlternateBouncer(.2f),
+                    stepFromAlternateBouncer(.4f),
                     stepFromAlternateBouncer(.6f),
                 ),
                 testScope,
@@ -104,21 +78,13 @@
             mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
-            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsUdfps()
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepToLockscreen(0f, TransitionState.STARTED),
-                    stepToLockscreen(.4f),
-                    stepToLockscreen(1f, TransitionState.FINISHED),
-                ),
-                testScope,
-            )
-            transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromLockscreenToAlternateBouncer(.4f),
-                    stepFromLockscreenToAlternateBouncer(.6f),
+                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
+                    stepFromAlternateBouncer(.4f),
+                    stepFromAlternateBouncer(.6f),
+                    stepFromAlternateBouncer(1f),
                 ),
                 testScope,
             )
@@ -131,23 +97,13 @@
             mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
-            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsUdfps()
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepFromLockscreenToDozing(0f, TransitionState.STARTED),
-                    stepFromLockscreenToDozing(.4f),
-                    stepFromLockscreenToDozing(.6f),
-                    stepFromLockscreenToDozing(1f, TransitionState.FINISHED),
-                ),
-                testScope,
-            )
-            assertThat(alternateBouncerWindowRequired).isFalse()
-            transitionRepository.sendTransitionSteps(
-                listOf(
                     stepFromDozingToLockscreen(0f, TransitionState.STARTED),
                     stepFromDozingToLockscreen(.4f),
                     stepFromDozingToLockscreen(.6f),
+                    stepFromDozingToLockscreen(1f),
                 ),
                 testScope,
             )
@@ -160,39 +116,19 @@
             mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
-            givenCanShowAlternateBouncer()
             fingerprintPropertyRepository.supportsRearFps()
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepToLockscreen(0f, TransitionState.STARTED),
-                    stepToLockscreen(.4f),
-                    stepToLockscreen(1f, TransitionState.FINISHED),
-                ),
-                testScope,
-            )
-            transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromLockscreenToAlternateBouncer(.4f),
-                    stepFromLockscreenToAlternateBouncer(.6f),
+                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
+                    stepFromAlternateBouncer(.4f),
+                    stepFromAlternateBouncer(.6f),
+                    stepFromAlternateBouncer(1f),
                 ),
                 testScope,
             )
             assertThat(alternateBouncerWindowRequired).isFalse()
         }
 
-    private fun stepToLockscreen(
-        value: Float,
-        state: TransitionState = TransitionState.RUNNING
-    ): TransitionStep {
-        return step(
-            from = KeyguardState.GONE,
-            to = KeyguardState.LOCKSCREEN,
-            value = value,
-            transitionState = state,
-        )
-    }
-
     private fun stepFromAlternateBouncer(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
@@ -205,18 +141,6 @@
         )
     }
 
-    private fun stepFromLockscreenToAlternateBouncer(
-        value: Float,
-        state: TransitionState = TransitionState.RUNNING
-    ): TransitionStep {
-        return step(
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.ALTERNATE_BOUNCER,
-            value = value,
-            transitionState = state,
-        )
-    }
-
     private fun stepFromDozingToLockscreen(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
@@ -229,18 +153,6 @@
         )
     }
 
-    private fun stepFromLockscreenToDozing(
-        value: Float,
-        state: TransitionState = TransitionState.RUNNING
-    ): TransitionStep {
-        return step(
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.DOZING,
-            value = value,
-            transitionState = state,
-        )
-    }
-
     private fun step(
         from: KeyguardState,
         to: KeyguardState,
@@ -255,16 +167,4 @@
             ownerName = "AlternateBouncerViewModelTest"
         )
     }
-
-    /**
-     * Given the alternate bouncer parameters are set so that the alternate bouncer can show, aside
-     * from the fingerprint modality.
-     */
-    private fun givenCanShowAlternateBouncer() {
-        kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false)
-        kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
-        kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
-        whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
-        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index 42b81de..c918ed8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -97,6 +97,7 @@
                 qsTileLogger,
                 FakeSystemClock(),
                 testCoroutineDispatcher,
+                testCoroutineDispatcher,
                 testScope.backgroundScope,
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index ee27cea..01540e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -49,6 +49,8 @@
 import com.android.systemui.animation.AnimatorTestRule;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.DisableSceneContainer;
+import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogcatEchoTracker;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -302,6 +304,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void disable_shadeOpenAndShouldHide_everythingHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -318,6 +321,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void disable_shadeOpenButNotShouldHide_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -335,6 +339,7 @@
 
     /** Regression test for b/279790651. */
     @Test
+    @DisableSceneContainer
     public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -376,6 +381,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void disable_isTransitioningToOccluded_everythingHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -390,6 +396,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -674,6 +681,80 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
+        resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false);
+
+        // THEN all views are hidden
+        assertEquals(View.GONE, getClockView().getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void isHomeStatusBarAllowedByScene_true_everythingShown() {
+        resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(true);
+
+        // THEN all views are shown
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        // WHEN the scene doesn't allow the status bar
+        mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false);
+
+        // BUT the disable flags want to show the status bar
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // THEN all views are hidden (the disable flags aren't respected)
+        assertEquals(View.GONE, getClockView().getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        // WHEN the scene does allow the status bar
+        mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(true);
+
+        // AND the disable flags want to hide the clock
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
+
+        // THEN all views are shown except the clock (the disable flags are used)
+        assertEquals(View.GONE, getClockView().getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+    }
+
+    @Test
+    @DisableSceneContainer
+    public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
+        resumeAndGetFragment();
+
+        // Even if the scene says to hide the home status bar
+        mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false);
+
+        // The value isn't used because the scene container flag is disabled, so all views are shown
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+    }
+
+    @Test
     public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -758,6 +839,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -779,6 +861,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index b4c6106..94159bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -36,6 +37,10 @@
 import com.android.systemui.log.assertLogsWtf
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
@@ -84,6 +89,8 @@
             kosmos.lightsOutInteractor,
             kosmos.activeNotificationsInteractor,
             kosmos.keyguardTransitionInteractor,
+            kosmos.sceneInteractor,
+            kosmos.sceneContainerOcclusionInteractor,
             kosmos.ongoingActivityChipsViewModel,
             kosmos.applicationCoroutineScope,
         )
@@ -426,6 +433,68 @@
             assertIsShareToAppChip(latest)
         }
 
+    @Test
+    fun isHomeStatusBarAllowedByScene_sceneLockscreen_notOccluded_false() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isHomeStatusBarAllowedByScene_sceneLockscreen_occluded_true() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null)
+
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun isHomeStatusBarAllowedByScene_sceneBouncer_false() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isHomeStatusBarAllowedByScene_sceneCommunal_false() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Communal)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isHomeStatusBarAllowedByScene_sceneShade_false() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Shade)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isHomeStatusBarAllowedByScene_sceneGone_true() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Gone)
+
+            assertThat(latest).isTrue()
+        }
+
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
         ActiveNotificationsStore.Builder()
             .apply { notifications.forEach(::addIndividualNotif) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index b66eed0..d3f1125 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -31,6 +31,8 @@
     override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
         MutableStateFlow(OngoingActivityChipModel.Hidden)
 
+    override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
+
     override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
 
     fun setNotificationLightsOut(lightsOut: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 78d8abe..a124b34 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -43,7 +43,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.systemui.broadcast.FakeBroadcastDispatcher;
 import com.android.systemui.flags.SceneContainerRule;
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index 5db9d31..9dae44d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -16,7 +16,6 @@
 package com.android.systemui
 
 import android.app.ActivityManager
-import android.app.DreamManager
 import android.app.admin.DevicePolicyManager
 import android.app.trust.TrustManager
 import android.os.UserManager
@@ -33,7 +32,6 @@
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.ScreenLifecycle
@@ -95,7 +93,6 @@
     @get:Provides val demoModeController: DemoModeController = mock(),
     @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
     @get:Provides val dozeParameters: DozeParameters = mock(),
-    @get:Provides val dreamManager: DreamManager = mock(),
     @get:Provides val dumpManager: DumpManager = mock(),
     @get:Provides val headsUpManager: HeadsUpManager = mock(),
     @get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
@@ -133,7 +130,6 @@
     @get:Provides val systemUIDialogManager: SystemUIDialogManager = mock(),
     @get:Provides val deviceEntryIconTransitions: Set<DeviceEntryIconTransition> = emptySet(),
     @get:Provides val communalInteractor: CommunalInteractor = mock(),
-    @get:Provides val communalSceneInteractor: CommunalSceneInteractor = mock(),
     @get:Provides val sceneLogger: SceneLogger = mock(),
     @get:Provides val trustManager: TrustManager = mock(),
     @get:Provides val primaryBouncerInteractor: PrimaryBouncerInteractor = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
index 744b127..edf77a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
@@ -16,9 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.service.dream.dreamManager
 import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
@@ -37,10 +35,8 @@
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             communalInteractor = communalInteractor,
-            communalSceneInteractor = communalSceneInteractor,
             powerInteractor = powerInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
             deviceEntryRepository = deviceEntryRepository,
-            dreamManager = dreamManager
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 3c62b44..b5e6f75 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel
-import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.ui.systemBarUtilsProxy
 
 val Kosmos.keyguardClockViewModel by
@@ -32,7 +31,6 @@
             keyguardClockInteractor = keyguardClockInteractor,
             applicationScope = applicationCoroutineScope,
             aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel,
-            notifsKeyguardInteractor = notificationsKeyguardInteractor,
             shadeInteractor = shadeInteractor,
             systemBarUtils = systemBarUtilsProxy,
             configurationInteractor = configurationInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
index 419e781..dceb8bf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.util.mockito.whenever
 import javax.inject.Provider
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
 
 var Kosmos.newFactoryTileMap by Kosmos.Fixture { emptyMap<String, Provider<QSTileViewModel>>() }
@@ -50,7 +49,7 @@
                         instanceIdSequenceFake.newInstanceId(),
                     )
                 object : QSTileViewModel {
-                    override val state: SharedFlow<QSTileState> =
+                    override val state: StateFlow<QSTileState?> =
                         MutableStateFlow(QSTileState.build({ null }, tileSpec.spec) {})
                     override val config: QSTileConfig = config
                     override val isAvailable: StateFlow<Boolean> = MutableStateFlow(true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
index dcfcce7..537be4f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
@@ -69,6 +69,7 @@
             qsTileLogger,
             systemClock,
             testDispatcher,
+            testDispatcher,
             testScope.backgroundScope,
         )
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 36d97f6..32491b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -47,6 +47,7 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
@@ -3086,6 +3087,7 @@
         scheduleUpdateClientsIfNeededLocked(userState, forceUpdate);
         updateAccessibilityShortcutTargetsLocked(userState, HARDWARE);
         updateAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
+        updateAccessibilityShortcutTargetsLocked(userState, GESTURE);
         updateAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
         // Update the capabilities before the mode because we will check the current mode is
         // invalid or not..
@@ -3207,6 +3209,7 @@
         somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, HARDWARE);
         somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
         somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
+        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, GESTURE);
         somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
         somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
         somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
@@ -3750,23 +3753,19 @@
             return;
         }
 
-        final List<Pair<Integer, String>> shortcutTypeAndShortcutSetting = new ArrayList<>(3);
-        shortcutTypeAndShortcutSetting.add(
-                new Pair<>(HARDWARE,
-                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
-        shortcutTypeAndShortcutSetting.add(
-                new Pair<>(SOFTWARE,
-                        Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
+        final List<Integer> shortcutTypes = new ArrayList<>(4);
+        shortcutTypes.add(HARDWARE);
+        shortcutTypes.add(SOFTWARE);
         if (android.view.accessibility.Flags.a11yQsShortcut()) {
-            shortcutTypeAndShortcutSetting.add(
-                    new Pair<>(QUICK_SETTINGS,
-                            Settings.Secure.ACCESSIBILITY_QS_TARGETS));
+            shortcutTypes.add(QUICK_SETTINGS);
+        }
+        if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            shortcutTypes.add(GESTURE);
         }
 
         final ComponentName serviceName = service.getComponentName();
-        for (Pair<Integer, String> shortcutTypePair : shortcutTypeAndShortcutSetting) {
-            int shortcutType = shortcutTypePair.first;
-            String shortcutSettingName = shortcutTypePair.second;
+        for (Integer shortcutType: shortcutTypes) {
+            String shortcutSettingName = ShortcutUtils.convertToKey(shortcutType);
             if (userState.removeShortcutTargetLocked(shortcutType, serviceName)) {
                 final Set<String> currentTargets = userState.getShortcutTargetsLocked(shortcutType);
                 persistColonDelimitedSetToSettingLocked(
@@ -4068,6 +4067,11 @@
             boolean enable, @UserShortcutType int shortcutTypes,
             @NonNull List<String> shortcutTargets, @UserIdInt int userId) {
         enableShortcutsForTargets_enforcePermission();
+        if ((shortcutTypes & GESTURE) == GESTURE
+                && !android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            throw new IllegalArgumentException(
+                    "GESTURE type shortcuts are disabled by feature flag");
+        }
         for (int shortcutType : USER_SHORTCUT_TYPES) {
             if ((shortcutTypes & shortcutType) == shortcutType) {
                 enableShortcutForTargets(enable, shortcutType, shortcutTargets, userId);
@@ -5451,6 +5455,9 @@
         private final Uri mAccessibilityButtonTargetsUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
 
+        private final Uri mAccessibilityGestureTargetsUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS);
+
         private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS);
 
@@ -5504,6 +5511,8 @@
             contentResolver.registerContentObserver(
                     mAccessibilityButtonTargetsUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
+                    mAccessibilityGestureTargetsUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
                     mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mUserInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
@@ -5575,6 +5584,10 @@
                     if (readAccessibilityShortcutTargetsLocked(userState, SOFTWARE)) {
                         onUserStateChangedLocked(userState);
                     }
+                } else if (mAccessibilityGestureTargetsUri.equals(uri)) {
+                    if (readAccessibilityShortcutTargetsLocked(userState, GESTURE)) {
+                        onUserStateChangedLocked(userState);
+                    }
                 } else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
                         || mUserInteractiveUiTimeoutUri.equals(uri)) {
                     readUserRecommendedUiTimeoutSettingsLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 710bae9..7bcbc27 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -27,6 +27,12 @@
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
 
 import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -101,6 +107,7 @@
     private final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
 
     private final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
+    private final ArraySet<String> mAccessibilityGestureTargets = new ArraySet<>();
     private final ArraySet<String> mAccessibilityQsTargets = new ArraySet<>();
 
     /**
@@ -224,6 +231,7 @@
         mTouchExplorationGrantedServices.clear();
         mAccessibilityShortcutKeyTargets.clear();
         mAccessibilityButtonTargets.clear();
+        mAccessibilityGestureTargets.clear();
         mTargetAssignedToAccessibilityButton = null;
         mIsTouchExplorationEnabled = false;
         mServiceHandlesDoubleTap = false;
@@ -524,6 +532,20 @@
         }
     }
 
+    private void dumpShortcutTargets(
+            PrintWriter pw, @UserShortcutType int shortcutType, String name) {
+        pw.append("     ").append(name).append(":{");
+        ArraySet<String> targets = getShortcutTargetsInternalLocked(shortcutType);
+        int size = targets.size();
+        for (int i = 0; i < size; i++) {
+            if (i > 0) {
+                pw.append(", ");
+            }
+            pw.append(targets.valueAt(i));
+        }
+        pw.println("}");
+    }
+
     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.append("User state[");
         pw.println();
@@ -553,30 +575,12 @@
                 .append(String.valueOf(mAlwaysOnMagnificationEnabled));
         pw.append("}");
         pw.println();
-        pw.append("     shortcut key:{");
-        int size = mAccessibilityShortcutKeyTargets.size();
-        for (int i = 0; i < size; i++) {
-            final String componentId = mAccessibilityShortcutKeyTargets.valueAt(i);
-            pw.append(componentId);
-            if (i + 1 < size) {
-                pw.append(", ");
-            }
-        }
-        pw.println("}");
-        pw.append("     button:{");
-        size = mAccessibilityButtonTargets.size();
-        for (int i = 0; i < size; i++) {
-            final String componentId = mAccessibilityButtonTargets.valueAt(i);
-            pw.append(componentId);
-            if (i + 1 < size) {
-                pw.append(", ");
-            }
-        }
-        pw.println("}");
+        dumpShortcutTargets(pw, HARDWARE, "shortcut key");
+        dumpShortcutTargets(pw, SOFTWARE, "button");
         pw.append("     button target:{").append(mTargetAssignedToAccessibilityButton);
         pw.println("}");
-        pw.append("     qs shortcut targets:").append(mAccessibilityQsTargets.toString());
-        pw.println();
+        dumpShortcutTargets(pw, GESTURE, "gesture");
+        dumpShortcutTargets(pw, QUICK_SETTINGS, "qs shortcut targets");
         pw.append("     a11y tiles in QS panel:").append(mA11yTilesInQsPanel.toString());
         pw.println();
         pw.append("     Bound services:{");
@@ -782,15 +786,17 @@
     }
 
     private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) {
-        if (shortcutType == UserShortcutType.HARDWARE) {
+        if (shortcutType == HARDWARE) {
             return mAccessibilityShortcutKeyTargets;
-        } else if (shortcutType == UserShortcutType.SOFTWARE) {
+        } else if (shortcutType == SOFTWARE) {
             return mAccessibilityButtonTargets;
-        } else if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+        } else if (shortcutType == GESTURE) {
+            return mAccessibilityGestureTargets;
+        } else if (shortcutType == QUICK_SETTINGS) {
             return mAccessibilityQsTargets;
-        } else if ((shortcutType == UserShortcutType.TRIPLETAP
+        } else if ((shortcutType == TRIPLETAP
                 && isMagnificationSingleFingerTripleTapEnabledLocked()) || (
-                shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP
+                shortcutType == TWOFINGER_DOUBLETAP
                         && isMagnificationTwoFingerTripleTapEnabledLocked())) {
             ArraySet<String> targets = new ArraySet<>();
             targets.add(MAGNIFICATION_CONTROLLER_NAME);
@@ -811,7 +817,7 @@
      */
     boolean updateShortcutTargetsLocked(
             Set<String> newTargets, @UserShortcutType int shortcutType) {
-        final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP;
+        final int mask = TRIPLETAP | TWOFINGER_DOUBLETAP;
         if ((shortcutType & mask) != 0) {
             throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets.");
         }
@@ -867,8 +873,8 @@
      */
     public boolean removeShortcutTargetLocked(
             @UserShortcutType int shortcutType, ComponentName target) {
-        if (shortcutType == UserShortcutType.TRIPLETAP
-                || shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP) {
+        if (shortcutType == TRIPLETAP
+                || shortcutType == TWOFINGER_DOUBLETAP) {
             throw new UnsupportedOperationException(
                     "removeShortcutTargetLocked only support shortcut type: "
                             + "software and hardware and quick settings for now"
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 3c323f9..657e261 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -24,6 +24,7 @@
 import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
@@ -720,6 +721,13 @@
                     }
                 }
                 break;
+            case POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR:
+                if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+                    synchronized (mVirtualDeviceLock) {
+                        mDevicePolicies.put(policyType, devicePolicy);
+                    }
+                }
+                break;
             default:
                 throw new IllegalArgumentException("Device policy " + policyType
                         + " cannot be changed at runtime. ");
@@ -1321,14 +1329,15 @@
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     private void onActivityBlocked(int displayId, ActivityInfo activityInfo) {
         Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
-        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()
-                || !Objects.equals(activityInfo.getComponentName(), intent.getComponent())) {
+        if (shouldShowBlockedActivityDialog(
+                activityInfo.getComponentName(), intent.getComponent())) {
             mContext.startActivityAsUser(
                     intent.addFlags(
                             Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
                     ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
                     UserHandle.SYSTEM);
         }
+
         if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
             mActivityListenerAdapter.onActivityLaunchBlocked(
                     displayId,
@@ -1338,6 +1347,20 @@
         }
     }
 
+    private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent,
+            ComponentName blockedAppStreamingActivityComponent) {
+        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+            return true;
+        }
+        if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) {
+            // Do not show the dialog if it was blocked for some reason already to avoid
+            // infinite blocking loop.
+            return false;
+        }
+        // Do not show the dialog if disabled by policy.
+        return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR) == DEVICE_POLICY_DEFAULT;
+    }
+
     private void onSecureWindowShown(int displayId, int uid) {
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(displayId)) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d9962c7..1cd20ed 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -47,7 +47,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) transform-protolog-calls " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
         "--loggroups-jar $(location :protolog-groups) " +
         "--viewer-config-file-path /etc/core.protolog.pb " +
@@ -66,7 +66,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
         "--loggroups-jar $(location :protolog-groups) " +
         "--viewer-config-type json " +
@@ -83,7 +83,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
         "--loggroups-jar $(location :protolog-groups) " +
         "--viewer-config-type proto " +
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 3d3535d..73d8384 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -197,8 +197,8 @@
                 /* processCpuTracker= */null, /* lastPids= */null,
                 CompletableFuture.completedFuture(Watchdog.getInterestingNativePids()),
                 /* logExceptionCreatingFile= */null, /* subject= */null,
-                /* criticalEventSection= */null, Runnable::run,
-                /* latencyTracker= */null);
+                /* criticalEventSection= */null, /* extraHeaders= */ null,
+                Runnable::run, /* latencyTracker= */null);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 72a55dbe..21947ba 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -74,6 +74,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
@@ -991,6 +992,9 @@
             FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
         }
 
+        final LinkedHashMap headersMap =
+                com.android.server.am.Flags.enableDropboxWatchdogHeaders()
+                ? new LinkedHashMap<>(Collections.singletonMap("Watchdog-Type", dropboxTag)) : null;
         long anrTime = SystemClock.uptimeMillis();
         StringBuilder report = new StringBuilder();
         report.append(ResourcePressureUtil.currentPsiState());
@@ -998,8 +1002,9 @@
         StringWriter tracesFileException = new StringWriter();
         final File stack = StackTracesDumpHelper.dumpStackTraces(
                 pids, processCpuTracker, new SparseBooleanArray(),
-                CompletableFuture.completedFuture(getInterestingNativePids()), tracesFileException,
-                subject, criticalEvents, Runnable::run, /* latencyTracker= */null);
+                CompletableFuture.completedFuture(getInterestingNativePids()),
+                tracesFileException, subject, criticalEvents, headersMap,
+                Runnable::run, /* latencyTracker= */null);
         // Give some extra time to make sure the stack traces get written.
         // The system's been hanging for a whlie, another second or two won't hurt much.
         SystemClock.sleep(5000);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6b3b39..25fb729 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -429,7 +429,7 @@
 import com.android.internal.os.Zygote;
 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.policy.AttributeCache;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index d4d4965..ba4b71c 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -60,7 +60,6 @@
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.server.wm.WindowProcessController;
-import com.android.server.utils.AnrTimer;
 
 import java.io.File;
 import java.io.PrintWriter;
@@ -69,6 +68,7 @@
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -429,7 +429,7 @@
             }
         }
         // Build memory headers for the ANRing process.
-        String memoryHeaders = buildMemoryHeadersFor(pid);
+        LinkedHashMap<String, String> memoryHeaders = buildMemoryHeadersFor(pid);
 
         // Get critical event log before logging the ANR so that it doesn't occur in the log.
         latencyTracker.criticalEventLogStarted();
@@ -753,7 +753,7 @@
             resolver.getUserId()) != 0;
     }
 
-    private @Nullable String buildMemoryHeadersFor(int pid) {
+    private @Nullable LinkedHashMap<String, String> buildMemoryHeadersFor(int pid) {
         if (pid <= 0) {
             Slog.i(TAG, "Memory header requested with invalid pid: " + pid);
             return null;
@@ -764,15 +764,13 @@
             return null;
         }
 
-        StringBuilder memoryHeaders = new StringBuilder();
-        memoryHeaders.append("RssHwmKb: ")
-            .append(snapshot.rssHighWaterMarkInKilobytes)
-            .append("\n");
-        memoryHeaders.append("RssKb: ").append(snapshot.rssInKilobytes).append("\n");
-        memoryHeaders.append("RssAnonKb: ").append(snapshot.anonRssInKilobytes).append("\n");
-        memoryHeaders.append("RssShmemKb: ").append(snapshot.rssShmemKilobytes).append("\n");
-        memoryHeaders.append("VmSwapKb: ").append(snapshot.swapInKilobytes).append("\n");
-        return memoryHeaders.toString();
+        LinkedHashMap<String, String> memoryHeaders = new LinkedHashMap<>();
+        memoryHeaders.put("RssHwmKb", Integer.toString(snapshot.rssHighWaterMarkInKilobytes));
+        memoryHeaders.put("RssKb", Integer.toString(snapshot.rssInKilobytes));
+        memoryHeaders.put("RssAnonKb", Integer.toString(snapshot.anonRssInKilobytes));
+        memoryHeaders.put("RssShmemKb", Integer.toString(snapshot.rssShmemKilobytes));
+        memoryHeaders.put("VmSwapKb", Integer.toString(snapshot.swapInKilobytes));
+        return memoryHeaders;
     }
     /**
      * Unless configured otherwise, swallow ANRs in background processes & kill the process.
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index c1374e1..2021ba4 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -47,6 +47,8 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -100,15 +102,17 @@
     /**
      * @param subject the subject of the dumped traces
      * @param criticalEventSection the critical event log, passed as a string
+     * @param extraHeaders Optional, Extra headers added by the dumping method
      */
     public static File dumpStackTraces(ArrayList<Integer> firstPids,
             ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
-            String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor,
+            String subject, String criticalEventSection,
+            LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor,
             AnrLatencyTracker latencyTracker) {
         return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
                 logExceptionCreatingFile, null, subject, criticalEventSection,
-                /* memoryHeaders= */ null, auxiliaryTaskExecutor, null, latencyTracker);
+                extraHeaders, auxiliaryTaskExecutor, null, latencyTracker);
     }
 
     /**
@@ -119,7 +123,7 @@
             ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
             AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
-            String memoryHeaders, @NonNull Executor auxiliaryTaskExecutor,
+            LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor,
            Future<File> firstPidFilePromise, AnrLatencyTracker latencyTracker) {
         try {
 
@@ -159,11 +163,12 @@
                 }
                 return null;
             }
+            boolean extraHeadersExist = extraHeaders != null && !extraHeaders.isEmpty();
 
-            if (subject != null || criticalEventSection != null || memoryHeaders != null) {
+            if (subject != null || criticalEventSection != null || extraHeadersExist) {
                 appendtoANRFile(tracesFile.getAbsolutePath(),
                         (subject != null ? "Subject: " + subject + "\n" : "")
-                        + (memoryHeaders != null ? memoryHeaders + "\n\n" : "")
+                        + (extraHeadersExist ? stringifyHeaders(extraHeaders) + "\n\n" : "")
                         + (criticalEventSection != null ? criticalEventSection : ""));
             }
 
@@ -614,4 +619,15 @@
         return pids;
     }
 
+    private static String stringifyHeaders(@NonNull LinkedHashMap<String, String> headers) {
+        StringBuilder headersString = new StringBuilder();
+        for (Map.Entry<String, String> entry : headers.entrySet()) {
+            headersString.append(entry.getKey())
+                    .append(": ")
+                    .append(entry.getValue())
+                    .append("\n");
+        }
+        return headersString.toString();
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 0dfa330..9b380ff 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -167,4 +167,12 @@
     description: "Allow logcat collection on synchronous dropbox collection"
     bug: "324222683"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enable_dropbox_watchdog_headers"
+    namespace: "dropbox"
+    description: "Add watchdog-specific dropbox headers"
+    bug: "330682397"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 39cb5a9..989a4d1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -461,6 +461,7 @@
     private static final int MSG_DISPATCH_PREFERRED_MIXER_ATTRIBUTES = 52;
     private static final int MSG_CONFIGURATION_CHANGED = 54;
     private static final int MSG_BROADCAST_MASTER_MUTE = 55;
+    private static final int MSG_UPDATE_CONTEXTUAL_VOLUMES = 56;
 
     /**
      * Messages handled by the {@link SoundDoseHelper}, do not exceed
@@ -707,10 +708,14 @@
     // Streams currently muted by ringer mode and dnd
     protected static volatile int sRingerAndZenModeMutedStreams;
 
-    /** Streams that can be muted. Do not resolve to aliases when checking.
+    /** Streams that can be muted by system. Do not resolve to aliases when checking.
      * @see System#MUTE_STREAMS_AFFECTED */
     private int mMuteAffectedStreams;
 
+    /** Streams that can be muted by user. Do not resolve to aliases when checking.
+     * @see System#MUTE_STREAMS_AFFECTED */
+    private int mUserMutableStreams;
+
     @NonNull
     private SoundEffectsHelper mSfxHelper;
 
@@ -2345,6 +2350,7 @@
                 mMuteAffectedStreams &= ~(1 << vss.mStreamType);
             }
         }
+        updateUserMutableStreams();
     }
 
     private void createStreamStates() {
@@ -2414,6 +2420,8 @@
         }
         pw.print("\n- mute affected streams = 0x");
         pw.println(Integer.toHexString(mMuteAffectedStreams));
+        pw.print("\n- user mutable streams = 0x");
+        pw.println(Integer.toHexString(mUserMutableStreams));
     }
 
     private void updateStreamVolumeAlias(boolean updateVolumes, String caller) {
@@ -2908,6 +2916,7 @@
         mMuteAffectedStreams = mSettings.getSystemIntForUser(cr,
                 System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
                 UserHandle.USER_CURRENT);
+        updateUserMutableStreams();
 
         updateMasterMono(cr);
 
@@ -2927,6 +2936,12 @@
         mVolumeController.loadSettings(cr);
     }
 
+    private void updateUserMutableStreams() {
+        mUserMutableStreams = mMuteAffectedStreams;
+        mUserMutableStreams &= ~(1 << AudioSystem.STREAM_VOICE_CALL);
+        mUserMutableStreams &= ~(1 << AudioSystem.STREAM_BLUETOOTH_SCO);
+    }
+
     @GuardedBy("mSettingsLock")
     private void resetActiveAssistantUidsLocked() {
         mActiveAssistantServiceUids = NO_ACTIVE_ASSISTANT_SERVICE_UIDS;
@@ -4466,7 +4481,7 @@
             }
         }
         if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) {
-            updateHearingAidVolumeOnVoiceActivityUpdate();
+            postUpdateContextualVolumes();
         }
         if (mMediaPlaybackActive.getAndSet(mediaActive) != mediaActive && mediaActive) {
             mSoundDoseHelper.scheduleMusicActiveCheck();
@@ -4604,13 +4619,29 @@
         }
     }
 
-    private void updateHearingAidVolumeOnVoiceActivityUpdate() {
-        final int streamType = getBluetoothContextualVolumeStream();
-        final int index = getStreamVolume(streamType);
-        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
-                mVoicePlaybackActive.get(), streamType, index));
-        mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
 
+    // delay between audio playback configuration update and checking
+    // actual stream activity to take async playback stop into account
+    private static final int UPDATE_CONTEXTUAL_VOLUME_DELAY_MS = 500;
+
+    /*package*/ void postUpdateContextualVolumes() {
+        sendMsg(mAudioHandler, MSG_UPDATE_CONTEXTUAL_VOLUMES, SENDMSG_REPLACE,
+                /*arg1*/ 0, /*arg2*/ 0, TAG, UPDATE_CONTEXTUAL_VOLUME_DELAY_MS);
+    }
+
+    private void onUpdateContextualVolumes() {
+        final int streamType = getBluetoothContextualVolumeStream();
+        final int device = getDeviceForStream(streamType);
+        final int index = getStreamVolume(streamType, device);
+
+        if (AudioSystem.isLeAudioDeviceType(device)) {
+            mDeviceBroker.postSetLeAudioVolumeIndex(index * 10,
+                    mStreamStates[streamType].getMaxIndex(), streamType);
+        } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
+            mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
+        }
+        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME,
+                mVoicePlaybackActive.get(), streamType, index, device));
     }
 
     /**
@@ -7125,6 +7156,11 @@
         return (mMuteAffectedStreams & (1 << streamType)) != 0;
     }
 
+    @Override
+    public boolean isStreamMutableByUi(int streamType) {
+        return (mUserMutableStreams & (1 << streamType)) != 0;
+    }
+
     private void ensureValidDirection(int direction) {
         switch (direction) {
             case AudioManager.ADJUST_LOWER:
@@ -9812,6 +9848,10 @@
                     onConfigurationChanged();
                     break;
 
+                case MSG_UPDATE_CONTEXTUAL_VOLUMES:
+                    onUpdateContextualVolumes();
+                    break;
+
                 case MusicFxHelper.MSG_EFFECT_CLIENT_GONE:
                     mMusicFxHelper.handleMessage(msg);
                     break;
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 8ea28be..631d5d8 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -227,7 +227,7 @@
         static final int VOL_SET_HEARING_AID_VOL = 3;
         static final int VOL_SET_AVRCP_VOL = 4;
         static final int VOL_ADJUST_VOL_UID = 5;
-        static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
+        static final int VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME = 6;
         static final int VOL_MODE_CHANGE_HEARING_AID = 7;
         static final int VOL_SET_GROUP_VOL = 8;
         static final int VOL_MUTE_STREAM_INT = 9;
@@ -299,14 +299,14 @@
             logMetricEvent();
         }
 
-        /** used for VOL_VOICE_ACTIVITY_HEARING_AID */
-        VolumeEvent(int op, boolean voiceActive, int stream, int index) {
+        /** used for VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME */
+        VolumeEvent(int op, boolean voiceActive, int stream, int index, int device) {
             mOp = op;
             mStream = stream;
             mVal1 = index;
             mVal2 = voiceActive ? 1 : 0;
             // unused
-            mVal3 = -1;
+            mVal3 = device;
             mCaller = null;
             mGroupName = null;
             logMetricEvent();
@@ -445,14 +445,16 @@
                             .set(MediaMetrics.Property.INDEX, mVal1)
                             .record();
                     return;
-                case VOL_VOICE_ACTIVITY_HEARING_AID:
+                case VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME:
                     new MediaMetrics.Item(mMetricsId)
-                            .set(MediaMetrics.Property.EVENT, "voiceActivityHearingAid")
+                            .set(MediaMetrics.Property.EVENT, "voiceActivityContextualVolume")
                             .set(MediaMetrics.Property.INDEX, mVal1)
                             .set(MediaMetrics.Property.STATE,
                                     mVal2 == 1 ? "active" : "inactive")
                             .set(MediaMetrics.Property.STREAM_TYPE,
                                     AudioSystem.streamToString(mStream))
+                            .set(MediaMetrics.Property.DEVICE,
+                                    AudioSystem.getOutputDeviceName(mVal3))
                             .record();
                     return;
                 case VOL_MODE_CHANGE_HEARING_AID:
@@ -538,11 +540,12 @@
                             .append(" flags:0x").append(Integer.toHexString(mVal2))
                             .append(") from ").append(mCaller)
                             .toString();
-                case VOL_VOICE_ACTIVITY_HEARING_AID:
+                case VOL_VOICE_ACTIVITY_CONTEXTUAL_VOLUME:
                     return new StringBuilder("Voice activity change (")
                             .append(mVal2 == 1 ? "active" : "inactive")
-                            .append(") causes setting HEARING_AID volume to idx:").append(mVal1)
+                            .append(") causes setting volume to idx:").append(mVal1)
                             .append(" stream:").append(AudioSystem.streamToString(mStream))
+                            .append(" device:").append(AudioSystem.getOutputDeviceName(mVal3))
                             .toString();
                 case VOL_MODE_CHANGE_HEARING_AID:
                     return new StringBuilder("setMode(")
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index f5a2a21..2a16872 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -369,10 +369,6 @@
                 checkPermission();
             }
 
-            if ((authenticators & Authenticators.MANDATORY_BIOMETRICS) != 0) {
-                checkBiometricAdvancedPermission();
-            }
-
             final long identity = Binder.clearCallingIdentity();
             try {
                 final int result = mBiometricService.canAuthenticate(
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 693a3e6..daaafcb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -418,7 +418,7 @@
     }
 
     protected int getRequestReason() {
-        if (isKeyguard() && !isBiometricPrompt()) {
+        if (isKeyguard()) {
             return BiometricRequestConstants.REASON_AUTH_KEYGUARD;
         } else if (isBiometricPrompt()) {
             // BP reason always takes precedent over settings, since callers from within
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 275c930..0f9c344 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -67,7 +67,7 @@
 /**
  * Represent a logical device of type TV residing in Android system.
  */
-public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
+public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     private static final String TAG = "HdmiCecLocalDeviceTv";
 
     // Whether ARC is available or not. "true" means that ARC is established between TV and
@@ -113,6 +113,18 @@
     // handle.
     private final DelayedMessageBuffer mDelayedMessageBuffer = new DelayedMessageBuffer(this);
 
+    private boolean mWasActiveSourceSetToConnectedDevice = false;
+
+    @VisibleForTesting
+    protected boolean getWasActiveSourceSetToConnectedDevice() {
+        return mWasActiveSourceSetToConnectedDevice;
+    }
+
+    protected void setWasActiveSourceSetToConnectedDevice(
+            boolean wasActiveSourceSetToConnectedDevice) {
+        mWasActiveSourceSetToConnectedDevice = wasActiveSourceSetToConnectedDevice;
+    }
+
     // Defines the callback invoked when TV input framework is updated with input status.
     // We are interested in the notification for HDMI input addition event, in order to
     // process any CEC commands that arrived before the input is added.
@@ -474,6 +486,7 @@
                 || info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
             mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
                     HdmiControlManager.POWER_STATUS_ON);
+            setWasActiveSourceSetToConnectedDevice(true);
             ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
             ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
         } else {
@@ -489,13 +502,16 @@
     protected int handleStandby(HdmiCecMessage message) {
         assertRunOnServiceThread();
 
-        // Ignore <Standby> from non-active source device.
-        if (getActiveSource().logicalAddress != message.getSource()) {
+        // If a device has previously asserted the active source status, ignore <Standby> from
+        // non-active source.
+        if (getWasActiveSourceSetToConnectedDevice()
+                && getActiveSource().logicalAddress != message.getSource()) {
             Slog.d(TAG, "<Standby> was not sent by the current active source, ignoring."
                     + " Current active source has logical address "
                     + getActiveSource().logicalAddress);
             return Constants.HANDLED;
         }
+        setWasActiveSourceSetToConnectedDevice(false);
         return super.handleStandby(message);
     }
 
@@ -1457,6 +1473,7 @@
             invokeStandbyCompletedCallback(callback);
             return;
         }
+        setWasActiveSourceSetToConnectedDevice(false);
         boolean sendStandbyOnSleep =
                 mService.getHdmiCecConfig().getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 97c32b9..4993412 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -255,17 +255,6 @@
         }
     }
 
-    private static boolean isCompatibleLocale(Locale systemLocale, Locale keyboardLocale) {
-        // Different languages are never compatible
-        if (!systemLocale.getLanguage().equals(keyboardLocale.getLanguage())) {
-            return false;
-        }
-        // If both the system and the keyboard layout have a country specifier, they must be equal.
-        return TextUtils.isEmpty(systemLocale.getCountry())
-                || TextUtils.isEmpty(keyboardLocale.getCountry())
-                || systemLocale.getCountry().equals(keyboardLocale.getCountry());
-    }
-
     @MainThread
     private void updateKeyboardLayouts() {
         // Scan all input devices state for keyboard layouts that have been uninstalled.
@@ -953,21 +942,33 @@
             return;
         }
 
+        List<String> layoutNames = new ArrayList<>();
+        for (String layoutDesc : config.getConfiguredLayouts()) {
+            KeyboardLayout kl = getKeyboardLayout(layoutDesc);
+            if (kl == null) {
+                // b/349033234: Weird state with stale keyboard layout configured.
+                // Possibly due to race condition between KCM providing package being removed and
+                // corresponding layouts being removed from Datastore and cache.
+                // {@see updateKeyboardLayouts()}
+                //
+                // Ideally notification will be correctly shown after the keyboard layouts are
+                // configured again with the new package state.
+                return;
+            }
+            layoutNames.add(kl.getLabel());
+        }
         showKeyboardLayoutNotification(
                 r.getString(
                         R.string.keyboard_layout_notification_selected_title,
                         inputDevice.getName()),
-                createConfiguredNotificationText(mContext, config.getConfiguredLayouts()),
+                createConfiguredNotificationText(mContext, layoutNames),
                 inputDevice);
     }
 
     @MainThread
     private String createConfiguredNotificationText(@NonNull Context context,
-            @NonNull Set<String> selectedLayouts) {
+            @NonNull List<String> layoutNames) {
         final Resources r = context.getResources();
-        List<String> layoutNames = new ArrayList<>();
-        selectedLayouts.forEach(
-                (layoutDesc) -> layoutNames.add(getKeyboardLayout(layoutDesc).getLabel()));
         Collections.sort(layoutNames);
         switch (layoutNames.size()) {
             case 1:
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index e1aa3a2..b229819 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -96,6 +96,32 @@
     @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY;
     @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
 
+    /**
+     * A set of status bits regarding the active IME.
+     *
+     * <p>This value is a combination of following two bits:</p>
+     * <dl>
+     * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
+     * <dd>
+     *   If this bit is ON, connected IME is ready to accept touch/key events.
+     * </dd>
+     * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
+     * <dd>
+     *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
+     * </dd>
+     * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
+     * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
+     *    currently invisible.
+     * </dd>
+     * </dl>
+     * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
+     * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
+     */
+    @GuardedBy("ImfLock.class") private int mImeWindowVis;
+
+    @GuardedBy("ImfLock.class")
+    private int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
+
     @Nullable private CountDownLatch mLatchForTesting;
 
     /**
@@ -473,7 +499,7 @@
 
         if (getCurToken() != null) {
             removeCurrentToken();
-            mService.resetSystemUiLocked();
+            mService.resetSystemUiLocked(this);
             mAutofillController.onResetSystemUi();
         }
 
@@ -662,4 +688,24 @@
     int getUserId() {
         return mUserId;
     }
+
+    @GuardedBy("ImfLock.class")
+    void setImeWindowVis(int imeWindowVis) {
+        mImeWindowVis = imeWindowVis;
+    }
+
+    @GuardedBy("ImfLock.class")
+    int getImeWindowVis() {
+        return mImeWindowVis;
+    }
+
+    @GuardedBy("ImfLock.class")
+    int getBackDisposition() {
+        return mBackDisposition;
+    }
+
+    @GuardedBy("ImfLock.class")
+    void setBackDisposition(int backDisposition) {
+        mBackDisposition = backDisposition;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ead7eb2..5228be4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -320,6 +320,19 @@
                 && Flags.concurrentInputMethods();
     }
 
+    /**
+     * Figures out the target IME user ID for a given {@link Binder} IPC.
+     *
+     * @param callingProcessUserId the user ID of the calling process
+     * @return User ID to be used for this {@link Binder} call.
+     */
+    @GuardedBy("ImfLock.class")
+    @UserIdInt
+    @BinderThread
+    private int resolveImeUserIdLocked(@UserIdInt int callingProcessUserId) {
+        return mExperimentalConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId;
+    }
+
     final Context mContext;
     final Resources mRes;
     private final Handler mHandler;
@@ -539,33 +552,6 @@
     @MultiUserUnawareField
     boolean mIsInteractive = true;
 
-    @MultiUserUnawareField
-    int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
-
-    /**
-     * A set of status bits regarding the active IME.
-     *
-     * <p>This value is a combination of following two bits:</p>
-     * <dl>
-     * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
-     * <dd>
-     *   If this bit is ON, connected IME is ready to accept touch/key events.
-     * </dd>
-     * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
-     * <dd>
-     *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
-     * </dd>
-     * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
-     * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
-     *    currently invisible.
-     * </dd>
-     * </dl>
-     * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
-     * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
-     */
-    @MultiUserUnawareField
-    int mImeWindowVis;
-
     @SharedByAllUsersField
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
 
@@ -923,7 +909,9 @@
                             // Uh oh, current input method is no longer around!
                             // Pick another one...
                             Slog.i(TAG, "Current input method removed: " + curInputMethodId);
-                            updateSystemUiLocked(0 /* vis */, mBackDisposition);
+                            final var bindingController = getInputMethodBindingController(userId);
+                            updateSystemUiLocked(0 /* vis */,
+                                    bindingController.getBackDisposition(), userId);
                             if (!chooseNewDefaultIMELocked()) {
                                 changed = true;
                                 curIm = null;
@@ -1399,7 +1387,9 @@
                 mStatusBarManagerInternal =
                         LocalServices.getService(StatusBarManagerInternal.class);
                 hideStatusBarIconLocked();
-                updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+                final var bindingController = getInputMethodBindingController(currentUserId);
+                updateSystemUiLocked(bindingController.getImeWindowVis(),
+                        bindingController.getBackDisposition(), currentUserId);
                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
                         com.android.internal.R.bool.show_ongoing_ime_switcher);
                 if (mShowOngoingImeSwitcherForPhones) {
@@ -2413,11 +2403,13 @@
     }
 
     @GuardedBy("ImfLock.class")
-    void resetSystemUiLocked() {
+    void resetSystemUiLocked(InputMethodBindingController bindingController) {
         // Set IME window status as invisible when unbinding current method.
-        mImeWindowVis = 0;
-        mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
-        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+        final int imeWindowVis = 0;
+        final int backDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
+        bindingController.setImeWindowVis(imeWindowVis);
+        bindingController.setBackDisposition(backDisposition);
+        updateSystemUiLocked(imeWindowVis, backDisposition, bindingController.getUserId());
     }
 
     @GuardedBy("ImfLock.class")
@@ -2531,7 +2523,12 @@
                     sessionState.mSession.finishSession();
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Session failed to close due to remote exception", e);
-                    updateSystemUiLocked(0 /* vis */, mBackDisposition);
+                    // TODO(b/350386877): Propagate userId from the caller or infer it from
+                    //  sessionState
+                    final int userId = mCurrentUserId;
+                    final var bindingController = getInputMethodBindingController(userId);
+                    updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(),
+                            userId);
                 }
                 sessionState.mSession = null;
             }
@@ -2751,8 +2748,8 @@
             if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) {
                 return;
             }
-            mImeWindowVis = vis;
-            mBackDisposition = backDisposition;
+            bindingController.setImeWindowVis(vis);
+            bindingController.setBackDisposition(backDisposition);
             updateSystemUiLocked(vis, backDisposition, userId);
         }
 
@@ -2789,23 +2786,23 @@
 
     private void updateImeWindowStatus(boolean disableImeIcon) {
         synchronized (ImfLock.class) {
+            // TODO(b/350386877): Propagate userId from the caller.
+            final int userId = mCurrentUserId;
             if (disableImeIcon) {
-                updateSystemUiLocked(0, mBackDisposition);
+                final var bindingController = getInputMethodBindingController(userId);
+                updateSystemUiLocked(0, bindingController.getBackDisposition(), userId);
             } else {
-                updateSystemUiLocked();
+                updateSystemUiLocked(userId);
             }
         }
     }
 
-    @GuardedBy("ImfLock.class")
-    void updateSystemUiLocked() {
-        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
-    }
-
     // Caution! This method is called in this class. Handle multi-user carefully
     @GuardedBy("ImfLock.class")
-    private void updateSystemUiLocked(int vis, int backDisposition) {
-        updateSystemUiLocked(vis, backDisposition, mCurrentUserId);
+    void updateSystemUiLocked(@UserIdInt int userId) {
+        final var bindingController = getInputMethodBindingController(userId);
+        updateSystemUiLocked(bindingController.getImeWindowVis(),
+                bindingController.getBackDisposition(), userId);
     }
 
     @GuardedBy("ImfLock.class")
@@ -2916,6 +2913,9 @@
         final var userData = getUserData(userId);
         userData.mSwitchingController.resetCircularListLocked(mContext, settings);
         userData.mHardwareKeyboardShortcutController.update(settings);
+
+        final var bindingController = getInputMethodBindingController(userId);
+        bindingController.setSelectedMethodId(id);
     }
 
     @GuardedBy("ImfLock.class")
@@ -3052,7 +3052,8 @@
                 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId);
                 IInputMethodInvoker curMethod = bindingController.getCurMethod();
                 if (curMethod != null) {
-                    updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+                    updateSystemUiLocked(bindingController.getImeWindowVis(),
+                            bindingController.getBackDisposition(), userId);
                     curMethod.changeInputMethodSubtype(newSubtype);
                 }
             }
@@ -3101,19 +3102,19 @@
             int lastClickToolType, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
-        int uid = Binder.getCallingUid();
+        final int uid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(uid);
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#showSoftInput", mDumper);
         synchronized (ImfLock.class) {
-            if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken)) {
+            final int userId = resolveImeUserIdLocked(callingUserId);
+            if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken,
+                    userId)) {
                 ImeTracker.forLogging().onFailed(
                         statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 return false;
             }
-            // TODO(b/305849394): Create a utility method for the following policy.
-            final int userId = mExperimentalConcurrentMultiUserModeEnabled
-                    ? UserHandle.getCallingUserId() : mCurrentUserId;
             final long ident = Binder.clearCallingIdentity();
             final var userData = getUserData(userId);
             try {
@@ -3265,13 +3266,15 @@
         try {
             ImeTracing.getInstance().triggerManagerServiceDump(
                     "InputMethodManagerService#startStylusHandwriting", mDumper);
-            int uid = Binder.getCallingUid();
+            final int uid = Binder.getCallingUid();
+            final int callingUserId = UserHandle.getUserId(uid);
             synchronized (ImfLock.class) {
+                final int userId = resolveImeUserIdLocked(callingUserId);
                 if (!acceptingDelegation) {
                     mHwController.clearPendingHandwritingDelegation();
                 }
                 if (!canInteractWithImeLocked(uid, client, "startStylusHandwriting",
-                        null /* statsToken */)) {
+                        null /* statsToken */, userId)) {
                     return false;
                 }
                 if (!hasSupportedStylusLocked()) {
@@ -3281,7 +3284,7 @@
                 }
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    final var bindingController = getInputMethodBindingController(mCurrentUserId);
+                    final var bindingController = getInputMethodBindingController(userId);
                     if (!bindingController.supportsStylusHandwriting()) {
                         Slog.w(TAG,
                                 "Stylus HW unsupported by IME. Ignoring startStylusHandwriting()");
@@ -3437,7 +3440,7 @@
                     return;
                 }
                 mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
-                updateSystemUiLocked();
+                updateSystemUiLocked(mCurrentUserId);
             }
         });
     }
@@ -3531,11 +3534,13 @@
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
             ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
-        int uid = Binder.getCallingUid();
+        final int uid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(uid);
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#hideSoftInput", mDumper);
         synchronized (ImfLock.class) {
-            if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken)) {
+            final int userId = resolveImeUserIdLocked(callingUserId);
+            if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken, userId)) {
                 if (isInputShownLocked()) {
                     ImeTracker.forLogging().onFailed(
                             statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
@@ -3545,9 +3550,6 @@
                 }
                 return false;
             }
-            // TODO(b/305849394): Create a utility method for the following policy.
-            final int userId = mExperimentalConcurrentMultiUserModeEnabled
-                    ? UserHandle.getCallingUserId() : mCurrentUserId;
             final long ident = Binder.clearCallingIdentity();
             final var userData = getUserData(userId);
             try {
@@ -3582,9 +3584,9 @@
     @Override
     @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
     public void hideSoftInputFromServerForTest() {
+        final int callingUserId = UserHandle.getCallingUserId();
         synchronized (ImfLock.class) {
-            // TODO(b/305849394): Get userId from caller.
-            final int userId = mCurrentUserId;
+            final int userId = resolveImeUserIdLocked(callingUserId);
             final var userData = getUserData(userId);
             hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
                     SoftInputShowHideReason.HIDE_SOFT_INPUT, userId);
@@ -3604,29 +3606,30 @@
     boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
             @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason, @UserIdInt int userId) {
+        final var bindingController = getInputMethodBindingController(userId);
         if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) {
             return false;
         }
 
         // There is a chance that IMM#hideSoftInput() is called in a transient state where
-        // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
-        // to be updated with the new value sent from IME process.  Even in such a transient state
-        // historically we have accepted an incoming call of IMM#hideSoftInput() from the
+        // IMMS#InputShown is already updated to be true whereas the user's ImeWindowVis is still
+        // waiting to be updated with the new value sent from IME process.  Even in such a transient
+        // state historically we have accepted an incoming call of IMM#hideSoftInput() from the
         // application process as a valid request, and have even promised such a behavior with CTS
         // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
         // IMMS#InputShown indicates that the software keyboard is shown.
         // TODO(b/246309664): Clean up IMMS#mImeWindowVis
-        final var bindingController = getInputMethodBindingController(userId);
         final var userData = getUserData(userId);
         IInputMethodInvoker curMethod = bindingController.getCurMethod();
         final boolean shouldHideSoftInput = curMethod != null
-                && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
+                && (isInputShownLocked()
+                || (bindingController.getImeWindowVis() & InputMethodService.IME_ACTIVE) != 0);
 
         mVisibilityStateComputer.requestImeVisibility(windowToken, false);
         if (shouldHideSoftInput) {
             // The IME will report its visible state again after the following message finally
             // delivered to the IME process as an IPC.  Hence the inconsistency between
-            // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
+            // IMMS#mInputShown and the user's ImeWindowVis should be resolved spontaneously in
             // the final state.
             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
             mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason,
@@ -3945,9 +3948,7 @@
 
     @GuardedBy("ImfLock.class")
     private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName,
-            @Nullable ImeTracker.Token statsToken) {
-        // TODO(b/305849394): Get userId from callers.
-        final int userId = mCurrentUserId;
+            @Nullable ImeTracker.Token statsToken, @UserIdInt int userId) {
         final var userData = getUserData(userId);
         if (userData.mCurClient == null || client == null
                 || userData.mCurClient.mClient.asBinder() != client.asBinder()) {
@@ -3994,15 +3995,14 @@
     @Override
     public void showInputMethodPickerFromClient(IInputMethodClient client,
             int auxiliarySubtypeMode) {
+        final int callingUserId = UserHandle.getCallingUserId();
         synchronized (ImfLock.class) {
             if (!canShowInputMethodPickerLocked(client)) {
                 Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
                         + Binder.getCallingUid() + ": " + client);
                 return;
             }
-            // TODO(b/305849394): Create a utility method for the following policy.
-            final int userId = mExperimentalConcurrentMultiUserModeEnabled
-                    ? UserHandle.getCallingUserId() : mCurrentUserId;
+            final int userId = resolveImeUserIdLocked(callingUserId);
             final var userData = getUserData(userId);
             // Always call subtype picker, because subtype picker is a superset of input method
             // picker.
@@ -4045,7 +4045,7 @@
             if (!calledWithValidTokenLocked(token, userId)) {
                 return;
             }
-            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
             final InputMethodInfo imi = settings.getMethodMap().get(id);
             if (imi == null || !canCallerAccessInputMethod(
                     imi.getPackageName(), callingUid, userId, settings)) {
@@ -4063,7 +4063,7 @@
             if (!calledWithValidTokenLocked(token, userId)) {
                 return;
             }
-            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
             final InputMethodInfo imi = settings.getMethodMap().get(id);
             if (imi == null || !canCallerAccessInputMethod(
                     imi.getPackageName(), callingUid, userId, settings)) {
@@ -4318,13 +4318,11 @@
         return Binder.withCleanCallingIdentity(() -> {
             final int curTokenDisplayId;
             synchronized (ImfLock.class) {
+                final int userId = resolveImeUserIdLocked(callingUserId);
                 if (!canInteractWithImeLocked(callingUid, client,
-                        "getInputMethodWindowVisibleHeight", null /* statsToken */)) {
+                        "getInputMethodWindowVisibleHeight", null /* statsToken */, userId)) {
                     return 0;
                 }
-                // TODO(b/305849394): Create a utility method for the following policy.
-                final int userId = mExperimentalConcurrentMultiUserModeEnabled
-                        ? callingUserId : mCurrentUserId;
                 final var bindingController = getInputMethodBindingController(userId);
                 // This should probably use the caller's display id, but because this is unsupported
                 // and maintained only for compatibility, there's no point in fixing it.
@@ -4453,10 +4451,12 @@
     @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
     @Override
     public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
-        int uid = Binder.getCallingUid();
+        final int uid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(uid);
         synchronized (ImfLock.class) {
+            final int userId = resolveImeUserIdLocked(callingUserId);
             if (!canInteractWithImeLocked(uid, client, "addVirtualStylusIdForTestSession",
-                    null /* statsToken */)) {
+                    null /* statsToken */, userId)) {
                 return;
             }
             final long ident = Binder.clearCallingIdentity();
@@ -4480,10 +4480,12 @@
     @Override
     public void setStylusWindowIdleTimeoutForTest(
             IInputMethodClient client, @DurationMillisLong long timeout) {
-        int uid = Binder.getCallingUid();
+        final int uid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(uid);
         synchronized (ImfLock.class) {
+            final int userId = resolveImeUserIdLocked(callingUserId);
             if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest",
-                    null /* statsToken */)) {
+                    null /* statsToken */, userId)) {
                 return;
             }
             final long ident = Binder.clearCallingIdentity();
@@ -4617,8 +4619,8 @@
             proto.write(HAVE_CONNECTION, bindingController.hasMainConnection());
             proto.write(BOUND_TO_METHOD, userData.mBoundToMethod);
             proto.write(IS_INTERACTIVE, mIsInteractive);
-            proto.write(BACK_DISPOSITION, mBackDisposition);
-            proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis);
+            proto.write(BACK_DISPOSITION, bindingController.getBackDisposition());
+            proto.write(IME_WINDOW_VISIBILITY, bindingController.getImeWindowVis());
             proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard());
             proto.end(token);
         }
@@ -5127,18 +5129,18 @@
 
     private void handleSetInteractive(final boolean interactive) {
         synchronized (ImfLock.class) {
-            mIsInteractive = interactive;
-            updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
-
             // TODO(b/305849394): Support multiple IMEs.
-            final var userId = mCurrentUserId;
+            final int userId = mCurrentUserId;
+            final var bindingController = getInputMethodBindingController(userId);
+            mIsInteractive = interactive;
+            updateSystemUiLocked(
+                    interactive ? bindingController.getImeWindowVis() : 0,
+                    bindingController.getBackDisposition(), userId);
             final var userData = getUserData(userId);
             // Inform the current client of the change in active status
             if (userData.mCurClient == null || userData.mCurClient.mClient == null) {
                 return;
             }
-            // TODO(b/325515685): user data must be retrieved by a userId parameter
-            final var bindingController = getInputMethodBindingController(mCurrentUserId);
             if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
                     bindingController.getCurMethodUid())) {
                 // Handle IME visibility when interactive changed before finishing the input to
@@ -6157,7 +6159,19 @@
                         p.println("      hasMainConnection="
                                 + u.mBindingController.hasMainConnection());
                         p.println("      isVisibleBound=" + u.mBindingController.isVisibleBound());
-                        p.println("      mSwitchingController:");
+                        p.println("      boundToMethod=" + u.mBoundToMethod);
+                        p.println("      curClient=" + u.mCurClient);
+                        if (u.mCurEditorInfo != null) {
+                            p.println("      curEditorInfo:");
+                            u.mCurEditorInfo.dump(p, "        ", false /* dumpExtras */);
+                        } else {
+                            p.println("      curEditorInfo: null");
+                        }
+                        p.println("      imeBindingState:");
+                        u.mImeBindingState.dump("        ", p);
+                        p.println("      enabledSession=" + u.mEnabledSession);
+                        p.println("      inFullscreenMode=" + u.mInFullscreenMode);
+                        p.println("      switchingController:");
                         u.mSwitchingController.dump(p, "        ");
                     };
             mUserDataRepository.forAllUserData(userDataDump);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 89a31e7..154ee1d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -201,7 +201,7 @@
         attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         attrs.setTitle("Select input method");
         w.setAttributes(attrs);
-        mService.updateSystemUiLocked();
+        mService.updateSystemUiLocked(userId);
         mService.sendOnNavButtonFlagsChangedLocked();
         mSwitchingDialog.show();
     }
@@ -239,7 +239,9 @@
             mSwitchingDialog = null;
             mSwitchingDialogTitleView = null;
 
-            mService.updateSystemUiLocked();
+            // TODO(b/305849394): Make InputMethodMenuController multi-user aware
+            final int userId = mService.getCurrentImeUserIdLocked();
+            mService.updateSystemUiLocked(userId);
             mService.sendOnNavButtonFlagsChangedLocked();
             mDialogBuilder = null;
             mIms = null;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 3051379..1a449e0 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -618,12 +618,14 @@
 
     /**
      * Processes message transactions, starting and completing them as needed.
+     * <p>
      * This function is called when adding a message transaction or when a timer
      * expires for an existing message transaction's retry or timeout. The
      * internal processing loop will iterate at most twice as if one iteration
      * completes a transaction, the next iteration can only start new transactions.
      * If the first iteration does not complete any transaction, the loop will
      * only iterate once.
+     * <p>
      */
     private synchronized void processMessageTransactions() {
         if (!Flags.reliableMessageRetrySupportService()) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e0e8a03..e215ca3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -5975,6 +5975,7 @@
     @VisibleForTesting
     void removeUserInfo(@UserIdInt int userId) {
         synchronized (mUsersLock) {
+            UserManager.invalidateUserSerialNumberCache();
             mUsers.remove(userId);
         }
     }
@@ -6400,6 +6401,7 @@
 
         // Remove this user from the list
         synchronized (mUsersLock) {
+            UserManager.invalidateUserSerialNumberCache();
             mUsers.remove(userId);
             mIsUserManaged.delete(userId);
         }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 57ea233..4d07ab5 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -807,7 +807,7 @@
                     getDefaultSystemHandlerActivityPackage(pm,
                             SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
                     userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
-                    NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS);
+                    NOTIFICATION_PERMISSIONS);
         }
 
         // Voice recognition
@@ -875,6 +875,12 @@
                     getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId,
                     SENSORS_PERMISSIONS);
             }
+
+            // Allow voice search on wear
+            grantPermissionsToSystemPackage(pm,
+                    getDefaultSystemHandlerActivityPackage(pm,
+                            SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
+                    userId, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS);
         }
 
         // Print Spooler
diff --git a/services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java b/services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java
new file mode 100644
index 0000000..e0768fe
--- /dev/null
+++ b/services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.stats.pull;
+
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Utility class to redact Battery Health data from HealthServiceWrapper
+ *
+ * @hide
+ */
+public abstract class BatteryHealthUtility {
+    /**
+     * Create a StatsEvent corresponding to the Battery Health data, the fields
+     * of which are redacted to preserve users' privacy.
+     * The redaction consists in truncating the timestamps to the Monday of the
+     * corresponding week, and reducing the battery serial into the last byte
+     * of its MD5.
+     */
+    public static StatsEvent buildStatsEvent(int atomTag,
+            android.hardware.health.BatteryHealthData data, int chargeStatus, int chargePolicy)
+            throws NoSuchAlgorithmException {
+        int manufacturingDate = secondsToWeekYYYYMMDD(data.batteryManufacturingDateSeconds);
+        int firstUsageDate = secondsToWeekYYYYMMDD(data.batteryFirstUsageSeconds);
+        long stateOfHealth = data.batteryStateOfHealth;
+        int partStatus = data.batteryPartStatus;
+        int serialHashTruncated = stringToIntHash(data.batterySerialNumber) & 0xFF; // Last byte
+
+        return FrameworkStatsLog.buildStatsEvent(atomTag, manufacturingDate, firstUsageDate,
+                (int) stateOfHealth, serialHashTruncated, partStatus, chargeStatus, chargePolicy);
+    }
+
+    private static int secondsToWeekYYYYMMDD(long seconds) {
+        Calendar calendar = Calendar.getInstance();
+        long millis = seconds * 1000L;
+
+        calendar.setTimeInMillis(millis);
+
+        // Truncate all date information, up to week, which is rounded to
+        // MONDAY
+        calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.US);
+
+        String formattedDate = sdf.format(calendar.getTime());
+
+        return Integer.parseInt(formattedDate);
+    }
+
+    private static int stringToIntHash(String data) throws NoSuchAlgorithmException {
+        if (data == null || data.isEmpty()) {
+            return 0;
+        }
+
+        MessageDigest digest = MessageDigest.getInstance("MD5");
+        byte[] hashBytes = digest.digest(data.getBytes());
+
+        // Convert to integer (simplest way, but potential for loss of information)
+        BigInteger bigInt = new BigInteger(1, hashBytes);
+        return bigInt.intValue();
+    }
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index c1b825b..0041d39 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -119,6 +119,8 @@
 import android.net.NetworkTemplate;
 import android.net.wifi.WifiManager;
 import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.BatteryStatsManager;
@@ -243,6 +245,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.security.NoSuchAlgorithmException;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
@@ -769,6 +772,7 @@
                     case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
                     case FrameworkStatsLog.BATTERY_VOLTAGE:
                     case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
+                    case FrameworkStatsLog.BATTERY_HEALTH:
                         synchronized (mHealthHalLock) {
                             return pullHealthHalLocked(atomTag, data);
                         }
@@ -999,6 +1003,7 @@
         registerFullBatteryCapacity();
         registerBatteryVoltage();
         registerBatteryCycleCount();
+        registerBatteryHealth();
         registerSettingsStats();
         registerInstalledIncrementalPackages();
         registerKeystoreStorageStats();
@@ -4365,7 +4370,15 @@
         );
     }
 
-    int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) {
+    private void registerBatteryHealth() {
+        int tagId = FrameworkStatsLog.BATTERY_HEALTH;
+        mStatsManager.setPullAtomCallback(tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR, mStatsCallbackImpl);
+    }
+
+    @GuardedBy("mHealthHalLock")
+    private int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) {
         if (mHealthService == null) {
             return StatsManager.PULL_SKIP;
         }
@@ -4396,6 +4409,44 @@
             case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
                 pulledValue = healthInfo.batteryCycleCount;
                 break;
+            case FrameworkStatsLog.BATTERY_HEALTH:
+                android.hardware.health.BatteryHealthData bhd;
+                try {
+                    bhd = mHealthService.getBatteryHealthData();
+                } catch (RemoteException | IllegalStateException e) {
+                    return StatsManager.PULL_SKIP;
+                }
+                if (bhd == null) {
+                    return StatsManager.PULL_SKIP;
+                }
+
+                StatsEvent batteryHealthEvent;
+                try {
+                    BatteryProperty chargeStatusProperty = new BatteryProperty();
+                    BatteryProperty chargePolicyProperty = new BatteryProperty();
+
+                    if (0 > mHealthService.getProperty(
+                                BatteryManager.BATTERY_PROPERTY_STATUS, chargeStatusProperty)) {
+                        return StatsManager.PULL_SKIP;
+                    }
+                    if (0 > mHealthService.getProperty(
+                                BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY,
+                                chargePolicyProperty)) {
+                        return StatsManager.PULL_SKIP;
+                    }
+                    int chargeStatus = (int) chargeStatusProperty.getLong();
+                    int chargePolicy = (int) chargePolicyProperty.getLong();
+                    batteryHealthEvent = BatteryHealthUtility.buildStatsEvent(
+                            atomTag, bhd, chargeStatus, chargePolicy);
+                    pulledData.add(batteryHealthEvent);
+
+                    return StatsManager.PULL_SUCCESS;
+                } catch (RemoteException | IllegalStateException e) {
+                    Slog.e(TAG, "Failed to add pulled data", e);
+                } catch (NoSuchAlgorithmException e) {
+                    Slog.e(TAG, "Could not find message digest algorithm", e);
+                }
+                return StatsManager.PULL_SKIP;
             default:
                 return StatsManager.PULL_SKIP;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 3e177c9..a8dcaa8 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -102,7 +102,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
 import com.android.server.pm.KnownPackages;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dce496d..5f19924 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -386,7 +386,7 @@
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.AttributeCache;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -8179,8 +8179,7 @@
     }
 
     void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
-        if (mAppCompatController.getAppCompatOverrides()
-                .getAppCompatOrientationOverrides()
+        if (mAppCompatController.getAppCompatOrientationOverrides()
                 .shouldIgnoreRequestedOrientation(requestedOrientation)) {
             return;
         }
@@ -8559,7 +8558,7 @@
         final int parentWindowingMode =
                 newParentConfiguration.windowConfiguration.getWindowingMode();
         final boolean isInCameraCompatFreeform = parentWindowingMode == WINDOWING_MODE_FREEFORM
-                && mLetterboxUiController.getFreeformCameraCompatMode()
+                && mAppCompatController.getAppCompatCameraOverrides().getFreeformCameraCompatMode()
                         != CAMERA_COMPAT_FREEFORM_NONE;
 
         // Bubble activities should always fill their parent and should not be letterboxed.
@@ -9887,7 +9886,8 @@
             return mLetterboxUiController.getUserMinAspectRatio();
         }
         if (!mLetterboxUiController.shouldOverrideMinAspectRatio()
-                && !mLetterboxUiController.shouldOverrideMinAspectRatioForCamera()) {
+                && !mAppCompatController.getAppCompatCameraOverrides()
+                    .shouldOverrideMinAspectRatioForCamera()) {
             return info.getMinAspectRatio();
         }
         if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
@@ -10814,15 +10814,18 @@
         proto.write(OVERRIDE_ORIENTATION, getOverrideOrientation());
         proto.write(SHOULD_SEND_COMPAT_FAKE_FOCUS, shouldSendCompatFakeFocus());
         proto.write(SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT,
-                mLetterboxUiController.shouldForceRotateForCameraCompat());
+                mAppCompatController.getAppCompatCameraOverrides()
+                        .shouldForceRotateForCameraCompat());
         proto.write(SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT,
-                mLetterboxUiController.shouldRefreshActivityForCameraCompat());
+                mAppCompatController.getAppCompatCameraOverrides()
+                        .shouldRefreshActivityForCameraCompat());
         proto.write(SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT,
-                mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat());
+                mAppCompatController.getAppCompatCameraOverrides()
+                        .shouldRefreshActivityViaPauseForCameraCompat());
         proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO,
                 mLetterboxUiController.shouldOverrideMinAspectRatio());
         proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
-                mAppCompatController.getAppCompatOverrides().getAppCompatOrientationOverrides()
+                mAppCompatController.getAppCompatOrientationOverrides()
                         .shouldIgnoreOrientationRequestLoop());
         proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
                 mLetterboxUiController.shouldOverrideForceResizeApp());
diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java
index 23a9708..0c32dfc 100644
--- a/services/core/java/com/android/server/wm/ActivityRefresher.java
+++ b/services/core/java/com/android/server/wm/ActivityRefresher.java
@@ -27,7 +27,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
@@ -77,10 +77,10 @@
         final boolean cycleThroughStop =
                 mWmService.mLetterboxConfiguration
                         .isCameraCompatRefreshCycleThroughStopEnabled()
-                        && !activity.mLetterboxUiController
-                        .shouldRefreshActivityViaPauseForCameraCompat();
+                        && !activity.mAppCompatController.getAppCompatCameraOverrides()
+                            .shouldRefreshActivityViaPauseForCameraCompat();
 
-        activity.mLetterboxUiController.setIsRefreshRequested(true);
+        activity.mAppCompatController.getAppCompatCameraOverrides().setIsRefreshRequested(true);
         ProtoLog.v(WM_DEBUG_STATES,
                 "Refreshing activity for freeform camera compatibility treatment, "
                         + "activityRecord=%s", activity);
@@ -97,24 +97,26 @@
                 }
             }, REFRESH_CALLBACK_TIMEOUT_MS);
         } catch (RemoteException e) {
-            activity.mLetterboxUiController.setIsRefreshRequested(false);
+            activity.mAppCompatController.getAppCompatCameraOverrides()
+                    .setIsRefreshRequested(false);
         }
     }
 
     boolean isActivityRefreshing(@NonNull ActivityRecord activity) {
-        return activity.mLetterboxUiController.isRefreshRequested();
+        return activity.mAppCompatController.getAppCompatCameraOverrides().isRefreshRequested();
     }
 
     void onActivityRefreshed(@NonNull ActivityRecord activity) {
         // TODO(b/333060789): can we tell that refresh did not happen by observing the activity
         //  state?
-        activity.mLetterboxUiController.setIsRefreshRequested(false);
+        activity.mAppCompatController.getAppCompatCameraOverrides().setIsRefreshRequested(false);
     }
 
     private boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
             @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
         return mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled()
-                && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()
+                && activity.mAppCompatController.getAppCompatOverrides()
+                    .getAppCompatCameraOverrides().shouldRefreshActivityForCameraCompat()
                 && ArrayUtils.find(mEvaluators.toArray(), evaluator ->
                 ((Evaluator) evaluator)
                         .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 717c399..5bfe9d7 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -127,7 +127,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
 import com.android.server.pm.PackageArchiver;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2109f5d..ded205e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -260,7 +260,7 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.policy.KeyguardDismissCallback;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
@@ -6192,11 +6192,14 @@
             synchronized (mGlobalLockWithoutBoost) {
                 final WindowProcessController proc = mProcessNames.remove(name, uid);
                 if (proc != null && !mStartingProcessActivities.isEmpty()) {
-                    for (int i = mStartingProcessActivities.size() - 1; i >= 0; i--) {
-                        final ActivityRecord r = mStartingProcessActivities.get(i);
+                    // Use a copy in case finishIfPossible changes the list indirectly.
+                    final ArrayList<ActivityRecord> activities =
+                            new ArrayList<>(mStartingProcessActivities);
+                    for (int i = activities.size() - 1; i >= 0; i--) {
+                        final ActivityRecord r = activities.get(i);
                         if (uid == r.info.applicationInfo.uid && name.equals(r.processName)) {
                             Slog.w(TAG, proc + " is removed with pending start " + r);
-                            mStartingProcessActivities.remove(i);
+                            mStartingProcessActivities.remove(r);
                             // If visible, finish it to avoid getting stuck on screen.
                             if (r.isVisibleRequested()) {
                                 r.finishIfPossible("starting-proc-removed", false /* oomAdj */);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index cd5576f..d65a106 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -148,7 +148,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 0013c5c..80671ef 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -345,7 +345,7 @@
                     null /* processCpuTracker */, null /* lastPids */,
                     CompletableFuture.completedFuture(nativePids),
                     null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents,
-                    Runnable::run, null/* AnrLatencyTracker */);
+                    null /* extraHeaders */, Runnable::run, null/* AnrLatencyTracker */);
             if (tracesFile != null) {
                 tracesFile.renameTo(
                         new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
new file mode 100644
index 0000000..c0e5005
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
+
+import com.android.server.wm.utils.OptPropFactory;
+import com.android.window.flags.Flags;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Encapsulates app compat configurations and overrides related to camera.
+ */
+class AppCompatCameraOverrides {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME
+            ? "AppCompatCameraOverrides" : TAG_ATM;
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final AppCompatCameraOverridesState mAppCompatCameraOverridesState;
+    @NonNull
+    private final LetterboxConfiguration mLetterboxConfiguration;
+    @NonNull
+    private final OptPropFactory.OptProp mAllowMinAspectRatioOverrideOptProp;
+    @NonNull
+    private final OptPropFactory.OptProp mCameraCompatAllowRefreshOptProp;
+    @NonNull
+    private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp;
+    @NonNull
+    private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp;
+
+    AppCompatCameraOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull LetterboxConfiguration letterboxConfiguration,
+            @NonNull OptPropFactory optPropBuilder) {
+        mActivityRecord = activityRecord;
+        mLetterboxConfiguration = letterboxConfiguration;
+        mAppCompatCameraOverridesState = new AppCompatCameraOverridesState();
+        mAllowMinAspectRatioOverrideOptProp = optPropBuilder.create(
+                PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+        final BooleanSupplier isCameraCompatTreatmentEnabled = AppCompatUtils.asLazy(
+                mLetterboxConfiguration::isCameraCompatTreatmentEnabled);
+        mCameraCompatAllowRefreshOptProp = optPropBuilder.create(
+                PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH,
+                isCameraCompatTreatmentEnabled);
+        mCameraCompatEnableRefreshViaPauseOptProp = optPropBuilder.create(
+                PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
+                isCameraCompatTreatmentEnabled);
+        mCameraCompatAllowForceRotationOptProp = optPropBuilder.create(
+                PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION,
+                isCameraCompatTreatmentEnabled);
+    }
+
+    /**
+     * Whether we should apply the min aspect ratio per-app override only when an app is connected
+     * to the camera.
+     * When this override is applied the min aspect ratio given in the app's manifest will be
+     * overridden to the largest enabled aspect ratio treatment unless the app's manifest value
+     * is higher. The treatment will also apply if no value is provided in the manifest.
+     *
+     * <p>This method returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Per-app override is enabled
+     * </ul>
+     */
+    boolean shouldOverrideMinAspectRatioForCamera() {
+        return mActivityRecord.isCameraActive()
+                && mAllowMinAspectRatioOverrideOptProp
+                .shouldEnableWithOptInOverrideAndOptOutProperty(
+                        isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
+    }
+
+    /**
+     * Whether activity is eligible for activity "refresh" after camera compat force rotation
+     * treatment. See {@link DisplayRotationCompatPolicy} for context.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Flag gating the camera compat treatment is enabled.
+     *     <li>Activity isn't opted out by the device manufacturer with override or by the app
+     *     developers with the component property.
+     * </ul>
+     */
+    boolean shouldRefreshActivityForCameraCompat() {
+        return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty(
+                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH));
+    }
+
+    /**
+     * Whether activity should be "refreshed" after the camera compat force rotation treatment
+     * using the "resumed -> paused -> resumed" cycle rather than the "resumed -> ... -> stopped
+     * -> ... -> resumed" cycle. See {@link DisplayRotationCompatPolicy} for context.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Flag gating the camera compat treatment is enabled.
+     *     <li>Activity "refresh" via "resumed -> paused -> resumed" cycle isn't disabled with the
+     *     component property by the app developers.
+     *     <li>Activity "refresh" via "resumed -> paused -> resumed" cycle is enabled by the device
+     *     manufacturer with override / by the app developers with the component property.
+     * </ul>
+     */
+    boolean shouldRefreshActivityViaPauseForCameraCompat() {
+        return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty(
+                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE));
+    }
+
+    /**
+     * Whether activity is eligible for camera compat force rotation treatment. See {@link
+     * DisplayRotationCompatPolicy} for context.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Flag gating the camera compat treatment is enabled.
+     *     <li>Activity isn't opted out by the device manufacturer with override or by the app
+     *     developers with the component property.
+     * </ul>
+     */
+    boolean shouldForceRotateForCameraCompat() {
+        return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty(
+                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION));
+    }
+
+    /**
+     * Whether activity is eligible for camera compatibility free-form treatment.
+     *
+     * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
+     * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
+     * provides changes to the camera and display orientation signals to match those expected on a
+     * portrait device in that orientation (for example, on a standard phone).
+     *
+     * <p>The treatment is enabled when the following conditions are met:
+     * <ul>
+     * <li>Property gating the camera compatibility free-form treatment is enabled.
+     * <li>Activity isn't opted out by the device manufacturer with override.
+     * </ul>
+     */
+    boolean shouldApplyFreeformTreatmentForCameraCompat() {
+        return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled(
+                OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
+    }
+
+    /**
+     * @return {@code true} if the configuration needs to be recomputed after a camera state update.
+     */
+    boolean shouldRecomputeConfigurationForCameraCompat() {
+        return isOverrideOrientationOnlyForCameraEnabled()
+                || isCameraCompatSplitScreenAspectRatioAllowed()
+                || shouldOverrideMinAspectRatioForCamera();
+    }
+
+    boolean isOverrideOrientationOnlyForCameraEnabled() {
+        return isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
+    }
+
+    /**
+     * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}.
+     */
+    boolean isRefreshRequested() {
+        return mAppCompatCameraOverridesState.mIsRefreshRequested;
+    }
+
+    /**
+     * @param isRequested Whether activity "refresh" was requested but not finished
+     *                    in {@link #activityResumedLocked}.
+     */
+    void setIsRefreshRequested(boolean isRequested) {
+        mAppCompatCameraOverridesState.mIsRefreshRequested = isRequested;
+    }
+
+    /**
+     * Whether we use split screen aspect ratio for the activity when camera compat treatment
+     * is active because the corresponding config is enabled and activity supports resizing.
+     */
+    boolean isCameraCompatSplitScreenAspectRatioAllowed() {
+        return mLetterboxConfiguration.isCameraCompatSplitScreenAspectRatioEnabled()
+                && !mActivityRecord.shouldCreateCompatDisplayInsets();
+    }
+
+    @FreeformCameraCompatMode
+    int getFreeformCameraCompatMode() {
+        return mAppCompatCameraOverridesState.mFreeformCameraCompatMode;
+    }
+
+    void setFreeformCameraCompatMode(@FreeformCameraCompatMode int freeformCameraCompatMode) {
+        mAppCompatCameraOverridesState.mFreeformCameraCompatMode = freeformCameraCompatMode;
+    }
+
+    private boolean isCompatChangeEnabled(long overrideChangeId) {
+        return mActivityRecord.info.isChangeEnabled(overrideChangeId);
+    }
+
+    static class AppCompatCameraOverridesState {
+        // Whether activity "refresh" was requested but not finished in
+        // ActivityRecord#activityResumedLocked following the camera compat force rotation in
+        // DisplayRotationCompatPolicy.
+        private boolean mIsRefreshRequested;
+
+        @FreeformCameraCompatMode
+        private int mFreeformCameraCompatMode = CAMERA_COMPAT_FREEFORM_NONE;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
new file mode 100644
index 0000000..ee523a2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+
+/**
+ * Encapsulate the app compat logic related to camera.
+ */
+class AppCompatCameraPolicy {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME
+            ? "AppCompatCameraPolicy" : TAG_ATM;
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+
+    @NonNull
+    private final AppCompatCameraOverrides mAppCompatCameraOverrides;
+
+    AppCompatCameraPolicy(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatCameraOverrides appCompatCameraOverrides) {
+        mActivityRecord = activityRecord;
+        mAppCompatCameraOverrides = appCompatCameraOverrides;
+    }
+
+    void recomputeConfigurationForCameraCompatIfNeeded() {
+        if (mAppCompatCameraOverrides.shouldRecomputeConfigurationForCameraCompat()) {
+            mActivityRecord.recomputeConfiguration();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 4b0d739..d8c0c17 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -16,6 +16,9 @@
 package com.android.server.wm;
 
 import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+
+import com.android.server.wm.utils.OptPropFactory;
 
 /**
  * Allows the interaction with all the app compat policies and configurations
@@ -28,19 +31,26 @@
     private final AppCompatOrientationPolicy mOrientationPolicy;
     @NonNull
     private final AppCompatOverrides mAppCompatOverrides;
+    @NonNull
+    private final AppCompatCameraPolicy mAppCompatCameraPolicy;
 
     AppCompatController(@NonNull WindowManagerService wmService,
                         @NonNull ActivityRecord activityRecord) {
+        final PackageManager packageManager = wmService.mContext.getPackageManager();
+        final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
+                activityRecord.packageName);
         mTransparentPolicy = new TransparentPolicy(activityRecord,
                 wmService.mLetterboxConfiguration);
-        mAppCompatOverrides = new AppCompatOverrides(wmService, activityRecord,
-                wmService.mLetterboxConfiguration);
+        mAppCompatOverrides = new AppCompatOverrides(activityRecord,
+                wmService.mLetterboxConfiguration, optPropBuilder);
         // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with aspectRatio.
         final LetterboxUiController tmpController = activityRecord.mLetterboxUiController;
         mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord,
                 mAppCompatOverrides, tmpController::shouldApplyUserFullscreenOverride,
                 tmpController::shouldApplyUserMinAspectRatioOverride,
                 tmpController::isSystemOverrideToFullscreenEnabled);
+        mAppCompatCameraPolicy = new AppCompatCameraPolicy(activityRecord,
+                mAppCompatOverrides.getAppCompatCameraOverrides());
     }
 
     @NonNull
@@ -54,7 +64,22 @@
     }
 
     @NonNull
+    AppCompatCameraPolicy getAppCompatCameraPolicy() {
+        return mAppCompatCameraPolicy;
+    }
+
+    @NonNull
     AppCompatOverrides getAppCompatOverrides() {
         return mAppCompatOverrides;
     }
+
+    @NonNull
+    AppCompatOrientationOverrides getAppCompatOrientationOverrides() {
+        return mAppCompatOverrides.getAppCompatOrientationOverrides();
+    }
+
+    @NonNull
+    AppCompatCameraOverrides getAppCompatCameraOverrides() {
+        return mAppCompatOverrides.getAppCompatCameraOverrides();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index 446a75c..b0fdbb5 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -28,7 +28,7 @@
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.AppCompatOverrides.asLazy;
+import static com.android.server.wm.AppCompatUtils.asLazy;
 
 import android.annotation.NonNull;
 import android.content.pm.ActivityInfo;
@@ -60,9 +60,9 @@
     @NonNull
     final OrientationOverridesState mOrientationOverridesState;
 
-    AppCompatOrientationOverrides(@NonNull OptPropFactory optPropBuilder,
-                                   @NonNull LetterboxConfiguration letterboxConfiguration,
-                                   @NonNull ActivityRecord activityRecord) {
+    AppCompatOrientationOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull LetterboxConfiguration letterboxConfiguration,
+            @NonNull OptPropFactory optPropBuilder) {
         mActivityRecord = activityRecord;
         mOrientationOverridesState = new OrientationOverridesState(mActivityRecord,
                 System::currentTimeMillis);
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 8e9a9e9..960ef5a 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -103,11 +103,11 @@
             return candidate;
         }
 
-        if (displayContent != null && mAppCompatOverrides
+        if (displayContent != null && mAppCompatOverrides.getAppCompatCameraOverrides()
                 .isOverrideOrientationOnlyForCameraEnabled()
-                && (displayContent.mDisplayRotationCompatPolicy == null
-                || !displayContent.mDisplayRotationCompatPolicy
-                .isActivityEligibleForOrientationOverride(mActivityRecord))) {
+                    && (displayContent.mDisplayRotationCompatPolicy == null
+                    || !displayContent.mDisplayRotationCompatPolicy
+                        .isActivityEligibleForOrientationOverride(mActivityRecord))) {
             return candidate;
         }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 794008a..c20da7c 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -19,22 +19,13 @@
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
-import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
 import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
@@ -47,12 +38,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.NonNull;
-import android.content.pm.PackageManager;
 
 import com.android.server.wm.utils.OptPropFactory;
-import com.android.window.flags.Flags;
-
-import java.util.function.BooleanSupplier;
 
 /**
  * Encapsulate logic related to operations guarded by an app override.
@@ -70,12 +57,6 @@
     @NonNull
     private final OptPropFactory.OptProp mFakeFocusOptProp;
     @NonNull
-    private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp;
-    @NonNull
-    private final OptPropFactory.OptProp mCameraCompatAllowRefreshOptProp;
-    @NonNull
-    private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp;
-    @NonNull
     private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp;
     @NonNull
     private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp;
@@ -87,37 +68,25 @@
     private final OptPropFactory.OptProp mAllowUserAspectRatioOverrideOptProp;
     @NonNull
     private final OptPropFactory.OptProp mAllowUserAspectRatioFullscreenOverrideOptProp;
-
+    @NonNull
     private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
+    @NonNull
+    private final AppCompatCameraOverrides mAppCompatCameraOverrides;
 
-    AppCompatOverrides(@NonNull WindowManagerService wmService,
-                        @NonNull ActivityRecord activityRecord,
-                        @NonNull LetterboxConfiguration letterboxConfiguration) {
+    AppCompatOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull LetterboxConfiguration letterboxConfiguration,
+            @NonNull OptPropFactory optPropBuilder) {
         mLetterboxConfiguration = letterboxConfiguration;
         mActivityRecord = activityRecord;
-        final PackageManager packageManager = wmService.mContext.getPackageManager();
 
-        final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
-                activityRecord.packageName);
-
-        mAppCompatOrientationOverrides =
-                new AppCompatOrientationOverrides(optPropBuilder, mLetterboxConfiguration,
-                        mActivityRecord);
+        mAppCompatOrientationOverrides = new AppCompatOrientationOverrides(mActivityRecord,
+                mLetterboxConfiguration, optPropBuilder);
+        mAppCompatCameraOverrides = new AppCompatCameraOverrides(mActivityRecord,
+                mLetterboxConfiguration, optPropBuilder);
 
         mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
                 mLetterboxConfiguration::isCompatFakeFocusEnabled);
 
-        final BooleanSupplier isCameraCompatTreatmentEnabled = asLazy(
-                mLetterboxConfiguration::isCameraCompatTreatmentEnabled);
-        mCameraCompatAllowForceRotationOptProp = optPropBuilder.create(
-                PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION,
-                isCameraCompatTreatmentEnabled);
-        mCameraCompatAllowRefreshOptProp = optPropBuilder.create(
-                PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH,
-                isCameraCompatTreatmentEnabled);
-        mCameraCompatEnableRefreshViaPauseOptProp = optPropBuilder.create(
-                PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
-                isCameraCompatTreatmentEnabled);
 
         mAllowOrientationOverrideOptProp = optPropBuilder.create(
                 PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
@@ -162,6 +131,11 @@
         return mAppCompatOrientationOverrides;
     }
 
+    @NonNull
+    AppCompatCameraOverrides getAppCompatCameraOverrides() {
+        return mAppCompatCameraOverrides;
+    }
+
     /**
      * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
      * because some game engines wait to get focus before drawing the content of the app which isn't
@@ -179,57 +153,6 @@
                 isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS));
     }
 
-    /**
-     * Whether activity is eligible for camera compat force rotation treatment. See {@link
-     * DisplayRotationCompatPolicy} for context.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the camera compat treatment is enabled.
-     *     <li>Activity isn't opted out by the device manufacturer with override or by the app
-     *     developers with the component property.
-     * </ul>
-     */
-    boolean shouldForceRotateForCameraCompat() {
-        return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION));
-    }
-
-    /**
-     * Whether activity is eligible for activity "refresh" after camera compat force rotation
-     * treatment. See {@link DisplayRotationCompatPolicy} for context.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the camera compat treatment is enabled.
-     *     <li>Activity isn't opted out by the device manufacturer with override or by the app
-     *     developers with the component property.
-     * </ul>
-     */
-    boolean shouldRefreshActivityForCameraCompat() {
-        return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH));
-    }
-
-    /**
-     * Whether activity should be "refreshed" after the camera compat force rotation treatment
-     * using the "resumed -> paused -> resumed" cycle rather than the "resumed -> ... -> stopped
-     * -> ... -> resumed" cycle. See {@link DisplayRotationCompatPolicy} for context.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the camera compat treatment is enabled.
-     *     <li>Activity "refresh" via "resumed -> paused -> resumed" cycle isn't disabled with the
-     *     component property by the app developers.
-     *     <li>Activity "refresh" via "resumed -> paused -> resumed" cycle is enabled by the device
-     *     manufacturer with override / by the app developers with the component property.
-     * </ul>
-     */
-    boolean shouldRefreshActivityViaPauseForCameraCompat() {
-        return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE));
-    }
-
     boolean isSystemOverrideToFullscreenEnabled(int userAspectRatio) {
         return isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER)
                 && !mAllowOrientationOverrideOptProp.isFalse()
@@ -262,50 +185,6 @@
         return isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
     }
 
-    boolean isOverrideOrientationOnlyForCameraEnabled() {
-        return isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
-    }
-
-    /**
-     * Whether activity is eligible for camera compatibility free-form treatment.
-     *
-     * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
-     * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
-     * provides changes to the camera and display orientation signals to match those expected on a
-     * portrait device in that orientation (for example, on a standard phone).
-     *
-     * <p>The treatment is enabled when the following conditions are met:
-     * <ul>
-     * <li>Property gating the camera compatibility free-form treatment is enabled.
-     * <li>Activity isn't opted out by the device manufacturer with override.
-     * </ul>
-     */
-    boolean shouldApplyFreeformTreatmentForCameraCompat() {
-        return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled(
-                OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
-    }
-
-
-    /**
-     * Whether we should apply the min aspect ratio per-app override only when an app is connected
-     * to the camera.
-     * When this override is applied the min aspect ratio given in the app's manifest will be
-     * overridden to the largest enabled aspect ratio treatment unless the app's manifest value
-     * is higher. The treatment will also apply if no value is provided in the manifest.
-     *
-     * <p>This method returns {@code true} when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Per-app override is enabled
-     * </ul>
-     */
-    boolean shouldOverrideMinAspectRatioForCamera() {
-        return mActivityRecord.isCameraActive()
-                && mAllowMinAspectRatioOverrideOptProp
-                    .shouldEnableWithOptInOverrideAndOptOutProperty(
-                        isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
-    }
-
     /**
      * Whether should fix display orientation to landscape natural orientation when a task is
      * fullscreen and the display is ignoring orientation requests.
@@ -382,21 +261,4 @@
     private boolean isCompatChangeEnabled(long overrideChangeId) {
         return mActivityRecord.info.isChangeEnabled(overrideChangeId);
     }
-
-    @NonNull
-    static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) {
-        return new BooleanSupplier() {
-            private boolean mRead;
-            private boolean mValue;
-
-            @Override
-            public boolean getAsBoolean() {
-                if (!mRead) {
-                    mRead = true;
-                    mValue = supplier.getAsBoolean();
-                }
-                return mValue;
-            }
-        };
-    }
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
new file mode 100644
index 0000000..be51dd3b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Utilities for App Compat policies and overrides.
+ */
+class AppCompatUtils {
+
+    /**
+     * Lazy version of a {@link BooleanSupplier} which access an existing BooleanSupplier and
+     * caches the value.
+     *
+     * @param supplier The BooleanSupplier to decorate.
+     * @return A lazy implementation of a BooleanSupplier
+     */
+    @NonNull
+    static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) {
+        return new BooleanSupplier() {
+            private boolean mRead;
+            private boolean mValue;
+
+            @Override
+            public boolean getAsBoolean() {
+                if (!mRead) {
+                    mRead = true;
+                    mValue = supplier.getAsBoolean();
+                }
+                return mValue;
+            }
+        };
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c2b9128..bc7e84a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -135,7 +135,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index c55a100..44b414f 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -92,7 +92,7 @@
 import android.window.ITaskFragmentOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index a8cc2ae..4554b21 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -30,7 +30,7 @@
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.ArrayList;
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 14ae918..cb69039 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -61,7 +61,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 0b2c851..ba4ab7d 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -22,7 +22,7 @@
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.function.Supplier;
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 0c751cf..68a4172 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -32,7 +32,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 
 /**
@@ -110,12 +110,14 @@
         if (!isTreatmentEnabledForActivity(cameraActivity)) {
             return false;
         }
-        final int existingCameraCompatMode =
-                cameraActivity.mLetterboxUiController.getFreeformCameraCompatMode();
+        final int existingCameraCompatMode = cameraActivity.mAppCompatController
+                .getAppCompatCameraOverrides()
+                        .getFreeformCameraCompatMode();
         final int newCameraCompatMode = getCameraCompatMode(cameraActivity);
         if (newCameraCompatMode != existingCameraCompatMode) {
             mIsCameraCompatTreatmentPending = true;
-            cameraActivity.mLetterboxUiController.setFreeformCameraCompatMode(newCameraCompatMode);
+            cameraActivity.mAppCompatController.getAppCompatCameraOverrides()
+                    .setFreeformCameraCompatMode(newCameraCompatMode);
             forceUpdateActivityAndTask(cameraActivity);
             return true;
         } else {
@@ -134,8 +136,8 @@
                     mDisplayContent.mDisplayId, cameraId);
             return false;
         }
-        cameraActivity.mLetterboxUiController.setFreeformCameraCompatMode(
-                CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE);
+        cameraActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE);
         forceUpdateActivityAndTask(cameraActivity);
         mIsCameraCompatTreatmentPending = false;
         return true;
@@ -191,6 +193,6 @@
                 || mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) {
             return false;
         }
-        return topActivity.mLetterboxUiController.isRefreshRequested();
+        return topActivity.mAppCompatController.getAppCompatCameraOverrides().isRefreshRequested();
     }
 }
diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java
index ea7edea..a54141c 100644
--- a/services/core/java/com/android/server/wm/CameraStateMonitor.java
+++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java
@@ -26,7 +26,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index b795987f..44202a2 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -48,7 +48,7 @@
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index d70a880..7e7073c 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -42,7 +42,7 @@
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.display.feature.DisplayManagerFlags;
 
 /**
diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java
index b589085..283f819 100644
--- a/services/core/java/com/android/server/wm/ContentRecordingController.java
+++ b/services/core/java/com/android/server/wm/ContentRecordingController.java
@@ -23,7 +23,7 @@
 import android.view.ContentRecordingSession;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 /**
  * Orchestrates the handoff between displays if the recording session changes, and keeps track of
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index be44629..3b99954 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -36,7 +36,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater;
 import com.android.window.flags.Flags;
 
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 735c73a..22fa88f 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -29,7 +29,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 0006bd2..def495f 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -45,7 +45,7 @@
 import android.window.IDisplayAreaOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 3dc3be9..8f471d7 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -33,7 +33,7 @@
 import android.window.IDisplayAreaOrganizerController;
 import android.window.WindowContainerToken;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 8c52288..bb596cc 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -42,7 +42,7 @@
 
 import com.android.internal.annotations.Keep;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7ffffe9..b5b9377 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -254,7 +254,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4a59fc2..b36fbd3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -132,7 +132,7 @@
 import com.android.internal.policy.ForceShowNavBarSettingsObserver;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.statusbar.LetterboxDetails;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.ScreenshotRequest;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index afcf364..f3ccc3b 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -80,7 +80,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 6ecafdb..3d71e95 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -42,7 +42,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.UiThread;
 
 /**
@@ -228,9 +228,11 @@
                 != lastReportedConfig.windowConfiguration.getDisplayRotation());
         return isTreatmentEnabledForDisplay()
                 && isTreatmentEnabledForActivity(activity)
-                && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()
+                && activity.mAppCompatController.getAppCompatCameraOverrides()
+                    .shouldRefreshActivityForCameraCompat()
                 && (displayRotationChanged
-                || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed());
+                || activity.mAppCompatController.getAppCompatCameraOverrides()
+                        .isCameraCompatSplitScreenAspectRatioAllowed());
     }
 
     /**
@@ -254,7 +256,8 @@
     boolean isActivityEligibleForOrientationOverride(@NonNull ActivityRecord activity) {
         return isTreatmentEnabledForDisplay()
                 && isCameraActive(activity, /* mustBeFullscreen */ true)
-                && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
+                && activity.mAppCompatController.getAppCompatCameraOverrides()
+                    .shouldForceRotateForCameraCompat();
     }
 
     /**
@@ -286,7 +289,8 @@
                 // handle dynamic changes so we shouldn't force rotate them.
                 && activity.getOverrideOrientation() != SCREEN_ORIENTATION_NOSENSOR
                 && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED
-                && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
+                && activity.mAppCompatController.getAppCompatCameraOverrides()
+                    .shouldForceRotateForCameraCompat();
     }
 
     @Override
@@ -295,7 +299,8 @@
         // Checking whether an activity in fullscreen rather than the task as this camera
         // compat treatment doesn't cover activity embedding.
         if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-            cameraActivity.mLetterboxUiController.recomputeConfigurationForCameraCompatIfNeeded();
+            cameraActivity.mAppCompatController
+                    .getAppCompatCameraPolicy().recomputeConfigurationForCameraCompatIfNeeded();
             mDisplayContent.updateOrientation();
             return true;
         }
@@ -362,7 +367,8 @@
                 || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
             return true;
         }
-        topActivity.mLetterboxUiController.recomputeConfigurationForCameraCompatIfNeeded();
+        topActivity.mAppCompatController
+                .getAppCompatCameraPolicy().recomputeConfigurationForCameraCompatIfNeeded();
         mDisplayContent.updateOrientation();
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
index 50b29ec..f94b8c4 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
@@ -26,7 +26,7 @@
 import android.content.ActivityInfoProto;
 import android.view.Surface;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 /**
  * Defines the behavior of reversion from device rotation overrides.
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index c79565a..8bd8098 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.UserHandle.USER_SYSTEM;
 import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
@@ -27,6 +28,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.WindowConfiguration;
 import android.os.Environment;
 import android.util.ArrayMap;
@@ -42,6 +44,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.wm.DisplayWindowSettings.SettingsProvider;
+import com.android.window.flags.Flags;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -53,6 +56,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Implementation of {@link SettingsProvider} that reads the base settings provided in a display
@@ -91,11 +95,11 @@
     @NonNull
     private ReadableSettings mBaseSettings;
     @NonNull
-    private final WritableSettings mOverrideSettings;
+    private WritableSettings mOverrideSettings;
 
     DisplayWindowSettingsProvider() {
         this(new AtomicFileStorage(getVendorSettingsFile()),
-                new AtomicFileStorage(getOverrideSettingsFile()));
+                new AtomicFileStorage(getOverrideSettingsFileForUser(USER_SYSTEM)));
     }
 
     @VisibleForTesting
@@ -133,6 +137,48 @@
         mBaseSettings = new ReadableSettings(baseSettingsStorage);
     }
 
+    /**
+     * Overrides the storage that should be used to save override settings for a user.
+     *
+     * @see #DATA_DISPLAY_SETTINGS_FILE_PATH
+     */
+    void setOverrideSettingsForUser(@UserIdInt int userId) {
+        if (!Flags.perUserDisplayWindowSettings()) {
+            return;
+        }
+        final AtomicFile settingsFile = getOverrideSettingsFileForUser(userId);
+        setOverrideSettingsStorage(new AtomicFileStorage(settingsFile));
+    }
+
+    /**
+     * Removes display override settings that are no longer associated with active displays.
+     * This is necessary because displays can be dynamically added or removed during
+     * the system's lifecycle (e.g., user switch, system server restart).
+     *
+     * @param root The root window container used to obtain the currently active displays.
+     */
+    void removeStaleDisplaySettings(@NonNull RootWindowContainer root) {
+        if (!Flags.perUserDisplayWindowSettings()) {
+            return;
+        }
+        final Set<String> displayIdentifiers = new ArraySet<>();
+        root.forAllDisplays(dc -> {
+            final String identifier = mOverrideSettings.getIdentifier(dc.getDisplayInfo());
+            displayIdentifiers.add(identifier);
+        });
+        mOverrideSettings.removeStaleDisplaySettings(displayIdentifiers);
+    }
+
+    /**
+     * Overrides the storage that should be used to save override settings.
+     *
+     * @see #setOverrideSettingsForUser(int)
+     */
+    @VisibleForTesting
+    void setOverrideSettingsStorage(@NonNull WritableSettingsStorage overrideSettingsStorage) {
+        mOverrideSettings = new WritableSettings(overrideSettingsStorage);
+    }
+
     @Override
     @NonNull
     public SettingsEntry getSettings(@NonNull DisplayInfo info) {
@@ -302,6 +348,12 @@
             mVirtualDisplayIdentifiers.remove(identifier);
         }
 
+        void removeStaleDisplaySettings(@NonNull Set<String> currentDisplayIdentifiers) {
+            if (mSettings.retainAll(currentDisplayIdentifiers)) {
+                writeSettings();
+            }
+        }
+
         private void writeSettings() {
             final FileData fileData = new FileData();
             fileData.mIdentifierType = mIdentifierType;
@@ -332,9 +384,14 @@
     }
 
     @NonNull
-    private static AtomicFile getOverrideSettingsFile() {
-        final File overrideSettingsFile = new File(Environment.getDataDirectory(),
-                DATA_DISPLAY_SETTINGS_FILE_PATH);
+    private static AtomicFile getOverrideSettingsFileForUser(@UserIdInt int userId) {
+        final File directory;
+        if (userId == USER_SYSTEM || !Flags.perUserDisplayWindowSettings()) {
+            directory = Environment.getDataDirectory();
+        } else {
+            directory = Environment.getDataSystemCeDirectory(userId);
+        }
+        final File overrideSettingsFile = new File(directory, DATA_DISPLAY_SETTINGS_FILE_PATH);
         return new AtomicFile(overrideSettingsFile, WM_DISPLAY_COMMIT_TAG);
     }
 
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e3827aa..4be5bad 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -64,7 +64,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.view.IDragAndDropPermissions;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index a21ba26..c66d659 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -35,7 +35,7 @@
 import android.view.InputChannel;
 import android.window.InputTransferToken;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.input.InputManagerService;
 
 /**
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 156e9f9..91c61b1 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -40,7 +40,7 @@
 import android.view.inputmethod.ImeTracker;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8035a29..74dbd15 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -66,7 +66,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f68b67f6..33dea54 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -48,7 +48,7 @@
 import android.view.WindowInsets;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a967f7a..3483842 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -41,7 +41,7 @@
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index f70d2a5..872b4e1 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
@@ -657,7 +656,6 @@
             final boolean lastKeyguardGoingAway = mKeyguardGoingAway;
 
             final ActivityRecord lastDismissKeyguardActivity = mDismissingKeyguardActivity;
-            final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
 
             mRequestDismissKeyguard = false;
             mOccluded = false;
@@ -666,7 +664,6 @@
             mDismissingKeyguardActivity = null;
             mTopTurnScreenOnActivity = null;
 
-            boolean occludedByActivity = false;
             final Task task = getRootTaskForControllingOccluding(display);
             final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null;
             if (top != null) {
@@ -712,7 +709,7 @@
 
             if (mTopTurnScreenOnActivity != null
                     && !mService.mWindowManager.mPowerManager.isInteractive()
-                    && (mRequestDismissKeyguard || occludedByActivity)) {
+                    && (mRequestDismissKeyguard || mOccluded)) {
                 controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
                 mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
             }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 85eeab1..e924fb6 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
@@ -65,7 +64,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.TaskDescription;
-import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -113,18 +111,10 @@
     @Nullable
     private Letterbox mLetterbox;
 
-    // Whether activity "refresh" was requested but not finished in
-    // ActivityRecord#activityResumedLocked following the camera compat force rotation in
-    // DisplayRotationCompatPolicy.
-    private boolean mIsRefreshRequested;
-
     private boolean mLastShouldShowLetterboxUi;
 
     private boolean mDoubleTapEvent;
 
-    @FreeformCameraCompatMode
-    private int mFreeformCameraCompatMode = CAMERA_COMPAT_FREEFORM_NONE;
-
     LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
         mLetterboxConfiguration = wmService.mLetterboxConfiguration;
         // Given activityRecord may not be fully constructed since LetterboxUiController
@@ -147,13 +137,6 @@
         }
     }
 
-
-    @VisibleForTesting
-    int getSetOrientationRequestCounter() {
-        return getAppCompatOverrides().getAppCompatOrientationOverrides()
-                .getSetOrientationRequestCounter();
-    }
-
     /**
      * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
      * because some game engines wait to get focus before drawing the content of the app which isn't
@@ -187,23 +170,6 @@
     }
 
     /**
-     * Whether we should apply the min aspect ratio per-app override only when an app is connected
-     * to the camera.
-     * When this override is applied the min aspect ratio given in the app's manifest will be
-     * overridden to the largest enabled aspect ratio treatment unless the app's manifest value
-     * is higher. The treatment will also apply if no value is provided in the manifest.
-     *
-     * <p>This method returns {@code true} when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Per-app override is enabled
-     * </ul>
-     */
-    boolean shouldOverrideMinAspectRatioForCamera() {
-        return getAppCompatOverrides().shouldOverrideMinAspectRatioForCamera();
-    }
-
-    /**
      * Whether we should apply the force resize per-app override. When this override is applied it
      * forces the packages it is applied to to be resizable. It won't change whether the app can be
      * put into multi-windowing mode, but allow the app to resize without going into size-compat
@@ -242,16 +208,6 @@
                 .setRelaunchingAfterRequestedOrientationChanged(isRelaunching);
     }
 
-    /**
-     * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}.
-     */
-    boolean isRefreshRequested() {
-        return mIsRefreshRequested;
-    }
-
-    void setIsRefreshRequested(boolean isRequested) {
-        mIsRefreshRequested = isRequested;
-    }
 
     boolean isOverrideRespectRequestedOrientationEnabled() {
         return getAppCompatOverrides().isOverrideRespectRequestedOrientationEnabled();
@@ -274,85 +230,6 @@
         return getAppCompatOverrides().shouldUseDisplayLandscapeNaturalOrientation();
     }
 
-    boolean isOverrideOrientationOnlyForCameraEnabled() {
-        return getAppCompatOverrides().isOverrideOrientationOnlyForCameraEnabled();
-    }
-
-    /**
-     * Whether activity is eligible for activity "refresh" after camera compat force rotation
-     * treatment. See {@link DisplayRotationCompatPolicy} for context.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the camera compat treatment is enabled.
-     *     <li>Activity isn't opted out by the device manufacturer with override or by the app
-     *     developers with the component property.
-     * </ul>
-     */
-    boolean shouldRefreshActivityForCameraCompat() {
-        return getAppCompatOverrides().shouldRefreshActivityForCameraCompat();
-    }
-
-    /**
-     * Whether activity should be "refreshed" after the camera compat force rotation treatment
-     * using the "resumed -> paused -> resumed" cycle rather than the "resumed -> ... -> stopped
-     * -> ... -> resumed" cycle. See {@link DisplayRotationCompatPolicy} for context.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the camera compat treatment is enabled.
-     *     <li>Activity "refresh" via "resumed -> paused -> resumed" cycle isn't disabled with the
-     *     component property by the app developers.
-     *     <li>Activity "refresh" via "resumed -> paused -> resumed" cycle is enabled by the device
-     *     manufacturer with override / by the app developers with the component property.
-     * </ul>
-     */
-    boolean shouldRefreshActivityViaPauseForCameraCompat() {
-        return getAppCompatOverrides().shouldRefreshActivityViaPauseForCameraCompat();
-    }
-
-    /**
-     * Whether activity is eligible for camera compat force rotation treatment. See {@link
-     * DisplayRotationCompatPolicy} for context.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the camera compat treatment is enabled.
-     *     <li>Activity isn't opted out by the device manufacturer with override or by the app
-     *     developers with the component property.
-     * </ul>
-     */
-    boolean shouldForceRotateForCameraCompat() {
-        return getAppCompatOverrides().shouldForceRotateForCameraCompat();
-    }
-
-    /**
-     * Whether activity is eligible for camera compatibility free-form treatment.
-     *
-     * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
-     * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
-     * provides changes to the camera and display orientation signals to match those expected on a
-     * portrait device in that orientation (for example, on a standard phone).
-     *
-     * <p>The treatment is enabled when the following conditions are met:
-     * <ul>
-     * <li>Property gating the camera compatibility free-form treatment is enabled.
-     * <li>Activity isn't opted out by the device manufacturer with override.
-     * </ul>
-     */
-    boolean shouldApplyFreeformTreatmentForCameraCompat() {
-        return getAppCompatOverrides().shouldApplyFreeformTreatmentForCameraCompat();
-    }
-
-    @FreeformCameraCompatMode
-    int getFreeformCameraCompatMode() {
-        return mFreeformCameraCompatMode;
-    }
-
-    void setFreeformCameraCompatMode(@FreeformCameraCompatMode int freeformCameraCompatMode) {
-        mFreeformCameraCompatMode = freeformCameraCompatMode;
-    }
-
     private boolean isCompatChangeEnabled(long overrideChangeId) {
         return mActivityRecord.info.isChangeEnabled(overrideChangeId);
     }
@@ -574,27 +451,10 @@
                         : getDefaultMinAspectRatio();
     }
 
-    void recomputeConfigurationForCameraCompatIfNeeded() {
-        if (isOverrideOrientationOnlyForCameraEnabled()
-                || isCameraCompatSplitScreenAspectRatioAllowed()
-                || shouldOverrideMinAspectRatioForCamera()) {
-            mActivityRecord.recomputeConfiguration();
-        }
-    }
-
     boolean isLetterboxEducationEnabled() {
         return mLetterboxConfiguration.getIsEducationEnabled();
     }
 
-    /**
-     * Whether we use split screen aspect ratio for the activity when camera compat treatment
-     * is active because the corresponding config is enabled and activity supports resizing.
-     */
-    boolean isCameraCompatSplitScreenAspectRatioAllowed() {
-        return mLetterboxConfiguration.isCameraCompatSplitScreenAspectRatioEnabled()
-                && !mActivityRecord.shouldCreateCompatDisplayInsets();
-    }
-
     private boolean shouldUseSplitScreenAspectRatio(@NonNull Configuration parentConfiguration) {
         final boolean isBookMode = isDisplayFullScreenAndInPosture(/* isTabletop */ false);
         final boolean isNotCenteredHorizontally = getHorizontalPositionMultiplier(
@@ -605,8 +465,9 @@
 
         // Don't resize to split screen size when in book mode if letterbox position is centered
         return (isBookMode && isNotCenteredHorizontally || isTabletopMode && isLandscape)
-                    || isCameraCompatSplitScreenAspectRatioAllowed()
-                        && getAppCompatOverrides().isCameraCompatTreatmentActive();
+                    || mActivityRecord.mAppCompatController.getAppCompatCameraOverrides()
+                            .isCameraCompatSplitScreenAspectRatioAllowed()
+                                && getAppCompatOverrides().isCameraCompatTreatmentActive();
     }
 
     private float getDefaultMinAspectRatioForUnresizableApps() {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 0f9998c..0dadade 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -63,7 +63,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.telephony.CellBroadcastUtils;
 import com.android.internal.widget.LockPatternUtils;
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 36c092b..1a895ea 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -35,7 +35,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 4c797f8..3cf301c 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -36,7 +36,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.wm.DeviceStateController.DeviceState;
 
 public class PhysicalDisplaySwitchTransitionLauncher {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index e07b72a..6f25280 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -80,7 +80,7 @@
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.am.ActivityManagerService;
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 469cc64..c592caf 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -42,7 +42,7 @@
 import android.util.Slog;
 import android.view.IRecentsAnimationRunner;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 312c4be..6f94713 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -64,7 +64,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 63bbb31..a8edaeb 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -45,7 +45,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
index c22b07a..e4962bf 100644
--- a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
+++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
@@ -28,7 +28,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index d497d8c..243dbc7 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -24,7 +24,7 @@
 import android.content.pm.ActivityInfo;
 import android.os.Debug;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.ArrayList;
 import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6abd488..d1f1cab 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -141,7 +141,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.LocalServices;
@@ -2158,6 +2158,12 @@
                 // Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
                 // will be updated later after this is collected in transition.
                 rootTask.setBoundsUnchecked(taskFragment.getBounds());
+                // The exit-PIP activity resumes early for seamless transition. In certain
+                // scenarios, this introduces unintended addition to recents. To address this,
+                // we mark the root task for automatic removal from recents. This ensures that
+                // after the pinned activity reparents to its original task, the root task is
+                // automatically removed from the recents list.
+                rootTask.autoRemoveRecents = true;
 
                 // Move the last recents animation transaction from original task to the new one.
                 if (task.mLastRecentsAnimationTransaction != null) {
diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
index 967f415..ad4faab 100644
--- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
+++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
@@ -33,7 +33,7 @@
 import android.window.IScreenRecordingCallback;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index e7bffdf..3eb3218 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -57,7 +57,7 @@
 
 import com.android.internal.R;
 import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.display.DisplayControl;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 4a0239b..9addce6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -84,7 +84,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.wm.WindowManagerService.H;
 import com.android.window.flags.Flags;
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
index b5d94a2..2b4d901 100644
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -26,7 +26,7 @@
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 class SmoothDimmer extends Dimmer {
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c632714..9cfd396 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -33,7 +33,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 0c36d27..34abf23 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -31,7 +31,7 @@
 import android.window.ScreenCapture;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 /**
  * This class handles "freezing" of an Animatable. The Animatable in question should implement
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f9c53aa..f0b0e91 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -181,7 +181,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
@@ -3522,7 +3522,8 @@
         appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
         appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = top == null
                 ? CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE
-                : top.mLetterboxUiController.getFreeformCameraCompatMode();
+                : top.mAppCompatController.getAppCompatCameraOverrides()
+                        .getFreeformCameraCompatMode();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index eff8315..eaf3012 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -52,7 +52,7 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 3cd071b..9b2c022 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -106,7 +106,7 @@
 import android.window.TaskFragmentOrganizerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.am.HostingRecord;
 import com.android.server.pm.pkg.AndroidPackage;
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 26315f9..b6b6cf2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -59,7 +59,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index b24d53b..6e36d427 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -57,7 +57,7 @@
 import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 9b3fb6b..972dd2e 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -59,7 +59,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.TaskResizingAlgorithm;
 import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.concurrent.CompletableFuture;
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index e698a3d..35a7702 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -106,7 +106,7 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 0812323..f4ff404 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -53,7 +53,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.FgThread;
 import com.android.window.flags.Flags;
 
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index fa2d9bf..c0dc424 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -40,7 +40,7 @@
 import android.window.TrustedPresentationThresholds;
 import android.window.WindowInfosListener;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.wm.utils.RegionUtils;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index 4a5a20e..fffe692 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -27,7 +27,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 43f7ecc..3b5a605 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -57,7 +57,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
 import com.android.window.flags.Flags;
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index b7f8505..31156de 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -31,7 +31,7 @@
 import android.os.RemoteException;
 import android.util.SparseArray;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 
 import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 8afcf0e..03342d3 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -34,7 +34,7 @@
 import android.view.Choreographer;
 import android.view.SurfaceControl;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1f31af6..325ef0d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -111,7 +111,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index b000a98..57fc4c7 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -39,7 +39,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.animation.Animation;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 21f251f..cd785e5 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -39,7 +39,7 @@
 import android.window.WindowContext;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.util.Objects;
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5215609..57b8040 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -335,7 +335,7 @@
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.internal.protolog.LegacyProtoLogImpl;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
@@ -3743,6 +3743,8 @@
                         null /* trigger */, null /* remote */, null /* disp */);
             }
             mCurrentUserId = newUserId;
+            mDisplayWindowSettingsProvider.setOverrideSettingsForUser(newUserId);
+            mDisplayWindowSettingsProvider.removeStaleDisplaySettings(mRoot);
             mPolicy.setCurrentUserLw(newUserId);
             mKeyguardDisableHandler.setCurrentUser(newUserId);
 
@@ -5479,6 +5481,9 @@
             // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also
             // affect the Configuration.
             mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
+            // Per-user display settings may leave outdated settings after user switches, especially
+            // during reboots starting with the default user without setCurrentUser called.
+            mDisplayWindowSettingsProvider.removeStaleDisplaySettings(mRoot);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 1f06bfa..6febe80 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -51,7 +51,7 @@
 import com.android.internal.protolog.LegacyProtoLogImpl;
 import com.android.internal.protolog.PerfettoProtoLogImpl;
 import com.android.internal.protolog.common.IProtoLog;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.IoThread;
 import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
 import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d26df7a..de58457 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -124,7 +124,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
@@ -1105,6 +1105,7 @@
                     break;
                 }
                 if (activity.isVisible() || activity.isVisibleRequested()) {
+                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
                     // Prevent the transition from being executed too early if the activity is
                     // visible.
                     activity.finishIfPossible("finish-activity-op", false /* oomAdj */);
@@ -1122,6 +1123,7 @@
                 launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
                 final SafeActivityOptions safeOptions =
                         SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
                 waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents(
                         caller.mPid, caller.mUid, taskId, safeOptions));
                 break;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index b878072..60d3e78 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -82,7 +82,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.Watchdog;
 import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d845968..fec1175 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -250,7 +250,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.policy.WindowManagerPolicy;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9ecd492..397a6357 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -62,7 +62,7 @@
 import android.view.animation.AnimationUtils;
 
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 import com.android.server.policy.WindowManagerPolicy;
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 4456a94..d9766e0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -36,7 +36,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowContentFrameStats;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 4dca23b..11ef2cd 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -47,7 +47,7 @@
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.window.WindowContext;
 
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index b0e71bd..ba5323e 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -37,7 +37,7 @@
 
 import com.android.internal.protolog.LegacyProtoLogImpl;
 import com.android.internal.protolog.common.IProtoLog;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.TraceBuffer;
 
 import java.io.File;
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index c558aae..7ed23cd 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -230,18 +230,7 @@
         }
         val isSoftRestricted =
             if (permission.isSoftRestricted && !isExempt) {
-                val targetSdkVersion =
-                    reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT) {
-                        targetSdkVersion,
-                        packageState ->
-                        if (permissionName in packageState.androidPackage!!.requestedPermissions) {
-                            targetSdkVersion.coerceAtMost(
-                                packageState.androidPackage!!.targetSdkVersion
-                            )
-                        } else {
-                            targetSdkVersion
-                        }
-                    }
+                val targetSdkVersion = getAppIdTargetSdkVersion(appId, permissionName)
                 !anyPackageInAppId(appId) {
                     permissionName in it.androidPackage!!.requestedPermissions &&
                         isSoftRestrictedPermissionExemptForPackage(
@@ -718,18 +707,8 @@
 
         // If the app is updated, and has scoped storage permissions, then it is possible that the
         // app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
-        val oldTargetSdkVersion =
-            reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, oldState) {
-                targetSdkVersion,
-                packageState ->
-                targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
-            }
-        val newTargetSdkVersion =
-            reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, newState) {
-                targetSdkVersion,
-                packageState ->
-                targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
-            }
+        val oldTargetSdkVersion = getAppIdTargetSdkVersion(appId, null, oldState)
+        val newTargetSdkVersion = getAppIdTargetSdkVersion(appId, null, newState)
         @Suppress("ConvertTwoComparisonsToRangeCheck")
         val isTargetSdkVersionDowngraded =
             oldTargetSdkVersion >= Build.VERSION_CODES.Q &&
@@ -1115,10 +1094,9 @@
     }
 
     private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) {
-        var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
+        val targetSdkVersion = getAppIdTargetSdkVersion(appId, null)
         val implicitPermissions = MutableIndexedSet<String>()
         forEachPackageInAppId(appId) {
-            targetSdkVersion = targetSdkVersion.coerceAtMost(it.androidPackage!!.targetSdkVersion)
             implicitPermissions += it.androidPackage!!.implicitPermissions
         }
         implicitPermissions.forEachIndexed implicitPermissions@{ _, implicitPermissionName ->
@@ -1418,6 +1396,22 @@
             else -> false
         }
 
+    private fun MutateStateScope.getAppIdTargetSdkVersion(
+        appId: Int,
+        permissionName: String?,
+        state: AccessState = newState
+    ): Int =
+        reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, state) {
+            targetSdkVersion,
+            packageState ->
+            val androidPackage = packageState.androidPackage!!
+            if (permissionName == null || permissionName in androidPackage.requestedPermissions) {
+                targetSdkVersion.coerceAtMost(androidPackage.targetSdkVersion)
+            } else {
+                targetSdkVersion
+            }
+        }
+
     private inline fun MutateStateScope.anyPackageInAppId(
         appId: Int,
         state: AccessState = newState,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 33ea9b4..9e46f2f 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import static java.util.Objects.requireNonNull;
 
@@ -134,8 +135,10 @@
 
     @Test
     public void testApplyImeVisibility_hideImeExplicit() throws Exception {
-        mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
         synchronized (ImfLock.class) {
+            final var bindingController =
+                    mInputMethodManagerService.getInputMethodBindingController(mUserId);
+            when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE);
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                     STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId);
         }
@@ -144,8 +147,10 @@
 
     @Test
     public void testApplyImeVisibility_hideNotAlways() throws Exception {
-        mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
         synchronized (ImfLock.class) {
+            final var bindingController =
+                    mInputMethodManagerService.getInputMethodBindingController(mUserId);
+            when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE);
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                     STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId);
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 7b8b712..d070aaa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1700,10 +1700,12 @@
         verify(mDisplayOffloadSession, never()).cancelBlockScreenOn();
     }
 
-    @RequiresFlagsEnabled(Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON)
     @Test
     public void testOffloadBlocker_turnON_thenOFF_cancelBlockScreenOnNotCalledIfUnblocked() {
         // Set up.
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        when(mDisplayManagerFlagsMock.isOffloadSessionCancelBlockScreenOnEnabled())
+                .thenReturn(true);
         int initState = Display.STATE_OFF;
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
@@ -1737,10 +1739,12 @@
         verify(mDisplayOffloadSession, never()).cancelBlockScreenOn();
     }
 
-    @RequiresFlagsEnabled(Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON)
     @Test
     public void testOffloadBlocker_turnON_thenOFF_cancelBlockScreenOn() {
         // Set up.
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        when(mDisplayManagerFlagsMock.isOffloadSessionCancelBlockScreenOnEnabled())
+                .thenReturn(true);
         int initState = Display.STATE_OFF;
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
         mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 123f0ed..ca30551 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -28,6 +28,7 @@
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
@@ -163,6 +164,7 @@
         mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
         mUserState.updateShortcutTargetsLocked(Set.of(COMPONENT_NAME.flattenToString()), HARDWARE);
         mUserState.updateShortcutTargetsLocked(Set.of(COMPONENT_NAME.flattenToString()), SOFTWARE);
+        mUserState.updateShortcutTargetsLocked(Set.of(COMPONENT_NAME.flattenToString()), GESTURE);
         mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
         mUserState.setTouchExplorationEnabledLocked(true);
         mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
@@ -186,6 +188,7 @@
         assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
         assertTrue(mUserState.getShortcutTargetsLocked(HARDWARE).isEmpty());
         assertTrue(mUserState.getShortcutTargetsLocked(SOFTWARE).isEmpty());
+        assertTrue(mUserState.getShortcutTargetsLocked(GESTURE).isEmpty());
         assertNull(mUserState.getTargetAssignedToAccessibilityButton());
         assertFalse(mUserState.isTouchExplorationEnabledLocked());
         assertFalse(mUserState.isMagnificationSingleFingerTripleTapEnabledLocked());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 36a5cda..9cd3186 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -393,18 +393,6 @@
         testAuthenticate_throwsSecurityException(promptInfo);
     }
 
-    @Test
-    public void testCanAuthenticate_throwsWhenUsingAdvancedApis() {
-        mAuthService = new AuthService(mContext, mInjector);
-        mAuthService.onStart();
-
-        assertThrows(SecurityException.class, () -> {
-            mAuthService.mImpl.canAuthenticate(TEST_OP_PACKAGE_NAME, 1 /* userId */,
-                    BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
-            waitForIdle();
-        });
-    }
-
     private void testAuthenticate_throwsSecurityException(PromptInfo promptInfo) {
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 2a4b797..21364b8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -2012,38 +2012,94 @@
     }
 
     @Test
-    public void handleStandby_fromActiveSource_standby() {
-        mPowerManager.setInteractive(true);
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+    public void handleStandby_fromActiveSource_previousActiveSourceSet_standby() {
+        mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, 0x1000);
+        HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
+                ADDR_TV);
         mTestLooper.dispatchAll();
 
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isFalse();
+        mPowerManager.setInteractive(true);
+        mTestLooper.dispatchAll();
+
+        mHdmiCecLocalDeviceTv.dispatchMessage(activeSourceFromPlayback);
         mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000,
                 "HdmiCecLocalDeviceTvTest");
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
-                ADDR_TV);
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isTrue();
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
                 .isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
 
         assertThat(mPowerManager.isInteractive()).isFalse();
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isFalse();
     }
 
     @Test
-    public void handleStandby_fromNonActiveSource_noStandby() {
+    public void handleStandby_fromNonActiveSource_previousActiveSourceSet_noStandby() {
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_2, 0x2000);
+        HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
+                ADDR_TV);
+        mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isFalse();
         mPowerManager.setInteractive(true);
-        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+        mHdmiCecLocalDeviceTv.dispatchMessage(activeSourceFromPlayback);
         mHdmiControlService.setActiveSource(ADDR_PLAYBACK_2, 0x2000,
                 "HdmiCecLocalDeviceTvTest");
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
-                ADDR_TV);
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isTrue();
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
                 .isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
 
         assertThat(mPowerManager.isInteractive()).isTrue();
     }
+
+
+    @Test
+    public void handleStandby_fromNonActiveSource_previousActiveSourceNotSet_Standby() {
+        HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
+                ADDR_TV);
+        mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isFalse();
+        mPowerManager.setInteractive(true);
+
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isFalse();
+        assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        assertThat(mPowerManager.isInteractive()).isFalse();
+        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+                .isFalse();
+    }
+
+    protected static class MockTvDevice extends HdmiCecLocalDeviceTv {
+        MockTvDevice(HdmiControlService service) {
+            super(service);
+        }
+
+        @Override
+        protected int handleActiveSource(HdmiCecMessage message) {
+            setWasActiveSourceSetToConnectedDevice(true);
+            return super.handleActiveSource(message);
+        }
+    }
 }
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 37065fd..02d3b59 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -1424,6 +1424,39 @@
 
     @MediumTest
     @Test
+    public void testSerialNumberAfterUserRemoval() {
+        final UserInfo user = mUserManager.createUser("Test User", 0);
+        assertThat(user).isNotNull();
+
+        final int userId = user.id;
+        assertThat(mUserManager.getUserSerialNumber(userId))
+            .isEqualTo(user.serialNumber);
+        mUsersToRemove.add(userId);
+        removeUser(userId);
+        int serialNumber = mUserManager.getUserSerialNumber(userId);
+        int timeout = REMOVE_USER_TIMEOUT_SECONDS * 5; // called every 200ms
+
+        // Wait for the user to be removed from memory
+        while(serialNumber > 0 && timeout > 0){
+          sleep(200);
+          timeout--;
+          serialNumber = mUserManager.getUserSerialNumber(userId);
+        }
+        assertThat(serialNumber).isEqualTo(-1);
+    }
+
+
+    private void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    @MediumTest
+    @Test
     public void testMaxUsers() {
         int N = UserManager.getMaxSupportedUsers();
         int count = mUserManager.getUsers().size();
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index b292294..6ba2c70 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -28,7 +28,7 @@
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) transform-protolog-calls " +
-        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+        "--protolog-class com.android.internal.protolog.ProtoLog " +
         "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
         "--loggroups-jar $(location :protolog-groups) " +
         // Used for the ProtoLogIntegrationTest, where don't test decoding or writing to file
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
index 12ab3e1..a3252f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
@@ -103,8 +103,8 @@
     @Test
     public void testShouldRefreshActivity_refreshDisabledForActivity() throws Exception {
         configureActivityAndDisplay();
-        when(mActivity.mLetterboxUiController.shouldRefreshActivityForCameraCompat())
-                .thenReturn(false);
+        when(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldRefreshActivityForCameraCompat()).thenReturn(false);
         mActivityRefresher.addEvaluator(mEvaluatorTrue);
 
         mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
@@ -160,8 +160,9 @@
             throws Exception {
         configureActivityAndDisplay();
         mActivityRefresher.addEvaluator(mEvaluatorTrue);
-        doReturn(true).when(mActivity.mLetterboxUiController)
-                .shouldRefreshActivityViaPauseForCameraCompat();
+        doReturn(true)
+                .when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
+                    .shouldRefreshActivityViaPauseForCameraCompat();
 
         mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
 
@@ -172,8 +173,9 @@
     public void testOnActivityRefreshed_setIsRefreshRequestedToFalse() throws Exception {
         configureActivityAndDisplay();
         mActivityRefresher.addEvaluator(mEvaluatorTrue);
-        doReturn(true).when(mActivity.mLetterboxUiController)
-                .shouldRefreshActivityViaPauseForCameraCompat();
+        doReturn(true)
+                .when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
+                    .shouldRefreshActivityViaPauseForCameraCompat();
 
         mActivityRefresher.onActivityRefreshed(mActivity);
 
@@ -186,8 +188,8 @@
 
     private void assertActivityRefreshRequested(boolean refreshRequested,
             boolean cycleThroughStop) throws Exception {
-        verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
-                .setIsRefreshRequested(true);
+        verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(),
+                times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
 
         final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
                 cycleThroughStop ? ON_STOP : ON_PAUSE);
@@ -211,9 +213,9 @@
                 .getTopMostActivity();
 
         spyOn(mActivity.mLetterboxUiController);
-        doReturn(true).when(
-                mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
-
+        spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
         doReturn(true).when(mActivity).inFreeformWindowingMode();
+        doReturn(true).when(mActivity.mAppCompatController
+                .getAppCompatCameraOverrides()).shouldRefreshActivityForCameraCompat();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 5f2853a..467050e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -149,6 +149,10 @@
                 .mLetterboxUiController).shouldApplyUserMinAspectRatioOverride();
     }
 
+    void setShouldCreateCompatDisplayInsets(boolean enabled) {
+        doReturn(enabled).when(mActivityStack.top()).shouldCreateCompatDisplayInsets();
+    }
+
     void setShouldApplyUserFullscreenOverride(boolean enabled) {
         doReturn(enabled).when(mActivityStack.top()
                 .mLetterboxUiController).shouldApplyUserFullscreenOverride();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
new file mode 100644
index 0000000..9263b4f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.testng.Assert;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatCameraOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatCameraOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatCameraOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    public void testShouldRefreshActivityForCameraCompat_flagIsDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(false);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
+    public void testShouldRefreshActivityForCameraCompat_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
+    public void testShouldRefreshActivityForCameraCompat_propertyIsTrueAndOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityForCameraCompat(false);
+        });
+    }
+
+    @Test
+    public void testShouldRefreshActivityForCameraCompat_propertyIsFalse_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityForCameraCompat(false);
+        });
+    }
+
+    @Test
+    public void testShouldRefreshActivityForCameraCompat_propertyIsTrue_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityForCameraCompat(true);
+        });
+    }
+
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
+    public void testShouldRefreshActivityViaPauseForCameraCompat_flagIsDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(false);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityViaPauseForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
+    public void testShouldRefreshActivityViaPauseForCameraCompat_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityViaPauseForCameraCompat(true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
+    public void testShouldRefreshActivityViaPauseForCameraCompat_propertyFalseAndOverrideFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().disable(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityViaPauseForCameraCompat(false);
+        });
+    }
+
+    @Test
+    public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsTrue_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().enable(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldRefreshActivityViaPauseForCameraCompat(true);
+        });
+    }
+
+    @Test
+    public void testShouldForceRotateForCameraCompat_flagIsDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(false);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldForceRotateForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION})
+    public void testShouldForceRotateForCameraCompat_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldForceRotateForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION})
+    public void testShouldForceRotateForCameraCompat_propertyIsTrueAndOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldForceRotateForCameraCompat(false);
+        });
+    }
+
+    @Test
+    public void testShouldForceRotateForCameraCompat_propertyIsFalse_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldForceRotateForCameraCompat(false);
+        });
+    }
+
+    @Test
+    public void testShouldForceRotateForCameraCompat_propertyIsTrue_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldForceRotateForCameraCompat(true);
+        });
+    }
+
+    @Test
+    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testShouldApplyCameraCompatFreeformTreatment_flagIsDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA,
+            OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void testShouldRecomputeConfigurationForCameraCompat() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
+            robot.activity().createActivityWithComponentInNewTask();
+            robot.activateCamera(true);
+            robot.activity().setShouldCreateCompatDisplayInsets(false);
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<CameraOverridesRobotTest> consumer) {
+        spyOn(mWm.mLetterboxConfiguration);
+        final CameraOverridesRobotTest robot = new CameraOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class CameraOverridesRobotTest extends AppCompatRobotBase {
+
+        CameraOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void activateCamera(boolean isCameraActive) {
+            doReturn(isCameraActive).when(activity().top()).isCameraActive();
+        }
+
+        void checkShouldRefreshActivityForCameraCompat(boolean expected) {
+            Assert.assertEquals(getAppCompatCameraOverrides()
+                    .shouldRefreshActivityForCameraCompat(), expected);
+        }
+
+        void checkShouldRefreshActivityViaPauseForCameraCompat(boolean expected) {
+            Assert.assertEquals(getAppCompatCameraOverrides()
+                    .shouldRefreshActivityViaPauseForCameraCompat(), expected);
+        }
+
+        void checkShouldForceRotateForCameraCompat(boolean expected) {
+            Assert.assertEquals(getAppCompatCameraOverrides()
+                    .shouldForceRotateForCameraCompat(), expected);
+        }
+
+        void checkShouldApplyFreeformTreatmentForCameraCompat(boolean expected) {
+            Assert.assertEquals(getAppCompatCameraOverrides()
+                    .shouldApplyFreeformTreatmentForCameraCompat(), expected);
+        }
+
+        private AppCompatCameraOverrides getAppCompatCameraOverrides() {
+            return activity().top().mAppCompatController.getAppCompatCameraOverrides();
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
new file mode 100644
index 0000000..4116313
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatCameraPolicy}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatCameraPolicyTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatCameraPolicyTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+    public void testRecomputeConfigurationForCameraCompatIfNeeded_allDisabledNoRecompute() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.conf().enableCameraCompatSplitScreenAspectRatio(false);
+            robot.activateCamera(/* isCameraActive */ false);
+
+            robot.recomputeConfigurationForCameraCompatIfNeeded();
+            robot.checkRecomputeConfigurationInvoked(/* invoked */ false);
+
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+    public void testRecomputeConfigurationForCameraCompatIfNeeded_cameraEnabledRecompute() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.conf().enableCameraCompatSplitScreenAspectRatio(false);
+            robot.activateCamera(/* isCameraActive */ false);
+
+            robot.recomputeConfigurationForCameraCompatIfNeeded();
+            robot.checkRecomputeConfigurationInvoked(/* invoked */ true);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+    public void testRecomputeConfigurationForCameraSplitScreenCompatIfNeeded_recompute() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
+            robot.activateCamera(/* isCameraActive */ false);
+
+            robot.recomputeConfigurationForCameraCompatIfNeeded();
+            robot.checkRecomputeConfigurationInvoked(/* invoked */ true);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void testRecomputeConfigurationForCameraSplitScreenCompatIfNeededWithCamera_recompute() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.conf().enableCameraCompatSplitScreenAspectRatio(false);
+            robot.activateCamera(/* isCameraActive */ true);
+
+            robot.recomputeConfigurationForCameraCompatIfNeeded();
+            robot.checkRecomputeConfigurationInvoked(/* invoked */ true);
+        });
+    }
+
+    void runTestScenario(@NonNull Consumer<CameraPolicyRobotTest> consumer) {
+        spyOn(mWm.mLetterboxConfiguration);
+        final CameraPolicyRobotTest robot = new CameraPolicyRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class CameraPolicyRobotTest extends AppCompatRobotBase {
+
+        private final WindowManagerService mWm;
+
+        CameraPolicyRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+            mWm = wm;
+            spyOn(mWm);
+        }
+
+        void activateCamera(boolean isCameraActive) {
+            doReturn(isCameraActive).when(activity().top()).isCameraActive();
+        }
+
+        void recomputeConfigurationForCameraCompatIfNeeded() {
+            getAppCompatCameraPolicy().recomputeConfigurationForCameraCompatIfNeeded();
+        }
+
+        void checkRecomputeConfigurationInvoked(boolean invoked) {
+            if (invoked) {
+                verify(activity().top()).recomputeConfiguration();
+            } else {
+                verify(activity().top(), never()).recomputeConfiguration();
+            }
+        }
+
+        private AppCompatCameraPolicy getAppCompatCameraPolicy() {
+            return activity().top().mAppCompatController.getAppCompatCameraPolicy();
+        }
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
index 2ef77f6..e1da913 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
@@ -61,4 +61,11 @@
     void enableUserAppAspectRatioSettings(boolean enabled) {
         doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
     }
+
+    void enableCameraCompatSplitScreenAspectRatio(boolean enabled) {
+        doReturn(enabled).when(mLetterboxConfiguration)
+                .isCameraCompatSplitScreenAspectRatioEnabled();
+    }
+
+
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index b3f1502..564c29f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -245,8 +245,8 @@
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        doReturn(false).when(
-                mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
+        doReturn(false).when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
+                .shouldRefreshActivityForCameraCompat();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
         callOnActivityConfigurationChanging(mActivity);
@@ -271,7 +271,7 @@
     public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(true).when(mActivity.mLetterboxUiController)
+        doReturn(true).when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
                 .shouldRefreshActivityViaPauseForCameraCompat();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -305,6 +305,7 @@
                 .build();
 
         spyOn(mActivity.mLetterboxUiController);
+        spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
         spyOn(mActivity.info);
 
         doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
@@ -315,12 +316,14 @@
 
     private void assertInCameraCompatMode() {
         assertNotEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
-                mActivity.mLetterboxUiController.getFreeformCameraCompatMode());
+                mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                        .getFreeformCameraCompatMode());
     }
 
     private void assertNotInCameraCompatMode() {
         assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
-                mActivity.mLetterboxUiController.getFreeformCameraCompatMode());
+                mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                        .getFreeformCameraCompatMode());
     }
 
     private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
@@ -329,8 +332,8 @@
 
     private void assertActivityRefreshRequested(boolean refreshRequested,
             boolean cycleThroughStop) throws Exception {
-        verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
-                .setIsRefreshRequested(true);
+        verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(),
+                times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
 
         final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
                 cycleThroughStop ? ON_STOP : ON_PAUSE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index c65371f..d7814ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -266,7 +266,7 @@
     public void testTreatmentDisabledPerApp_noForceRotationOrRefresh()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(false).when(mActivity.mLetterboxUiController)
+        doReturn(false).when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
                 .shouldForceRotateForCameraCompat();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -468,8 +468,8 @@
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        doReturn(false).when(
-                mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
+        doReturn(false).when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
+                .shouldRefreshActivityForCameraCompat();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
@@ -494,8 +494,9 @@
     public void testOnActivityConfigurationChanging_displayRotationNotChanging_noRefresh()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(false).when(mActivity.mLetterboxUiController)
-                .isCameraCompatSplitScreenAspectRatioAllowed();
+        doReturn(false).when(mActivity
+                        .mAppCompatController.getAppCompatCameraOverrides())
+                            .isCameraCompatSplitScreenAspectRatioAllowed();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ false);
@@ -507,7 +508,7 @@
     public void testOnActivityConfigurationChanging_splitScreenAspectRatioAllowed_refresh()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(true).when(mActivity.mLetterboxUiController)
+        doReturn(true).when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
                 .isCameraCompatSplitScreenAspectRatioAllowed();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -533,7 +534,7 @@
     public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(true).when(mActivity.mLetterboxUiController)
+        doReturn(true).when(mActivity.mAppCompatController.getAppCompatCameraOverrides())
                 .shouldRefreshActivityViaPauseForCameraCompat();
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -597,6 +598,7 @@
 
         spyOn(mActivity.mAtmService.getLifecycleManager());
         spyOn(mActivity.mLetterboxUiController);
+        spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
 
         doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
         doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
@@ -608,8 +610,8 @@
 
     private void assertActivityRefreshRequested(boolean refreshRequested,
                 boolean cycleThroughStop) throws Exception {
-        verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
-                .setIsRefreshRequested(true);
+        verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(),
+                times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
 
         final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
                 cycleThroughStop ? ON_STOP : ON_PAUSE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 7d9fdd5..3fcf304 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -24,9 +24,12 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertFalse;
 
 import android.annotation.Nullable;
@@ -55,6 +58,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.function.Consumer;
 
 /**
  * Tests for the {@link DisplayWindowSettingsProvider} class.
@@ -128,9 +132,8 @@
         // Update settings with new value, should trigger write to injector.
         DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
                 mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
-        SettingsEntry overrideSettings = provider.getOverrideSettings(mPrimaryDisplayInfo);
-        overrideSettings.mForcedDensity = 200;
-        provider.updateOverrideSettings(mPrimaryDisplayInfo, overrideSettings);
+        updateOverrideSettings(provider, mPrimaryDisplayInfo,
+                overrideSettings -> overrideSettings.mForcedDensity = 200);
         assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
 
         // Verify that display identifier was updated.
@@ -167,7 +170,7 @@
     }
 
     @Test
-    public void testReadingDisplaySettingsFromStorage_secondayVendorDisplaySettingsLocation() {
+    public void testReadingDisplaySettingsFromStorage_secondaryVendorDisplaySettingsLocation() {
         final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId;
         prepareSecondaryDisplaySettings(displayIdentifier);
 
@@ -216,11 +219,11 @@
         // Write some settings to storage.
         DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
                 mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
-        SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
-        overrideSettings.mShouldShowSystemDecors = true;
-        overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
-        overrideSettings.mDontMoveToTop = true;
-        provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
+        updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> {
+            overrideSettings.mShouldShowSystemDecors = true;
+            overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+            overrideSettings.mDontMoveToTop = true;
+        });
         assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
 
         // Verify that settings were stored correctly.
@@ -235,6 +238,29 @@
     }
 
     @Test
+    public void testWritingDisplaySettingsToStorage_secondaryUserDisplaySettingsLocation() {
+        final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
+                mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
+        final DisplayInfo displayInfo = mPrimaryDisplay.getDisplayInfo();
+        final TestStorage secondaryUserOverrideSettingsStorage = new TestStorage();
+        final SettingsEntry expectedSettings = new SettingsEntry();
+        expectedSettings.mForcedDensity = 356;
+
+        // Write some settings to storage from default user.
+        updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 356);
+        assertThat(mOverrideSettingsStorage.wasWriteSuccessful()).isTrue();
+
+        // Now switch to secondary user override settings and write some settings.
+        provider.setOverrideSettingsStorage(secondaryUserOverrideSettingsStorage);
+        updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 420);
+        assertThat(secondaryUserOverrideSettingsStorage.wasWriteSuccessful()).isTrue();
+
+        // Switch back to primary and assert default user settings remain unchanged.
+        provider.setOverrideSettingsStorage(mOverrideSettingsStorage);
+        assertThat(provider.getOverrideSettings(displayInfo)).isEqualTo(expectedSettings);
+    }
+
+    @Test
     public void testDoNotWriteVirtualDisplaySettingsToStorage() throws Exception {
         final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
         secondaryDisplayInfo.type = TYPE_VIRTUAL;
@@ -242,11 +268,11 @@
         // No write to storage on virtual display change.
         final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
                 mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
-        final SettingsEntry virtualSettings = provider.getOverrideSettings(secondaryDisplayInfo);
-        virtualSettings.mShouldShowSystemDecors = true;
-        virtualSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
-        virtualSettings.mDontMoveToTop = true;
-        provider.updateOverrideSettings(secondaryDisplayInfo, virtualSettings);
+        updateOverrideSettings(provider, secondaryDisplayInfo, virtualSettings -> {
+            virtualSettings.mShouldShowSystemDecors = true;
+            virtualSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+            virtualSettings.mDontMoveToTop = true;
+        });
         assertFalse(mOverrideSettingsStorage.wasWriteSuccessful());
     }
 
@@ -263,10 +289,10 @@
         // Write some settings to storage.
         DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
                 mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
-        SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
-        overrideSettings.mShouldShowSystemDecors = true;
-        overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
-        provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
+        updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> {
+            overrideSettings.mShouldShowSystemDecors = true;
+            overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+        });
         assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
 
         // Verify that settings were stored correctly.
@@ -283,16 +309,16 @@
         final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
                 mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
         final int initialSize = provider.getOverrideSettingsSize();
-
-        // Size + 1 when query for a new display.
         final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
-        final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
 
-        assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
+        updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> {
+            // Size + 1 when query for a new display.
+            assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
 
-        // When a display is removed, its override Settings is not removed if there is any override.
-        overrideSettings.mShouldShowSystemDecors = true;
-        provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
+            // When a display is removed, its override Settings is not removed if there is any
+            // override.
+            overrideSettings.mShouldShowSystemDecors = true;
+        });
         provider.onDisplayRemoved(secondaryDisplayInfo);
 
         assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
@@ -309,23 +335,53 @@
         final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
                 mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
         final int initialSize = provider.getOverrideSettingsSize();
-
-        // Size + 1 when query for a new display.
         final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
         secondaryDisplayInfo.type = TYPE_VIRTUAL;
-        final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
 
-        assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
+        updateOverrideSettings(provider, secondaryDisplayInfo, overrideSettings -> {
+            // Size + 1 when query for a new display.
+            assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
 
-        // When a virtual display is removed, its override Settings is removed even if it has
-        // override.
-        overrideSettings.mShouldShowSystemDecors = true;
-        provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
+            // When a virtual display is removed, its override Settings is removed
+            // even if it has override.
+            overrideSettings.mShouldShowSystemDecors = true;
+        });
         provider.onDisplayRemoved(secondaryDisplayInfo);
 
         assertEquals(initialSize, provider.getOverrideSettingsSize());
     }
 
+    @Test
+    public void testRemovesStaleDisplaySettings() {
+        assumeTrue(com.android.window.flags.Flags.perUserDisplayWindowSettings());
+
+        final DisplayWindowSettingsProvider provider =
+                new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage,
+                        mOverrideSettingsStorage);
+        final DisplayInfo displayInfo = mSecondaryDisplay.getDisplayInfo();
+        updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 356);
+        mRootWindowContainer.removeChild(mSecondaryDisplay);
+
+        provider.removeStaleDisplaySettings(mRootWindowContainer);
+
+        assertThat(mOverrideSettingsStorage.wasWriteSuccessful()).isTrue();
+        assertThat(provider.getOverrideSettingsSize()).isEqualTo(0);
+    }
+
+    /**
+     * Updates the override settings for a specific display.
+     *
+     * @param provider the provider to obtain and update the settings from.
+     * @param displayInfo the information about the display to be updated.
+     * @param modifier a function that modifies the settings for the display.
+     */
+    private static void updateOverrideSettings(DisplayWindowSettingsProvider provider,
+            DisplayInfo displayInfo, Consumer<SettingsEntry> modifier) {
+        final SettingsEntry settings = provider.getOverrideSettings(displayInfo);
+        modifier.accept(settings);
+        provider.updateOverrideSettings(displayInfo, settings);
+    }
+
     /**
      * Prepares display settings and stores in {@link #mOverrideSettingsStorage}. Uses provided
      * display identifier and stores windowingMode=WINDOWING_MODE_PINNED.
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index bdd45c6..74e2d44 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -18,10 +18,6 @@
 
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
@@ -31,9 +27,6 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
@@ -45,7 +38,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -54,8 +46,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -129,200 +119,7 @@
         mController = new LetterboxUiController(mWm, mActivity);
     }
 
-    // shouldRefreshActivityForCameraCompat
 
-    @Test
-    public void testShouldRefreshActivityForCameraCompat_flagIsDisabled_returnsFalse() {
-        doReturn(false).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-
-        assertFalse(mController.shouldRefreshActivityForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
-    public void testShouldRefreshActivityForCameraCompat_overrideEnabled_returnsFalse() {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-
-        assertFalse(mController.shouldRefreshActivityForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
-    public void testShouldRefreshActivityForCameraCompat_propertyIsTrueAndOverride_returnsFalse()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldRefreshActivityForCameraCompat());
-    }
-
-    @Test
-    public void testShouldRefreshActivityForCameraCompat_propertyIsFalse_returnsFalse()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldRefreshActivityForCameraCompat());
-    }
-
-    @Test
-    public void testShouldRefreshActivityForCameraCompat_propertyIsTrue_returnsTrue()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldRefreshActivityForCameraCompat());
-    }
-
-    // shouldRefreshActivityViaPauseForCameraCompat
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
-    public void testShouldRefreshActivityViaPauseForCameraCompat_flagIsDisabled_returnsFalse() {
-        doReturn(false).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-
-        assertFalse(mController.shouldRefreshActivityViaPauseForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
-    public void testShouldRefreshActivityViaPauseForCameraCompat_overrideEnabled_returnsTrue() {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-
-        assertTrue(mController.shouldRefreshActivityViaPauseForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
-    public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsFalseAndOverride_returnFalse()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldRefreshActivityViaPauseForCameraCompat());
-    }
-
-    @Test
-    public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsTrue_returnsTrue()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldRefreshActivityViaPauseForCameraCompat());
-    }
-
-    // shouldForceRotateForCameraCompat
-
-    @Test
-    public void testShouldForceRotateForCameraCompat_flagIsDisabled_returnsFalse() {
-        doReturn(false).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-
-        assertFalse(mController.shouldForceRotateForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION})
-    public void testShouldForceRotateForCameraCompat_overrideEnabled_returnsFalse() {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-
-        assertFalse(mController.shouldForceRotateForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION})
-    public void testShouldForceRotateForCameraCompat_propertyIsTrueAndOverride_returnsFalse()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldForceRotateForCameraCompat());
-    }
-
-    @Test
-    public void testShouldForceRotateForCameraCompat_propertyIsFalse_returnsFalse()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldForceRotateForCameraCompat());
-    }
-
-    @Test
-    public void testShouldForceRotateForCameraCompat_propertyIsTrue_returnsTrue()
-            throws Exception {
-        doReturn(true).when(mLetterboxConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldForceRotateForCameraCompat());
-    }
-
-    // shouldApplyFreeformTreatmentForCameraCompat
-
-    @Test
-    public void testShouldApplyCameraCompatFreeformTreatment_flagIsDisabled_returnsFalse() {
-        mSetFlagsRule.disableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse()
-            throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
-
-    @Test
-    public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue()
-            throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldApplyFreeformTreatmentForCameraCompat());
-    }
 
     @Test
     public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
@@ -641,39 +438,6 @@
         assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
     }
 
-    @Test
-    public void testRecomputeConfigurationForCameraCompatIfNeeded() {
-        spyOn(mController);
-        doReturn(false).when(mController).isOverrideOrientationOnlyForCameraEnabled();
-        doReturn(false).when(mController).isCameraCompatSplitScreenAspectRatioAllowed();
-        doReturn(false).when(mController).shouldOverrideMinAspectRatioForCamera();
-        clearInvocations(mActivity);
-
-        mController.recomputeConfigurationForCameraCompatIfNeeded();
-
-        verify(mActivity, never()).recomputeConfiguration();
-
-        // isOverrideOrientationOnlyForCameraEnabled
-        doReturn(true).when(mController).isOverrideOrientationOnlyForCameraEnabled();
-        clearInvocations(mActivity);
-        mController.recomputeConfigurationForCameraCompatIfNeeded();
-        verify(mActivity).recomputeConfiguration();
-
-        // isCameraCompatSplitScreenAspectRatioAllowed
-        doReturn(false).when(mController).isOverrideOrientationOnlyForCameraEnabled();
-        doReturn(true).when(mController).isCameraCompatSplitScreenAspectRatioAllowed();
-        clearInvocations(mActivity);
-        mController.recomputeConfigurationForCameraCompatIfNeeded();
-        verify(mActivity).recomputeConfiguration();
-
-        // shouldOverrideMinAspectRatioForCamera
-        doReturn(false).when(mController).isCameraCompatSplitScreenAspectRatioAllowed();
-        doReturn(true).when(mController).shouldOverrideMinAspectRatioForCamera();
-        clearInvocations(mActivity);
-        mController.recomputeConfigurationForCameraCompatIfNeeded();
-        verify(mActivity).recomputeConfiguration();
-    }
-
     private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
             boolean orientationRequest) {
         spyOn(mController);
@@ -875,7 +639,8 @@
         doReturn(true).when(mActivity).isCameraActive();
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertTrue(mController.shouldOverrideMinAspectRatioForCamera());
+        assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
@@ -886,7 +651,8 @@
         mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertTrue(mController.shouldOverrideMinAspectRatioForCamera());
+        assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
@@ -897,7 +663,8 @@
         mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
@@ -908,7 +675,8 @@
         mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
@@ -917,7 +685,8 @@
         doReturn(true).when(mActivity).isCameraActive();
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
@@ -927,7 +696,8 @@
         mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
@@ -938,7 +708,8 @@
         doReturn(true).when(mActivity).isCameraActive();
         mController = new LetterboxUiController(mWm, mActivity);
 
-        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
index c5bf78b..7efbc88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
@@ -29,7 +29,7 @@
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.internal.protolog.common.IProtoLog;
 import com.android.internal.protolog.common.LogLevel;
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.protolog.ProtoLog;
 
 import org.junit.After;
 import org.junit.Ignore;
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 eb79118..3078df0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -392,6 +392,8 @@
         assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask());
         assertNotEquals(newPipTask, activity1.getTask());
         assertFalse("Created PiP task must not be in recents", newPipTask.inRecents);
+        assertThat(newPipTask.autoRemoveRecents).isTrue();
+        assertThat(activity1.getTask().autoRemoveRecents).isFalse();
     }
 
     /**
@@ -427,6 +429,7 @@
         bounds.scale(0.5f);
         task.setBounds(bounds);
         assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertThat(task.autoRemoveRecents).isFalse();
     }
 
     /**
@@ -451,6 +454,7 @@
         // Ensure a task has moved over.
         ensureTaskPlacement(task, activity);
         assertTrue(task.inPinnedWindowingMode());
+        assertThat(task.autoRemoveRecents).isFalse();
     }
 
     /**
@@ -480,6 +484,8 @@
         ensureTaskPlacement(fullscreenTask, secondActivity);
         assertTrue(pinnedRootTask.inPinnedWindowingMode());
         assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
+        assertThat(pinnedRootTask.autoRemoveRecents).isTrue();
+        assertThat(secondActivity.getTask().autoRemoveRecents).isFalse();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index e01cea3..4bc87b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -42,6 +42,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
@@ -222,6 +223,27 @@
     }
 
     @Test
+    public void testReparentPinnedActivityBackToOriginalTask() {
+        final ActivityRecord activityMain = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task originalTask = activityMain.getTask();
+        final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build();
+        activityPip.setState(RESUMED, "test");
+        mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip,
+                null /* launchIntoPipHostActivity */, "test");
+        final Task pinnedActivityTask = activityPip.getTask();
+
+        // Simulate pinnedActivityTask unintentionally added to recent during top activity resume.
+        mAtm.getRecentTasks().getRawTasks().add(pinnedActivityTask);
+
+        // Reparent the activity back to its original task when exiting PIP mode.
+        pinnedActivityTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        assertThat(activityPip.getTask()).isEqualTo(originalTask);
+        assertThat(originalTask.autoRemoveRecents).isFalse();
+        assertThat(mAtm.getRecentTasks().getRawTasks()).containsExactly(originalTask);
+    }
+
+    @Test
     public void testReparent_BetweenDisplays() {
         // Create first task on primary display.
         final Task rootTask1 = createTask(mDisplayContent);
@@ -1991,7 +2013,7 @@
     public void getTaskInfoPropagatesCameraCompatMode() {
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = task.getTopMostActivity();
-        activity.mLetterboxUiController
+        activity.mAppCompatController.getAppCompatCameraOverrides()
                 .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT);
 
         assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT,
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
new file mode 100644
index 0000000..fe46db8
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable ProvisionSubscriberId;
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java b/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
new file mode 100644
index 0000000..796c82d
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * ProvisionSubscriberId
+ *
+ * Satellite Gateway client will use these subscriber ids to register with satellite gateway service
+ * which identify user subscription with unique subscriber ids. These subscriber ids can be any
+ * unique value like iccid, imsi or msisdn which is decided based upon carrier requirements.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public final class ProvisionSubscriberId implements Parcelable {
+    /** provision subscriberId */
+    @NonNull
+    private String mSubscriberId;
+
+    /** carrier id */
+    private int mCarrierId;
+
+    /**
+     * @hide
+     */
+    public ProvisionSubscriberId(@NonNull String subscriberId, @NonNull int carrierId) {
+        this.mCarrierId = carrierId;
+        this.mSubscriberId = subscriberId;
+    }
+
+    private ProvisionSubscriberId(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mSubscriberId);
+        out.writeInt(mCarrierId);
+    }
+
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final @android.annotation.NonNull Creator<ProvisionSubscriberId> CREATOR =
+            new Creator<ProvisionSubscriberId>() {
+                @Override
+                public ProvisionSubscriberId createFromParcel(Parcel in) {
+                    return new ProvisionSubscriberId(in);
+                }
+
+                @Override
+                public ProvisionSubscriberId[] newArray(int size) {
+                    return new ProvisionSubscriberId[size];
+                }
+            };
+
+    /**
+     * @hide
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return token.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public String getSubscriberId() {
+        return mSubscriberId;
+    }
+
+    /**
+     * @return carrierId.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("SubscriberId:");
+        sb.append(mSubscriberId);
+        sb.append(",");
+
+        sb.append("CarrierId:");
+        sb.append(mCarrierId);
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSubscriberId, mCarrierId);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ProvisionSubscriberId that = (ProvisionSubscriberId) o;
+        return mSubscriberId.equals(that.mSubscriberId) && mCarrierId
+                == that.mCarrierId;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mSubscriberId = in.readString();
+        mCarrierId = in.readInt();
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 6caed14..b518c60d 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -50,6 +50,7 @@
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -233,6 +234,29 @@
     public static final String KEY_NTN_SIGNAL_STRENGTH = "ntn_signal_strength";
 
     /**
+     * Bundle key to get the response from
+     * {@link #requestProvisionSubscriberIds(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN =
+            "request_provision_subscriber_id";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsProvisioned(String, Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_IS_SATELLITE_PROVISIONED = "request_is_satellite_provisioned";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #provisionSatellite(List, Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_PROVISION_SATELLITE_TOKENS = "provision_satellite";
+
+
+    /**
      * The request was successfully processed.
      */
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@@ -2580,6 +2604,179 @@
         }
     }
 
+    /**
+     * Request to get list of prioritized satellite subscriber ids to be used for provision.
+     *
+     * Satellite Gateway client will use these subscriber ids to register with satellite gateway
+     * service which identify user subscription with unique subscriber ids. These subscriber ids
+     * can be any unique value like iccid, imsi or msisdn which is decided based upon carrier
+     * requirements.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     * If successful, the callback returns a list of tokens sorted in ascending priority order index
+     * 0 has the highest priority. Otherwise, it returns an error with a SatelliteException.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void requestProvisionSubscriberIds(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<List<ProvisionSubscriberId>, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN)) {
+                                List<ProvisionSubscriberId> list =
+                                        Collections.singletonList(resultData.getParcelable(
+                                                KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN,
+                                                ProvisionSubscriberId.class));
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(list)));
+                            } else {
+                                loge("KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestProvisionSubscriberIds(receiver);
+            } else {
+                loge("requestProvisionSubscriberIds() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestProvisionSubscriberIds() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Request to get provisioned status for given a satellite subscriber id.
+     *
+     * @param satelliteSubscriberId Satellite subscriber id requiring provisioned status check.
+     * @param executor The executor on which the callback will be called.
+     * @param callback callback.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void requestIsProvisioned(@NonNull String satelliteSubscriberId,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(satelliteSubscriberId);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
+                                boolean isIsProvisioned =
+                                        resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isIsProvisioned)));
+                            } else {
+                                loge("KEY_REQUEST_PROVISION_TOKENS does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsProvisioned(satelliteSubscriberId, receiver);
+            } else {
+                loge("requestIsSatelliteProvisioned() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsSatelliteProvisioned() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Deliver the list of provisioned satellite subscriber ids.
+     *
+     * @param list List of ProvisionSubscriberId.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void provisionSatellite(@NonNull List<ProvisionSubscriberId> list,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_PROVISION_SATELLITE_TOKENS)) {
+                                boolean isUpdated =
+                                        resultData.getBoolean(KEY_PROVISION_SATELLITE_TOKENS);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isUpdated)));
+                            } else {
+                                loge("KEY_REQUEST_PROVISION_TOKENS does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.provisionSatellite(list, receiver);
+            } else {
+                loge("provisionSatellite() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("provisionSatellite() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
     @Nullable
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
new file mode 100644
index 0000000..2dc8ffb
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+parcelable ProvisionSubscriberId {
+    /** provision subscriberId */
+    String subscriberId;
+
+    /** carrier id */
+    int mCarrierId;
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9b01b71..7be52ea 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -78,6 +78,7 @@
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.ProvisionSubscriberId;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.IBooleanConsumer;
@@ -3400,4 +3401,38 @@
      * @hide
      */
     void requestSatelliteSessionStats(int subId, in ResultReceiver receiver);
+
+    /**
+     * Request to get list of prioritized satellite subscriber ids to be used for provision.
+     *
+     * @param result The result receiver, which returns the list of prioritized satellite tokens
+     * to be used for provision if the request is successful or an error code if the request failed.
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestProvisionSubscriberIds(in ResultReceiver receiver);
+
+    /**
+     * Request to get provisioned status for given a satellite subscriber id.
+     *
+     * @param satelliteSubscriberId Satellite subscriber id requiring provisioned status check.
+     * @param result The result receiver, which returns the provisioned status of the token if the
+     * request is successful or an error code if the request failed.
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestIsProvisioned(in String satelliteSubscriberId, in ResultReceiver result);
+
+    /**
+     * Deliver the list of provisioned satellite subscriber ids.
+     *
+     * @param list List of provisioned satellite subscriber ids.
+     * @param result The result receiver that returns whether deliver success or fail.
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void provisionSatellite(in List<ProvisionSubscriberId> list, in ResultReceiver result);
 }
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
index fff1dd1..ed9c956 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.inputmethod.multisessiontest;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.getResponderUserId;
@@ -28,13 +30,21 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assume.assumeTrue;
+
+import android.app.UiAutomation;
 import android.content.ComponentName;
+import android.content.Context;
 import android.os.Bundle;
+import android.os.UserHandle;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
 
 import androidx.test.core.app.ActivityScenario;
 
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
+import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.After;
 import org.junit.Before;
@@ -44,8 +54,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
+import java.util.List;
+
 @RunWith(BedsteadJUnit4.class)
-@Ignore("b/345557347")
 public final class ConcurrentMultiUserTest {
 
     @ClassRule
@@ -55,6 +67,10 @@
     private static final ComponentName TEST_ACTIVITY = new ComponentName(
             getInstrumentation().getTargetContext().getPackageName(),
             MainActivity.class.getName());
+    private final Context mContext = getInstrumentation().getTargetContext();
+    private final InputMethodManager mInputMethodManager =
+            mContext.getSystemService(InputMethodManager.class);
+    private final UiAutomation mUiAutomation = getInstrumentation().getUiAutomation();
 
     private ActivityScenario<MainActivity> mActivityScenario;
     private MainActivity mActivity;
@@ -69,15 +85,18 @@
         // Launch driver activity.
         mActivityScenario = ActivityScenario.launch(MainActivity.class);
         mActivityScenario.onActivity(activity -> mActivity = activity);
+        mUiAutomation.adoptShellPermissionIdentity(INTERACT_ACROSS_USERS_FULL);
     }
 
     @After
     public void tearDown() {
+        mUiAutomation.dropShellPermissionIdentity();
         if (mActivityScenario != null) {
             mActivityScenario.close();
         }
     }
 
+    @Ignore("b/345557347")
     @Test
     public void driverShowImeNotAffectPassenger() {
         assertDriverImeHidden();
@@ -87,6 +106,58 @@
         assertPassengerImeHidden();
     }
 
+    @Test
+    public void imeListNotEmpty() {
+        List<InputMethodInfo> driverImeList = mInputMethodManager.getInputMethodList();
+        assertWithMessage("Driver IME list shouldn't be empty")
+                .that(driverImeList.isEmpty()).isFalse();
+
+        List<InputMethodInfo> passengerImeList =
+                mInputMethodManager.getInputMethodListAsUser(mPeerUserId);
+        assertWithMessage("Passenger IME list shouldn't be empty")
+                .that(passengerImeList.isEmpty()).isFalse();
+    }
+
+    @Test
+    public void enabledImeListNotEmpty() {
+        List<InputMethodInfo> driverEnabledImeList =
+                mInputMethodManager.getEnabledInputMethodList();
+        assertWithMessage("Driver enabled IME list shouldn't be empty")
+                .that(driverEnabledImeList.isEmpty()).isFalse();
+
+        List<InputMethodInfo> passengerEnabledImeList =
+                mInputMethodManager.getEnabledInputMethodListAsUser(UserHandle.of(mPeerUserId));
+        assertWithMessage("Passenger enabled IME list shouldn't be empty")
+                .that(passengerEnabledImeList.isEmpty()).isFalse();
+    }
+
+    @Test
+    public void currentImeNotNull() {
+        InputMethodInfo driverIme = mInputMethodManager.getCurrentInputMethodInfo();
+        assertWithMessage("Driver IME shouldn't be null").that(driverIme).isNotNull();
+
+        InputMethodInfo passengerIme =
+                mInputMethodManager.getCurrentInputMethodInfoAsUser(UserHandle.of(mPeerUserId));
+        assertWithMessage("Passenger IME shouldn't be null")
+                .that(passengerIme).isNotNull();
+    }
+
+    @Test
+    public void enableDisableImePerUser() throws IOException {
+        UserHandle driver = UserHandle.of(mContext.getUserId());
+        UserHandle passenger = UserHandle.of(mPeerUserId);
+        enableDisableImeForUser(driver, passenger);
+        enableDisableImeForUser(passenger, driver);
+    }
+
+    @Test
+    public void setImePerUser() throws IOException {
+        UserHandle driver = UserHandle.of(mContext.getUserId());
+        UserHandle passenger = UserHandle.of(mPeerUserId);
+        setImeForUser(driver, passenger);
+        setImeForUser(passenger, driver);
+    }
+
     private void assertDriverImeHidden() {
         assertWithMessage("Driver IME should be hidden")
                 .that(mActivity.isMyImeVisible()).isFalse();
@@ -104,4 +175,89 @@
     private void showDriverImeAndAssert() {
         mActivity.showMyImeAndWait();
     }
+
+    /**
+     * Disables/enables IME for {@code user1}, then verifies that the IME settings for {@code user1}
+     * has changed as expected and {@code user2} stays the same.
+     */
+    private void enableDisableImeForUser(UserHandle user1, UserHandle user2) throws IOException {
+        List<InputMethodInfo> user1EnabledImeList =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user1);
+        List<InputMethodInfo> user2EnabledImeList =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user2);
+
+        // Disable an IME for user1.
+        InputMethodInfo imeToDisable = user1EnabledImeList.get(0);
+        SystemUtil.runShellCommand(mUiAutomation,
+                "ime disable --user " + user1.getIdentifier() + " " + imeToDisable.getId());
+        List<InputMethodInfo> user1EnabledImeList2 =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user1);
+        List<InputMethodInfo> user2EnabledImeList2 =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user2);
+        assertWithMessage("User " + user1.getIdentifier() + " IME " + imeToDisable.getId()
+                + " should be disabled")
+                .that(user1EnabledImeList2.contains(imeToDisable)).isFalse();
+        assertWithMessage("Disabling user " + user1.getIdentifier()
+                + " IME shouldn't affect user " + user2.getIdentifier())
+                .that(user2EnabledImeList2.containsAll(user2EnabledImeList)
+                        && user2EnabledImeList.containsAll(user2EnabledImeList2))
+                .isTrue();
+
+        // Enable the IME.
+        SystemUtil.runShellCommand(mUiAutomation,
+                "ime enable --user " + user1.getIdentifier() + " " + imeToDisable.getId());
+        List<InputMethodInfo> user1EnabledImeList3 =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user1);
+        List<InputMethodInfo> user2EnabledImeList3 =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user2);
+        assertWithMessage("User " + user1.getIdentifier() + " IME " + imeToDisable.getId()
+                + " should be enabled").that(user1EnabledImeList3.contains(imeToDisable)).isTrue();
+        assertWithMessage("Enabling user " + user1.getIdentifier()
+                + " IME shouldn't affect user " + user2.getIdentifier())
+                .that(user2EnabledImeList2.containsAll(user2EnabledImeList3)
+                        && user2EnabledImeList3.containsAll(user2EnabledImeList2))
+                .isTrue();
+    }
+
+    /**
+     * Sets/resets IME for {@code user1}, then verifies that the IME settings for {@code user1}
+     * has changed as expected and {@code user2} stays the same.
+     */
+    private void setImeForUser(UserHandle user1, UserHandle user2) throws IOException {
+        // Reset IME for user1.
+        SystemUtil.runShellCommand(mUiAutomation,
+                "ime reset --user " + user1.getIdentifier());
+
+        List<InputMethodInfo> user1EnabledImeList =
+                mInputMethodManager.getEnabledInputMethodListAsUser(user1);
+        assumeTrue("There must be at least two IME to test", user1EnabledImeList.size() >= 2);
+        InputMethodInfo user1Ime = mInputMethodManager.getCurrentInputMethodInfoAsUser(user1);
+        InputMethodInfo user2Ime = mInputMethodManager.getCurrentInputMethodInfoAsUser(user2);
+
+        // Set to another IME for user1.
+        InputMethodInfo anotherIme = null;
+        for (InputMethodInfo info : user1EnabledImeList) {
+            if (!info.equals(user1Ime)) {
+                anotherIme = info;
+            }
+        }
+        SystemUtil.runShellCommand(mUiAutomation,
+                "ime set --user " + user1.getIdentifier() + " " + anotherIme.getId());
+        InputMethodInfo user1Ime2 = mInputMethodManager.getCurrentInputMethodInfoAsUser(user1);
+        InputMethodInfo user2Ime2 = mInputMethodManager.getCurrentInputMethodInfoAsUser(user2);
+        assertWithMessage("The current IME for user " + user1.getIdentifier() + " is wrong")
+                .that(user1Ime2).isEqualTo(anotherIme);
+        assertWithMessage("The current IME for user " + user2.getIdentifier() + " shouldn't change")
+                .that(user2Ime2).isEqualTo(user2Ime);
+
+        // Reset IME for user1.
+        SystemUtil.runShellCommand(mUiAutomation,
+                "ime reset --user " + user1.getIdentifier());
+        InputMethodInfo user1Ime3 = mInputMethodManager.getCurrentInputMethodInfoAsUser(user1);
+        InputMethodInfo user2Ime3 = mInputMethodManager.getCurrentInputMethodInfoAsUser(user2);
+        assertWithMessage("The current IME for user " + user1.getIdentifier() + " is wrong")
+                .that(user1Ime3).isEqualTo(user1Ime);
+        assertWithMessage("The current IME for user " + user2.getIdentifier() + " shouldn't change")
+                .that(user2Ime3).isEqualTo(user2Ime);
+    }
 }
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java
index f126000..3c97b79 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java
@@ -87,11 +87,13 @@
             }
             if (!mImm.showSoftInput(mEditor, /* flags= */ 0)) {
                 Log.e(TAG, String.format("Failed to show my IME as user %d, "
-                                + "mEditor:focused=%b,hasWindowFocus=%b", getUserId(),
+                                + "mEditor:focused=%b,hasWindowFocus=%b",
+                        Process.myUserHandle().getIdentifier(),
                         mEditor.isFocused(), mEditor.hasWindowFocus()));
             }
         });
         PollingCheck.waitFor(WAIT_IME_TIMEOUT_MS, () -> isMyImeVisible(),
-                String.format("My IME (user %d) didn't show up", getUserId()));
+                String.format("My IME (user %d) didn't show up",
+                        Process.myUserHandle().getIdentifier()));
     }
 }
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 0f1373c..2d5b50b 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -17,7 +17,6 @@
 package com.android.protolog.tool
 
 import com.android.internal.protolog.common.LogLevel
-import com.android.internal.protolog.common.ProtoLog
 import com.android.internal.protolog.common.ProtoLogToolInjected
 import com.android.protolog.tool.CommandOptions.Companion.USAGE
 import com.github.javaparser.ParseProblemException
@@ -61,6 +60,8 @@
     const val PROTOLOG_IMPL_SRC_PATH =
         "frameworks/base/core/java/com/android/internal/protolog/ProtoLogImpl.java"
 
+    private const val PROTOLOG_CLASS_NAME = "ProtoLog"; // ProtoLog::class.java.simpleName
+
     data class LogCall(
         val messageString: String,
         val logLevel: LogLevel,
@@ -124,7 +125,7 @@
                     val text = injector.readText(file)
                     val outSrc = try {
                         val code = tryParse(text, path)
-                        if (containsProtoLogText(text, ProtoLog::class.java.simpleName)) {
+                        if (containsProtoLogText(text, PROTOLOG_CLASS_NAME)) {
                             transformer.processClass(text, path, packagePath(file, code), code)
                         } else {
                             text
diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
index 822118c..2a83677 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
@@ -35,7 +35,7 @@
         val output = run(
                 srcs = mapOf("frameworks/base/org/example/Example.java" to """
                     package org.example;
-                    import com.android.internal.protolog.common.ProtoLog;
+                    import com.android.internal.protolog.ProtoLog;
                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
 
                     class Example {
@@ -48,7 +48,7 @@
                 """.trimIndent()),
                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
                 commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
-                        "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
+                        "--protolog-class", "com.android.internal.protolog.ProtoLog",
                         "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
                         "--loggroups-jar", "not_required.jar",
                         "--viewer-config-file-path", "not_required.pb",
@@ -69,7 +69,7 @@
         val output = run(
                 srcs = mapOf("frameworks/base/org/example/Example.java" to """
                     package org.example;
-                    import com.android.internal.protolog.common.ProtoLog;
+                    import com.android.internal.protolog.ProtoLog;
                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
 
                     class Example {
@@ -82,7 +82,7 @@
                 """.trimIndent()),
                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
                 commandOptions = CommandOptions(arrayOf("generate-viewer-config",
-                        "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
+                        "--protolog-class", "com.android.internal.protolog.ProtoLog",
                         "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
                         "--loggroups-jar", "not_required.jar",
                         "--viewer-config-type", "json",