Merge "Drop VDM permissions from Shell" into main
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index c2f6e30..87c9fa4 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -106,7 +106,6 @@
 static const int TEXT_MISSING_VALUE = INT_MIN;
 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
 static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
-static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
 static const char CLOCK_ENABLED_PROP_NAME[] = "persist.sys.bootanim.clock.enabled";
 static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
 static const int MAX_CHECK_EXIT_INTERVAL_US = 50000;
@@ -514,50 +513,9 @@
         return NAME_NOT_FOUND;
     }
 
-    // this system property specifies multi-display IDs to show the boot animation
-    // multiple ids can be set with comma (,) as separator, for example:
-    // setprop persist.boot.animation.displays 19260422155234049,19261083906282754
-    Vector<PhysicalDisplayId> physicalDisplayIds;
-    char displayValue[PROPERTY_VALUE_MAX] = "";
-    property_get(DISPLAYS_PROP_NAME, displayValue, "");
-    bool isValid = displayValue[0] != '\0';
-    if (isValid) {
-        char *p = displayValue;
-        while (*p) {
-            if (!isdigit(*p) && *p != ',') {
-                isValid = false;
-                break;
-            }
-            p ++;
-        }
-        if (!isValid)
-            SLOGE("Invalid syntax for the value of system prop: %s", DISPLAYS_PROP_NAME);
-    }
-    if (isValid) {
-        std::istringstream stream(displayValue);
-        for (PhysicalDisplayId id; stream >> id.value; ) {
-            physicalDisplayIds.add(id);
-            if (stream.peek() == ',')
-                stream.ignore();
-        }
-
-        // the first specified display id is used to retrieve mDisplayToken
-        for (const auto id : physicalDisplayIds) {
-            if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
-                if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) {
-                    mDisplayToken = token;
-                    break;
-                }
-            }
-        }
-    }
-
-    // If the system property is not present or invalid, display 0 is used
+    mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     if (mDisplayToken == nullptr) {
-        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
-        if (mDisplayToken == nullptr) {
-            return NAME_NOT_FOUND;
-        }
+        return NAME_NOT_FOUND;
     }
 
     DisplayMode displayMode;
@@ -577,17 +535,8 @@
             ISurfaceComposerClient::eOpaque);
 
     SurfaceComposerClient::Transaction t;
-    if (isValid) {
-        // In the case of multi-display, boot animation shows on the specified displays
-        for (const auto id : physicalDisplayIds) {
-            if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
-                if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) {
-                    t.setDisplayLayerStack(token, ui::DEFAULT_LAYER_STACK);
-                }
-            }
-        }
-        t.setLayerStack(control, ui::DEFAULT_LAYER_STACK);
-    }
+    t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
+    t.setLayerStack(control, ui::DEFAULT_LAYER_STACK);
 
     t.setLayer(control, 0x40000000)
         .apply();
diff --git a/core/api/current.txt b/core/api/current.txt
index d1c0c42..efede83 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26918,7 +26918,6 @@
     field public static final int STATE_FAST_FORWARDING = 4; // 0x4
     field public static final int STATE_NONE = 0; // 0x0
     field public static final int STATE_PAUSED = 2; // 0x2
-    field @FlaggedApi("com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change") public static final int STATE_PLAYBACK_SUPPRESSED = 12; // 0xc
     field public static final int STATE_PLAYING = 3; // 0x3
     field public static final int STATE_REWINDING = 5; // 0x5
     field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7e43e46..8a0b1ba 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3495,8 +3495,8 @@
     method @NonNull public android.content.Context createContext();
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
     method @FlaggedApi("android.companion.virtual.flags.virtual_camera") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.camera.VirtualCamera createVirtualCamera(@NonNull android.companion.virtual.camera.VirtualCameraConfig);
-    method @Deprecated @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
-    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig);
     method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -3509,8 +3509,9 @@
     method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method public int getDeviceId();
     method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId();
-    method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList();
-    method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList();
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void goToSleep();
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
     method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
     method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
@@ -3521,6 +3522,7 @@
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDisplayImePolicy(int, int);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void wakeUp();
   }
 
   public final class VirtualDeviceParams implements android.os.Parcelable {
@@ -8540,11 +8542,14 @@
     method public long getAudioHandle();
     method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
     method public long getAvDataId();
+    method @FlaggedApi("android.media.tv.flags.tuner_w_apis") public int getDataGroupId();
     method public long getDataLength();
     method public long getDts();
     method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
+    method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @IntRange(from=0) public int getIndexInDataGroup();
     method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
     method @IntRange(from=0) public int getMpuSequenceNumber();
+    method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @IntRange(from=0) public int getNumDataPieces();
     method public long getOffset();
     method public long getPts();
     method public int getScIndexMask();
@@ -11951,6 +11956,10 @@
   }
 
   @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount {
+    method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static int getNumberOfMovableLocalContacts(@NonNull android.content.ContentResolver);
+    method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static int getNumberOfMovableSimContacts(@NonNull android.content.ContentResolver);
+    method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static void moveLocalContactsToCloudDefaultAccount(@NonNull android.content.ContentResolver);
+    method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static void moveSimContactsToCloudDefaultAccount(@NonNull android.content.ContentResolver);
     method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static void setDefaultAccountForNewContacts(@NonNull android.content.ContentResolver, @NonNull android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState);
   }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d318812..3bd121a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1323,4 +1323,12 @@
      */
     public abstract void killApplicationSync(String pkgName, int appId, int userId,
             String reason, int exitInfoReason);
+
+    /**
+     * Add a creator token for all embedded intents (stored as extra) of the given intent.
+     *
+     * @param intent The given intent
+     * @hide
+     */
+    public abstract void addCreatorToken(Intent intent);
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 3714e5d..393ec8c 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -1094,6 +1094,9 @@
             @Nullable String requiredPermission, @Nullable Bundle options)
             throws CanceledException {
         try {
+            if (intent != null) {
+                intent.collectExtraIntentKeys();
+            }
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 6fe0a73..40debe8 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -31,6 +31,8 @@
 import android.content.IntentFilter;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
 import android.hardware.input.VirtualDpadConfig;
 import android.hardware.input.VirtualKeyboardConfig;
 import android.hardware.input.VirtualKeyEvent;
@@ -93,6 +95,18 @@
      */
     boolean canCreateMirrorDisplays();
 
+    /*
+     * Turns off all trusted non-mirror displays of the virtual device.
+     */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+    void goToSleep();
+
+    /**
+     * Turns on all trusted non-mirror displays of the virtual device.
+     */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+    void wakeUp();
+
     /**
      * Closes the virtual device and frees all associated resources.
      */
@@ -137,6 +151,13 @@
     void onAudioSessionEnded();
 
     /**
+     * Creates a virtual display and registers it with the display framework.
+     */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+    int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
+            in IVirtualDisplayCallback callback);
+
+    /**
      * Creates a new dpad and registers it with the input framework with the given token.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
@@ -189,6 +210,7 @@
      * Returns the ID of the device corresponding to the given token, as registered with the input
      * framework.
      */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     int getInputDeviceId(IBinder token);
 
     /**
@@ -260,6 +282,7 @@
     /**
      * Launches a pending intent on the given display that is owned by this virtual device.
      */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
             in ResultReceiver resultReceiver);
 
@@ -267,6 +290,7 @@
      * Returns the current cursor position of the mouse corresponding to the given token, in x and y
      * coordinates.
      */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     PointF getCursorPosition(IBinder token);
 
     /** Sets whether to show or hide the cursor while this virtual device is active. */
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 83e18ec..c98238c 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -23,8 +23,6 @@
 import android.companion.virtual.VirtualDevice;
 import android.companion.virtual.VirtualDeviceParams;
 import android.content.AttributionSource;
-import android.hardware.display.IVirtualDisplayCallback;
-import android.hardware.display.VirtualDisplayConfig;
 
 /**
  * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
@@ -96,18 +94,6 @@
     int getDevicePolicy(int deviceId, int policyType);
 
     /**
-     * Creates a virtual display owned by a particular virtual device.
-     *
-     * @param virtualDisplayConfig The configuration used in creating the display
-     * @param callback A callback that receives display lifecycle events
-     * @param virtualDevice The device that will own this display
-     * @param packageName The package name of the calling app
-     */
-    int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
-            in IVirtualDisplayCallback callback, in IVirtualDevice virtualDevice,
-            String packageName);
-
-    /**
      * Returns device-specific session id for playback, or AUDIO_SESSION_ID_GENERATE
      * if there's none.
      */
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index de20a68..6708cce 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -242,6 +242,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     List<VirtualSensor> getVirtualSensorList() {
         try {
@@ -251,6 +252,25 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    void goToSleep() {
+        try {
+            mVirtualDevice.goToSleep();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    void wakeUp() {
+        try {
+            mVirtualDevice.wakeUp();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void launchPendingIntent(
             int displayId,
             @NonNull PendingIntent pendingIntent,
@@ -272,6 +292,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Nullable
     VirtualDisplay createVirtualDisplay(
             @NonNull VirtualDisplayConfig config,
@@ -281,16 +302,15 @@
                 new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor);
         final int displayId;
         try {
-            displayId = mService.createVirtualDisplay(config, callbackWrapper, mVirtualDevice,
-                    mContext.getPackageName());
+            displayId = mVirtualDevice.createVirtualDisplay(config, callbackWrapper);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
         DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
-        return displayManager.createVirtualDisplayWrapper(config, callbackWrapper,
-                displayId);
+        return displayManager.createVirtualDisplayWrapper(config, callbackWrapper, displayId);
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void close() {
         try {
             // This also takes care of unregistering all virtual sensors.
@@ -304,6 +324,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
         switch (policyType) {
@@ -323,6 +344,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         try {
             mVirtualDevice.addActivityPolicyExemption(exemption);
@@ -331,6 +353,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         try {
             mVirtualDevice.removeActivityPolicyExemption(exemption);
@@ -339,6 +362,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setDevicePolicyForDisplay(int displayId,
             @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
@@ -358,6 +382,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
         try {
@@ -370,6 +395,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) {
         try {
@@ -382,6 +408,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) {
         try {
@@ -394,6 +421,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualTouchscreen createVirtualTouchscreen(
             @NonNull VirtualTouchscreenConfig config) {
@@ -433,6 +461,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualNavigationTouchpad createVirtualNavigationTouchpad(
             @NonNull VirtualNavigationTouchpadConfig config) {
@@ -447,6 +476,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualAudioDevice createVirtualAudioDevice(
             @NonNull VirtualDisplay display,
@@ -483,6 +513,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setShowPointerIcon(boolean showPointerIcon) {
         try {
             mVirtualDevice.setShowPointerIcon(showPointerIcon);
@@ -491,6 +522,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
         try {
             mVirtualDevice.setDisplayImePolicy(displayId, policy);
@@ -532,6 +564,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void registerIntentInterceptor(
             @NonNull IntentFilter interceptorFilter,
             @CallbackExecutor @NonNull Executor executor,
@@ -551,6 +584,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void unregisterIntentInterceptor(
             @NonNull VirtualDeviceManager.IntentInterceptorCallback interceptorCallback) {
         Objects.requireNonNull(interceptorCallback);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 473ab27..96700a9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -614,12 +614,52 @@
          *
          * @return A list of all sensors for this device, or an empty list if no sensors exist.
          */
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public List<VirtualSensor> getVirtualSensorList() {
             return mVirtualDeviceInternal.getVirtualSensorList();
         }
 
         /**
+         * Forces all trusted non-mirror displays of the virtual device to turn off.
+         *
+         * <p>After this action, if all displays across all devices, including the default one, are
+         * off, then the physical device will be put to sleep. If the displays of this virtual
+         * device are already off, then nothing will happen.</p>
+         *
+         * <p>Overrides all the wake locks that are held. This is equivalent to pressing a "virtual
+         * power key" to turn off the screen.</p>
+         *
+         * @see #wakeUp()
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+         */
+        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        public void goToSleep() {
+            mVirtualDeviceInternal.goToSleep();
+        }
+
+        /**
+         * Forces all trusted non-mirror displays of the virtual device to turn on.
+         *
+         * <p>If the displays of this virtual device are turned off, then they will be turned on.
+         * Additionally, if the device is asleep it will be awoken. If the displays of this virtual
+         * device are already on, then nothing will happen.</p>
+         *
+         * <p>This is equivalent to pressing a "virtual power key" to turn on the screen.</p>
+         *
+         * @see #goToSleep()
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+         */
+        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        public void wakeUp() {
+            mVirtualDeviceInternal.wakeUp();
+        }
+
+        /**
          * Launches a given pending intent on the give display ID.
          *
          * @param displayId The display to launch the pending intent on. This display must be
@@ -637,6 +677,7 @@
          *   on the virtual display, or one of the {@code LAUNCH_FAILED} status explaining why it
          *   failed.
          */
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void launchPendingIntent(
                 int displayId,
                 @NonNull PendingIntent pendingIntent,
@@ -677,6 +718,7 @@
          * VirtualDisplay.Callback)}
          */
         @Deprecated
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @Nullable
         public VirtualDisplay createVirtualDisplay(
                 @IntRange(from = 1) int width,
@@ -714,6 +756,7 @@
          *
          * @see DisplayManager#createVirtualDisplay
          */
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @Nullable
         public VirtualDisplay createVirtualDisplay(
                 @NonNull VirtualDisplayConfig config,
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index b421339..ff0bb25 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -1149,7 +1149,7 @@
         for (int i = 0; i < size; i++) {
             final Item item = mItems.get(i);
             if (item.mIntent != null) {
-                item.mIntent.prepareToLeaveProcess(leavingPackage);
+                item.mIntent.prepareToLeaveProcess(leavingPackage, false);
             }
             if (item.mUri != null && leavingPackage) {
                 if (StrictMode.vmFileUriExposureEnabled()) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9a93ec4..0bb0027 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12221,6 +12221,8 @@
 
     /**
      * Collects keys in the extra bundle whose value are intents.
+     * With these keys collected on the client side, the system server would only unparcel values
+     * of these keys and create IntentCreatorToken for them.
      * @hide
      */
     public void collectExtraIntentKeys() {
@@ -12583,22 +12585,29 @@
      */
     @android.ravenwood.annotation.RavenwoodThrow
     public void prepareToLeaveProcess(boolean leavingPackage) {
+        prepareToLeaveProcess(leavingPackage, true);
+    }
+
+    /**
+     * @hide
+     */
+    void prepareToLeaveProcess(boolean leavingPackage, boolean isTopLevel) {
         setAllowFds(false);
 
         if (mSelector != null) {
-            mSelector.prepareToLeaveProcess(leavingPackage);
+            mSelector.prepareToLeaveProcess(leavingPackage, false);
         }
         if (mClipData != null) {
             mClipData.prepareToLeaveProcess(leavingPackage, getFlags());
         }
         if (mOriginalIntent != null) {
-            mOriginalIntent.prepareToLeaveProcess(leavingPackage);
+            mOriginalIntent.prepareToLeaveProcess(leavingPackage, false);
         }
 
         if (mExtras != null && !mExtras.isParcelled()) {
             final Object intent = mExtras.get(Intent.EXTRA_INTENT);
             if (intent instanceof Intent) {
-                ((Intent) intent).prepareToLeaveProcess(leavingPackage);
+                ((Intent) intent).prepareToLeaveProcess(leavingPackage, false);
             }
         }
 
@@ -12672,6 +12681,10 @@
                 StrictMode.onUnsafeIntentLaunch(this);
             }
         }
+
+        if (isTopLevel) {
+            collectExtraIntentKeys();
+        }
     }
 
     /**
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index ca6d86a..f406927 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -288,6 +288,9 @@
             @Nullable Executor executor, @Nullable OnFinished onFinished)
             throws SendIntentException {
         try {
+            if (intent != null) {
+                intent.collectExtraIntentKeys();
+            }
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
diff --git a/core/java/android/hardware/DisplayLuts.java b/core/java/android/hardware/DisplayLuts.java
new file mode 100644
index 0000000..b162ad6
--- /dev/null
+++ b/core/java/android/hardware/DisplayLuts.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class DisplayLuts {
+    private IntArray mOffsets;
+    private int mTotalLength;
+
+    private List<float[]> mLutBuffers;
+    private IntArray mLutDimensions;
+    private IntArray mLutSizes;
+    private IntArray mLutSamplingKeys;
+    private static final int LUT_LENGTH_LIMIT = 100000;
+
+    public DisplayLuts() {
+        mOffsets = new IntArray();
+        mTotalLength = 0;
+
+        mLutBuffers = new ArrayList<>();
+        mLutDimensions = new IntArray();
+        mLutSizes = new IntArray();
+        mLutSamplingKeys = new IntArray();
+    }
+
+    /**
+     * Add the lut to be applied.
+     *
+     * @param buffer
+     * @param dimension either 1D or 3D
+     * @param size
+     * @param samplingKey
+     */
+    public void addLut(@NonNull float[] buffer, @LutProperties.Dimension int dimension,
+                       int size, @LutProperties.SamplingKey int samplingKey) {
+
+        int lutLength = 0;
+        if (dimension == LutProperties.ONE_DIMENSION) {
+            lutLength = size;
+        } else if (dimension == LutProperties.THREE_DIMENSION) {
+            lutLength = size * size * size;
+        } else {
+            clear();
+            throw new IllegalArgumentException("The dimension is either 1D or 3D!");
+        }
+
+        if (lutLength >= LUT_LENGTH_LIMIT) {
+            clear();
+            throw new IllegalArgumentException("The lut length is too big to handle!");
+        }
+
+        mOffsets.add(mTotalLength);
+        mTotalLength += lutLength;
+
+        mLutBuffers.add(buffer);
+        mLutDimensions.add(dimension);
+        mLutSizes.add(size);
+        mLutSamplingKeys.add(samplingKey);
+    }
+
+    private void clear() {
+        mTotalLength = 0;
+        mOffsets.clear();
+        mLutBuffers.clear();
+        mLutDimensions.clear();
+        mLutSamplingKeys.clear();
+    }
+
+    /**
+     * @return the array of Lut buffers
+     */
+    public float[] getLutBuffers() {
+        float[] buffer = new float[mTotalLength];
+
+        for (int i = 0; i < mLutBuffers.size(); i++) {
+            float[] lutBuffer = mLutBuffers.get(i);
+            System.arraycopy(lutBuffer, 0, buffer, mOffsets.get(i), lutBuffer.length);
+        }
+        return buffer;
+    }
+
+    /**
+     * @return the starting point of each lut memory region of the lut buffer
+     */
+    public int[] getOffsets() {
+        return mOffsets.toArray();
+    }
+
+    /**
+     * @return the array of Lut size
+     */
+    public int[] getLutSizes() {
+        return mLutSizes.toArray();
+    }
+
+    /**
+     * @return the array of Lut dimension
+     */
+    public int[] getLutDimensions() {
+        return mLutDimensions.toArray();
+    }
+
+    /**
+     * @return the array of sampling key
+     */
+    public int[] getLutSamplingKeys() {
+        return mLutSamplingKeys.toArray();
+    }
+}
diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java
new file mode 100644
index 0000000..57f8a4e
--- /dev/null
+++ b/core/java/android/hardware/LutProperties.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Lut properties class.
+ *
+ * A Lut (Look-Up Table) is a pre-calculated table for color transformation.
+ *
+ * @hide
+ */
+public final class LutProperties {
+    private final @Dimension int mDimension;
+    private final long mSize;
+    private final @SamplingKey int[] mSamplingKeys;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SAMPLING_KEY_"}, value = {
+        SAMPLING_KEY_RGB,
+        SAMPLING_KEY_MAX_RGB
+    })
+    public @interface SamplingKey {
+    }
+
+    /** use r,g,b channel as the gain value of a Lut */
+    public static final int SAMPLING_KEY_RGB = 0;
+
+    /** use max of r,g,b channel as the gain value of a Lut */
+    public static final int SAMPLING_KEY_MAX_RGB = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+        ONE_DIMENSION,
+        THREE_DIMENSION
+    })
+    public @interface Dimension {
+    }
+
+    /** The Lut is one dimensional */
+    public static final int ONE_DIMENSION = 1;
+
+    /** The Lut is three dimensional */
+    public static final int THREE_DIMENSION = 3;
+
+    public @Dimension int getDimension() {
+        return mDimension;
+    }
+
+    /**
+     * @return the size of the Lut.
+     */
+    public long getSize() {
+        return mSize;
+    }
+
+    /**
+     * @return the list of sampling keys
+     */
+    public @SamplingKey int[] getSamplingKeys() {
+        if (mSamplingKeys.length == 0) {
+            throw new IllegalStateException("no sampling key!");
+        }
+        return mSamplingKeys;
+    }
+
+    /* use in the native code */
+    private LutProperties(@Dimension int dimension, long size, @SamplingKey int[] samplingKeys) {
+        if (dimension != ONE_DIMENSION || dimension != THREE_DIMENSION) {
+            throw new IllegalArgumentException("The dimension is either 1 or 3!");
+        }
+        mDimension = dimension;
+        mSize = size;
+        mSamplingKeys = samplingKeys;
+    }
+}
diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java
index 7b452a8..24cfc1b 100644
--- a/core/java/android/hardware/OverlayProperties.java
+++ b/core/java/android/hardware/OverlayProperties.java
@@ -50,6 +50,8 @@
     // Invoked on destruction
     private Runnable mCloser;
 
+    private LutProperties[] mLutProperties;
+
     private OverlayProperties(long nativeObject) {
         if (nativeObject != 0) {
             mCloser = sRegistry.registerNativeAllocation(this, nativeObject);
@@ -70,6 +72,20 @@
     }
 
     /**
+     * Gets the lut properties of the display.
+     * @hide
+     */
+    public LutProperties[] getLutProperties() {
+        if (mNativeObject == 0) {
+            return null;
+        }
+        if (mLutProperties == null) {
+            mLutProperties = nGetLutProperties(mNativeObject);
+        }
+        return mLutProperties;
+    }
+
+    /**
      * Indicates that hardware composition of a buffer encoded with the provided {@link DataSpace}
      * and {@link HardwareBuffer.Format} is supported on the device.
      *
@@ -140,4 +156,5 @@
             long nativeObject, int dataspace, int format);
     private static native void nWriteOverlayPropertiesToParcel(long nativeObject, Parcel dest);
     private static native long nReadOverlayPropertiesFromParcel(Parcel in);
-}
+    private static native LutProperties[] nGetLutProperties(long nativeObject);
+}
\ No newline at end of file
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index d557046..e65f0d2 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3028,6 +3028,13 @@
         @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
         public static final class DefaultAccount {
             /**
+             * no public constructor since this is a utility class
+             */
+            private DefaultAccount() {
+
+            }
+
+            /**
              * Key in the outgoing Bundle for the default account list.
              *
              * @hide
@@ -3063,11 +3070,6 @@
             public static final String QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD =
                     "queryDefaultAccountForNewContacts";
 
-            private DefaultAccount() {
-
-            }
-
-
             /**
              * Represents the state of the default account, and the actual {@link Account} if it's
              * a cloud account.
@@ -3356,6 +3358,161 @@
                 nullSafeCall(resolver, ContactsContract.AUTHORITY_URI,
                         SET_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, extras);
             }
+
+
+
+            /**
+             * The method to invoke to move local {@link RawContacts} and {@link Groups} from local
+             * account(s) to the Cloud Default Account (if any).
+             *
+             * @hide
+             */
+            public static final String MOVE_LOCAL_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD =
+                    "moveLocalContactsToCloudDefaultAccount";
+
+            /**
+             * Move {@link RawContacts} and {@link Groups} (if any) from the local account to the
+             * Cloud Default Account (if any).
+             * @param resolver the ContentResolver to query.
+             * @throws RuntimeException if it fails to move contacts to the default account.
+             *
+             * @hide
+             */
+            @SystemApi
+            @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
+            @RequiresPermission(allOf = {android.Manifest.permission.WRITE_CONTACTS,
+                    android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS})
+            public static void moveLocalContactsToCloudDefaultAccount(
+                    @NonNull ContentResolver resolver) {
+
+                Bundle extras = new Bundle();
+                Bundle result = nullSafeCall(
+                        resolver,
+                        ContactsContract.AUTHORITY_URI,
+                        MOVE_LOCAL_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD,
+                        null,
+                        extras);
+            }
+
+            /**
+             * The method to invoke to move {@link RawContacts} and {@link Groups} from SIM
+             * account(s) to the Cloud Default Account (if any).
+             *
+             * @hide
+             */
+            public static final String MOVE_SIM_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD =
+                    "moveSimContactsToCloudDefaultAccount";
+
+            /**
+             * Move {@link RawContacts} and {@link Groups} (if any) from the local account to the
+             * Cloud Default Account (if any).
+             * @param resolver the ContentResolver to query.
+             * @throws RuntimeException if it fails to move contacts to the default account.
+             *
+             * @hide
+             */
+            @SystemApi
+            @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
+            @RequiresPermission(allOf = {android.Manifest.permission.WRITE_CONTACTS,
+                    android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS})
+            public static void moveSimContactsToCloudDefaultAccount(
+                    @NonNull ContentResolver resolver) {
+                Bundle result = nullSafeCall(
+                        resolver,
+                        ContactsContract.AUTHORITY_URI,
+                        MOVE_SIM_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD,
+                        /* arg= */ null,
+                        /* extras= */ null);
+            }
+
+            /**
+             * The method to invoke to get the number of {@link RawContacts} that are in local
+             * account(s) and movable to the Cloud Default Account (if any).
+             *
+             * @hide
+             */
+            public static final String GET_NUMBER_OF_MOVABLE_LOCAL_CONTACTS_METHOD =
+                    "getNumberOfMovableLocalContacts";
+
+            /**
+             * The result key for moving local {@link RawContacts} and {@link Groups} from SIM
+             * account(s) to the Cloud Default Account (if any).
+             *
+             * @hide
+             */
+            public static final String KEY_NUMBER_OF_MOVABLE_LOCAL_CONTACTS =
+                    "key_number_of_movable_local_contacts";
+
+            /**
+             * Gets the number of {@link RawContacts} in the local account(s) which may be moved
+             * using {@link DefaultAccount#moveLocalContactsToCloudDefaultAccount} (if any).
+             * @param resolver the ContentResolver to query.
+             * @return the number of {@link RawContacts} in the local account(s), or 0 if there is
+             * no Cloud Default Account.
+             * @throws RuntimeException if it fails get the number of movable local contacts.
+             *
+             * @hide
+             */
+            @SystemApi
+            @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
+            @RequiresPermission(allOf = {android.Manifest.permission.READ_CONTACTS,
+                    android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS})
+            public static int getNumberOfMovableLocalContacts(
+                    @NonNull ContentResolver resolver) {
+                Bundle result = nullSafeCall(
+                        resolver,
+                        ContactsContract.AUTHORITY_URI,
+                        GET_NUMBER_OF_MOVABLE_LOCAL_CONTACTS_METHOD,
+                        /* arg= */ null,
+                        /* extras= */ null);
+                return result.getInt(KEY_NUMBER_OF_MOVABLE_LOCAL_CONTACTS,
+                        /* defaultValue= */ 0);
+            }
+
+            /**
+             * The method to invoke to get the number of {@link RawContacts} that are in SIM
+             * account(s) and movable to the Cloud Default Account (if any).
+             *
+             * @hide
+             */
+            public static final String GET_NUMBER_OF_MOVABLE_SIM_CONTACTS_METHOD =
+                    "getNumberOfMovableSimContacts";
+
+            /**
+             * The result key for moving local {@link RawContacts} and {@link Groups} from SIM
+             * account(s) to the Cloud Default Account (if any).
+             *
+             * @hide
+             */
+            public static final String KEY_NUMBER_OF_MOVABLE_SIM_CONTACTS =
+                    "key_number_of_movable_sim_contacts";
+
+            /**
+             * Gets the number of {@link RawContacts} in the SIM account(s) which may be moved using
+             * {@link DefaultAccount#moveSimContactsToCloudDefaultAccount} (if any).
+             * @param resolver the ContentResolver to query.
+             * @return the number of {@link RawContacts} in the SIM account(s), or 0 if there is
+             * no Cloud Default Account.
+             * @throws RuntimeException if it fails get the number of movable sim contacts.
+             *
+             * @hide
+             */
+            @SystemApi
+            @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
+            @RequiresPermission(allOf = {android.Manifest.permission.READ_CONTACTS,
+                    android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS})
+            public static int getNumberOfMovableSimContacts(
+                    @NonNull ContentResolver resolver) {
+                Bundle result = nullSafeCall(
+                        resolver,
+                        ContactsContract.AUTHORITY_URI,
+                        GET_NUMBER_OF_MOVABLE_SIM_CONTACTS_METHOD,
+                        /* arg= */ null,
+                        /* extras= */ null);
+                return result.getInt(KEY_NUMBER_OF_MOVABLE_SIM_CONTACTS,
+                        /* defaultValue= */ 0);
+            }
+
         }
 
         /**
@@ -9183,6 +9340,30 @@
         public static final String KEY_DEFAULT_ACCOUNT = "key_default_account";
 
         /**
+         * Key in the Bundle for the default account state.
+         *
+         * @hide
+         */
+        public static final String KEY_DEFAULT_ACCOUNT_STATE =
+                "key_default_contacts_account_state";
+
+        /**
+         * The method to invoke in order to set the default account.
+         *
+         * @hide
+         */
+        public static final String SET_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD =
+                "setDefaultAccountForNewContacts";
+
+        /**
+         * The method to invoke in order to query the default account.
+         *
+         * @hide
+         */
+        public static final String QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD =
+                "queryDefaultAccountForNewContacts";
+
+        /**
          * Get the account that is set as the default account for new contacts, which should be
          * initially selected when creating a new contact on contact management apps.
          * If the setting has not been set by any app, it will return null. Once the setting
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index f123a96..3181556 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -129,6 +129,16 @@
      * @hide
      */
     public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded";
+    /**
+     * @hide
+     */
+    public static final String MODEL_LOADED_BROADCAST_INTENT =
+        "android.service.ondeviceintelligence.MODEL_LOADED";
+    /**
+     * @hide
+     */
+    public static final String MODEL_UNLOADED_BROADCAST_INTENT =
+        "android.service.ondeviceintelligence.MODEL_UNLOADED";
 
     /**
      * @hide
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2748e32..19e244a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -49,6 +49,7 @@
 import android.gui.StalledTransactionInfo;
 import android.gui.TrustedOverlay;
 import android.hardware.DataSpace;
+import android.hardware.DisplayLuts;
 import android.hardware.HardwareBuffer;
 import android.hardware.OverlayProperties;
 import android.hardware.SyncFence;
@@ -307,9 +308,9 @@
     private static native StalledTransactionInfo nativeGetStalledTransactionInfo(int pid);
     private static native void nativeSetDesiredPresentTimeNanos(long transactionObj,
                                                                 long desiredPresentTimeNanos);
-    private static native void nativeSetFrameTimeline(long transactionObj,
-                                                           long vsyncId);
     private static native void nativeNotifyShutdown();
+    private static native void nativeSetLuts(long transactionObj, long nativeObject,
+            float[] buffers, int[] slots, int[] dimensions, int[] sizes, int[] samplingKeys);
 
     /**
      * Transforms that can be applied to buffers as they are displayed to a window.
@@ -4399,6 +4400,17 @@
             return this;
         }
 
+        /** @hide */
+        public @NonNull Transaction setLuts(@NonNull SurfaceControl sc,
+                @NonNull DisplayLuts displayLuts) {
+            checkPreconditions(sc);
+
+            nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(),
+                    displayLuts.getOffsets(), displayLuts.getLutDimensions(),
+                    displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys());
+            return this;
+        }
+
         /**
          * Sets the caching hint for the layer. By default, the caching hint is
          * {@link CACHING_ENABLED}.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 79ecfe1e..1a45939 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -117,6 +117,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import sun.misc.Cleaner;
@@ -4914,7 +4916,22 @@
                     && (root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) == 0) {
                 ViewNodeBuilder viewStructure = new ViewNodeBuilder();
                 viewStructure.setAutofillId(view.getAutofillId());
-                view.onProvideAutofillStructure(viewStructure, /* flags= */ 0);
+
+                // Post onProvideAutofillStructure to the UI thread
+                final CountDownLatch latch = new CountDownLatch(1);
+                afm.post(
+                    () -> {
+                        view.onProvideAutofillStructure(viewStructure, /* flags= */ 0);
+                        latch.countDown();
+                    }
+                );
+                try {
+                    latch.await(5000, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    return null;
+                }
+
                 // TODO(b/141703532): We don't call View#onProvideAutofillVirtualStructure for
                 //  efficiency reason. But this also means we will return null for virtual views
                 //  for now. We will add a new API to fetch the view node info of the virtual
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index 96494b1..63de195 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OverlayProperties"
 // #define LOG_NDEBUG 0
 
+#include <android/gui/LutProperties.h>
 #include <android/gui/OverlayProperties.h>
 #include <binder/Parcel.h>
 #include <gui/SurfaceComposerClient.h>
@@ -35,6 +36,12 @@
     jclass clazz;
     jmethodID ctor;
 } gOverlayPropertiesClassInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gLutPropertiesClassInfo;
+
 // ----------------------------------------------------------------------------
 // OverlayProperties lifecycle
 // ----------------------------------------------------------------------------
@@ -95,6 +102,36 @@
     return reinterpret_cast<jlong>(overlayProperties);
 }
 
+static jobjectArray android_hardware_OverlayProperties_getLutProperties(JNIEnv* env, jobject thiz,
+                                                                        jlong nativeObject) {
+    gui::OverlayProperties* overlayProperties =
+            reinterpret_cast<gui::OverlayProperties*>(nativeObject);
+    if (overlayProperties->lutProperties.has_value()) {
+        return NULL;
+    }
+    auto& lutProperties = overlayProperties->lutProperties.value();
+    if (lutProperties.empty()) {
+        return NULL;
+    }
+    int32_t size = static_cast<int32_t>(lutProperties.size());
+    jobjectArray nativeLutProperties =
+            env->NewObjectArray(size, gLutPropertiesClassInfo.clazz, NULL);
+    if (nativeLutProperties == NULL) {
+        return NULL;
+    }
+    for (int32_t i = 0; i < size; i++) {
+        if (lutProperties[i].has_value()) {
+            auto& item = lutProperties[i].value();
+            jobject properties =
+                    env->NewObject(gLutPropertiesClassInfo.clazz, gLutPropertiesClassInfo.ctor,
+                                   static_cast<int32_t>(item.dimension), item.size,
+                                   item.samplingKeys.data());
+            env->SetObjectArrayElement(nativeLutProperties, i, properties);
+        }
+    }
+    return nativeLutProperties;
+}
+
 // ----------------------------------------------------------------------------
 // Serialization
 // ----------------------------------------------------------------------------
@@ -161,6 +198,8 @@
     { "nReadOverlayPropertiesFromParcel", "(Landroid/os/Parcel;)J",
             (void*) android_hardware_OverlayProperties_read },
     {"nCreateDefault", "()J", (void*) android_hardware_OverlayProperties_createDefault },
+    {"nGetLutProperties", "(J)[Landroid/hardware/LutProperties;",
+            (void*) android_hardware_OverlayProperties_getLutProperties },
 };
 // clang-format on
 
@@ -171,5 +210,9 @@
     gOverlayPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
     gOverlayPropertiesClassInfo.ctor =
             GetMethodIDOrDie(env, gOverlayPropertiesClassInfo.clazz, "<init>", "(J)V");
+    clazz = FindClassOrDie(env, "android/hardware/LutProperties");
+    gLutPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+    gLutPropertiesClassInfo.ctor =
+            GetMethodIDOrDie(env, gLutPropertiesClassInfo.clazz, "<init>", "(IJ[I)V");
     return err;
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 71ba214..a939d92 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -33,11 +33,13 @@
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_view_SurfaceControl.h>
 #include <android_runtime/android_view_SurfaceSession.h>
+#include <cutils/ashmem.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <private/gui/ComposerService.h>
 #include <stdio.h>
@@ -736,6 +738,65 @@
     transaction->setDesiredHdrHeadroom(ctrl, desiredRatio);
 }
 
+static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+                          jfloatArray jbufferArray, jintArray joffsetArray,
+                          jintArray jdimensionArray, jintArray jsizeArray,
+                          jintArray jsamplingKeyArray) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+
+    ScopedIntArrayRW joffsets(env, joffsetArray);
+    if (joffsets.get() == nullptr) {
+        jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
+        return;
+    }
+    ScopedIntArrayRW jdimensions(env, jdimensionArray);
+    if (jdimensions.get() == nullptr) {
+        jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
+        return;
+    }
+    ScopedIntArrayRW jsizes(env, jsizeArray);
+    if (jsizes.get() == nullptr) {
+        jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
+        return;
+    }
+    ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
+    if (jsamplingKeys.get() == nullptr) {
+        jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
+        return;
+    }
+
+    jsize numLuts = env->GetArrayLength(jdimensionArray);
+    std::vector<int32_t> offsets(joffsets.get(), joffsets.get() + numLuts);
+    std::vector<int32_t> dimensions(jdimensions.get(), jdimensions.get() + numLuts);
+    std::vector<int32_t> sizes(jsizes.get(), jsizes.get() + numLuts);
+    std::vector<int32_t> samplingKeys(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
+
+    ScopedFloatArrayRW jbuffers(env, jbufferArray);
+    if (jbuffers.get() == nullptr) {
+        jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
+        return;
+    }
+
+    // create the shared memory and copy jbuffers
+    size_t bufferSize = jbuffers.size() * sizeof(float);
+    int32_t fd = ashmem_create_region("lut_shread_mem", bufferSize);
+    if (fd < 0) {
+        jniThrowRuntimeException(env, "ashmem_create_region() failed");
+        return;
+    }
+    void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if (ptr == MAP_FAILED) {
+        jniThrowRuntimeException(env, "Failed to map the shared memory");
+        return;
+    }
+    memcpy(ptr, jbuffers.get(), bufferSize);
+    // unmap
+    munmap(ptr, bufferSize);
+
+    transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys);
+}
+
 static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj,
                                  jlong nativeObject, jint cachingHint) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2541,6 +2602,7 @@
             (void*) nativeSetDesiredPresentTimeNanos },
     {"nativeNotifyShutdown", "()V",
             (void*)nativeNotifyShutdown },
+    {"nativeSetLuts", "(JJ[F[I[I[I[I)V", (void*)nativeSetLuts },
         // clang-format on
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f067b51..549f8df 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -845,6 +845,8 @@
     <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />
     <protected-broadcast android:name="com.android.uwb.uwbcountrycode.GEOCODE_RETRY" />
     <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED" />
+    <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_LOADED" />
+    <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_UNLOADED" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
diff --git a/libs/WindowManager/Shell/res/layout/pip2_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip2_menu_action.xml
new file mode 100644
index 0000000..04ece31
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/pip2_menu_action.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<com.android.wm.shell.pip2.phone.PipMenuActionView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/pip_action_size"
+    android:layout_height="@dimen/pip_action_size"
+    android:background="?android:selectableItemBackgroundBorderless"
+    android:forceHasOverlappingRendering="false">
+
+    <ImageView
+        android:id="@+id/custom_close_bg"
+        android:layout_width="@dimen/pip_custom_close_bg_size"
+        android:layout_height="@dimen/pip_custom_close_bg_size"
+        android:layout_gravity="center"
+        android:src="@drawable/pip_custom_close_bg"
+        android:visibility="gone"/>
+
+    <ImageView
+        android:id="@+id/image"
+        android:layout_width="@dimen/pip_action_inner_size"
+        android:layout_height="@dimen/pip_action_inner_size"
+        android:layout_gravity="center"
+        android:scaleType="fitXY"/>
+
+</com.android.wm.shell.pip2.phone.PipMenuActionView>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
index 5876682..85dabce 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
@@ -30,29 +30,32 @@
  */
 public class BubbleInfo implements Parcelable {
 
-    private String mKey; // Same key as the Notification
+    private final String mKey; // Same key as the Notification
     private int mFlags;  // Flags from BubbleMetadata
     @Nullable
-    private String mShortcutId;
-    private int mUserId;
-    private String mPackageName;
+    private final String mShortcutId;
+    private final int mUserId;
+    private final String mPackageName;
     /**
      * All notification bubbles require a shortcut to be set on the notification, however, the
      * app could still specify an Icon and PendingIntent to use for the bubble. In that case
      * this icon will be populated. If the bubble is entirely shortcut based, this will be null.
      */
     @Nullable
-    private Icon mIcon;
+    private final Icon mIcon;
     @Nullable
-    private String mTitle;
+    private final String mTitle;
     @Nullable
-    private String mAppName;
-    private boolean mIsImportantConversation;
-    private boolean mShowAppBadge;
+    private final String mAppName;
+    private final boolean mIsImportantConversation;
+    private final boolean mShowAppBadge;
+    @Nullable
+    private final ParcelableFlyoutMessage mParcelableFlyoutMessage;
 
     public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
             int userId, String packageName, @Nullable String title, @Nullable String appName,
-            boolean isImportantConversation, boolean showAppBadge) {
+            boolean isImportantConversation, boolean showAppBadge,
+            @Nullable ParcelableFlyoutMessage flyoutMessage) {
         mKey = key;
         mFlags = flags;
         mShortcutId = shortcutId;
@@ -63,6 +66,7 @@
         mAppName = appName;
         mIsImportantConversation = isImportantConversation;
         mShowAppBadge = showAppBadge;
+        mParcelableFlyoutMessage = flyoutMessage;
     }
 
     private BubbleInfo(Parcel source) {
@@ -76,6 +80,8 @@
         mAppName = source.readString();
         mIsImportantConversation = source.readBoolean();
         mShowAppBadge = source.readBoolean();
+        mParcelableFlyoutMessage = source.readParcelable(
+                ParcelableFlyoutMessage.class.getClassLoader(), ParcelableFlyoutMessage.class);
     }
 
     public String getKey() {
@@ -122,6 +128,11 @@
         return mShowAppBadge;
     }
 
+    @Nullable
+    public ParcelableFlyoutMessage getParcelableFlyoutMessage() {
+        return mParcelableFlyoutMessage;
+    }
+
     /**
      * Whether this bubble is currently being hidden from the stack.
      */
@@ -180,6 +191,7 @@
         parcel.writeString(mAppName);
         parcel.writeBoolean(mIsImportantConversation);
         parcel.writeBoolean(mShowAppBadge);
+        parcel.writeParcelable(mParcelableFlyoutMessage, flags);
     }
 
     @NonNull
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ParcelableFlyoutMessage.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ParcelableFlyoutMessage.kt
new file mode 100644
index 0000000..294d5e5
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ParcelableFlyoutMessage.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared.bubbles
+
+import android.graphics.drawable.Icon
+import android.os.Parcel
+import android.os.Parcelable
+
+/** The contents of the flyout message to be passed to launcher for rendering in the bubble bar. */
+class ParcelableFlyoutMessage(
+    val icon: Icon?,
+    val title: String?,
+    val message: String?,
+) : Parcelable {
+
+    constructor(
+        parcel: Parcel
+    ) : this(
+        icon = parcel.readParcelable(Icon::class.java.classLoader),
+        title = parcel.readString(),
+        message = parcel.readString(),
+    )
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeParcelable(icon, flags)
+        parcel.writeString(title)
+        parcel.writeString(message)
+    }
+
+    override fun describeContents() = 0
+
+    companion object {
+        @JvmField
+        val CREATOR =
+            object : Parcelable.Creator<ParcelableFlyoutMessage> {
+                override fun createFromParcel(parcel: Parcel) = ParcelableFlyoutMessage(parcel)
+
+                override fun newArray(size: Int) = arrayOfNulls<ParcelableFlyoutMessage>(size)
+            }
+    }
+}
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 169361a..e3fc5c2 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
@@ -56,6 +56,7 @@
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.ParcelableFlyoutMessage;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -350,7 +351,22 @@
                 getTitle(),
                 getAppName(),
                 isImportantConversation(),
-                !isAppLaunchIntent());
+                !isAppLaunchIntent(),
+                getParcelableFlyoutMessage());
+    }
+
+    /** Creates a parcelable flyout message to send to launcher. */
+    @Nullable
+    private ParcelableFlyoutMessage getParcelableFlyoutMessage() {
+        if (mFlyoutMessage == null) {
+            return null;
+        }
+        // the icon is only used in group chats
+        Icon icon = mFlyoutMessage.isGroupChat ? mFlyoutMessage.senderIcon : null;
+        String title =
+                mFlyoutMessage.senderName == null ? null : mFlyoutMessage.senderName.toString();
+        String message = mFlyoutMessage.message == null ? null : mFlyoutMessage.message.toString();
+        return new ParcelableFlyoutMessage(icon, title, message);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 3982a23..c5e3afd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -274,7 +274,7 @@
         @Nullable BubbleExpandedView expandedView;
         int dotColor;
         Path dotPath;
-        @Nullable Bubble.FlyoutMessage flyoutMessage;
+        Bubble.FlyoutMessage flyoutMessage;
         Bitmap bubbleBitmap;
         Bitmap badgeBitmap;
 
@@ -300,6 +300,10 @@
                 return null;
             }
 
+            // set the flyout message but don't load the avatar because we can't pass it on the
+            // binder to launcher
+            info.flyoutMessage = b.getFlyoutMessage();
+
             return info;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index 1b7bb0d..c12822a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -181,7 +181,7 @@
         @Nullable BubbleExpandedView expandedView;
         int dotColor;
         Path dotPath;
-        @Nullable Bubble.FlyoutMessage flyoutMessage;
+        Bubble.FlyoutMessage flyoutMessage;
         Bitmap bubbleBitmap;
         Bitmap badgeBitmap;
 
@@ -221,6 +221,10 @@
                 return null;
             }
 
+            // set the flyout message but don't load the avatar because we can't pass it on the
+            // binder to launcher
+            info.flyoutMessage = b.getFlyoutMessage();
+
             return info;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e80b95b..79c31e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -356,6 +356,7 @@
             ShellInit shellInit,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopRepository> desktopRepository,
+            Optional<DesktopTasksController> desktopTasksController,
             LaunchAdjacentController launchAdjacentController,
             WindowDecorViewModel windowDecorViewModel) {
         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
@@ -364,7 +365,8 @@
                 ? shellInit
                 : null;
         return new FreeformTaskListener(context, init, shellTaskOrganizer,
-                desktopRepository, launchAdjacentController, windowDecorViewModel);
+                desktopRepository, desktopTasksController, launchAdjacentController,
+                windowDecorViewModel);
     }
 
     @WMSingleton
@@ -652,7 +654,8 @@
     @WMSingleton
     @Provides
     static Optional<TaskChangeListener> provideDesktopTaskChangeListener(Context context) {
-        if (DesktopModeStatus.canEnterDesktopMode(context)) {
+        if (Flags.enableWindowingTransitionHandlersObservers() &&
+                DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.of(new DesktopTaskChangeListener());
         }
         return Optional.empty();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 702552e..3508ece 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -127,11 +127,9 @@
     @Provides
     static PipScheduler providePipScheduler(Context context,
             PipBoundsState pipBoundsState,
-            PhonePipMenuController pipMenuController,
             @ShellMainThread ShellExecutor mainExecutor,
             PipTransitionState pipTransitionState) {
-        return new PipScheduler(context, pipBoundsState, pipMenuController,
-                mainExecutor, pipTransitionState);
+        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState);
     }
 
     @WMSingleton
@@ -140,10 +138,13 @@
             PipBoundsState pipBoundsState, PipMediaController pipMediaController,
             SystemWindows systemWindows,
             PipUiEventLogger pipUiEventLogger,
+            PipTaskListener pipTaskListener,
+            @NonNull PipTransitionState pipTransitionState,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
         return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
-                systemWindows, pipUiEventLogger, mainExecutor, mainHandler);
+                systemWindows, pipUiEventLogger, pipTaskListener, pipTransitionState, mainExecutor,
+                mainHandler);
     }
 
 
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 4e548a6..5b9d2fe 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
@@ -110,6 +110,7 @@
 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import com.android.wm.shell.windowdecor.extension.isMultiWindow
+import com.android.wm.shell.windowdecor.extension.requestingImmersive
 import java.io.PrintWriter
 import java.util.Optional
 import java.util.concurrent.Executor
@@ -1844,6 +1845,17 @@
         userId = newUserId
     }
 
+    /** Called when a task's info changes. */
+    fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
+        if (!Flags.enableFullyImmersiveInDesktop()) return
+        val inImmersive = taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)
+        val requestingImmersive = taskInfo.requestingImmersive
+        if (inImmersive && !requestingImmersive) {
+            // Exit immersive if the app is no longer requesting it.
+            exitDesktopTaskFromFullImmersive(taskInfo)
+        }
+    }
+
     private fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopTasksController")
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 73f7011..fbd3c10 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
@@ -30,6 +30,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
@@ -50,6 +51,7 @@
     private final Context mContext;
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<DesktopRepository> mDesktopRepository;
+    private final Optional<DesktopTasksController> mDesktopTasksController;
     private final WindowDecorViewModel mWindowDecorationViewModel;
     private final LaunchAdjacentController mLaunchAdjacentController;
 
@@ -65,12 +67,14 @@
             ShellInit shellInit,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopRepository> desktopRepository,
+            Optional<DesktopTasksController> desktopTasksController,
             LaunchAdjacentController launchAdjacentController,
             WindowDecorViewModel windowDecorationViewModel) {
         mContext = context;
         mShellTaskOrganizer = shellTaskOrganizer;
         mWindowDecorationViewModel = windowDecorationViewModel;
         mDesktopRepository = desktopRepository;
+        mDesktopTasksController = desktopTasksController;
         mLaunchAdjacentController = launchAdjacentController;
         if (shellInit != null) {
             shellInit.addInitCallback(this::onInit, this);
@@ -147,6 +151,7 @@
 
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
                 taskInfo.taskId);
+        mDesktopTasksController.ifPresent(c -> c.onTaskInfoChanged(taskInfo));
         mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
         state.mTaskInfo = taskInfo;
         if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index d6b920e..056f6b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -119,6 +119,9 @@
                 case WindowManager.TRANSIT_TO_FRONT:
                     onToFrontTransitionReady(change, startT, finishT);
                     break;
+                case WindowManager.TRANSIT_TO_BACK:
+                    onToBackTransitionReady(change, startT, finishT);
+                    break;
                 case WindowManager.TRANSIT_CLOSE: {
                     taskInfoList.add(change.getTaskInfo());
                     onCloseTransitionReady(change, startT, finishT);
@@ -173,6 +176,16 @@
                 change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
+    private void onToBackTransitionReady(
+            TransitionInfo.Change change,
+            SurfaceControl.Transaction startT,
+            SurfaceControl.Transaction finishT) {
+        mTaskChangeListener.ifPresent(
+                listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
+        mWindowDecorViewModel.onTaskChanging(
+                change.getTaskInfo(), change.getLeash(), startT, finishT);
+    }
+
     @Override
     public void onTransitionStarting(@NonNull IBinder transition) {}
 
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 9cfe162..8c1e5e6 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
@@ -18,10 +18,12 @@
 
 import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -52,7 +54,8 @@
  * The current media session provides actions whenever there are no valid actions provided by the
  * current PiP activity. Otherwise, those actions always take precedence.
  */
-public class PhonePipMenuController implements PipMenuController {
+public class PhonePipMenuController implements PipMenuController,
+        PipTransitionState.PipTransitionStateChangedListener {
 
     private static final String TAG = "PhonePipMenuController";
     private static final boolean DEBUG = false;
@@ -113,6 +116,11 @@
 
     private PipMenuView mPipMenuView;
 
+    private final PipTaskListener mPipTaskListener;
+
+    @NonNull
+    private final PipTransitionState mPipTransitionState;
+
     private SurfaceControl mLeash;
 
     private ActionListener mMediaActionListener = new ActionListener() {
@@ -125,15 +133,27 @@
 
     public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
             PipMediaController mediaController, SystemWindows systemWindows,
-            PipUiEventLogger pipUiEventLogger,
-            ShellExecutor mainExecutor, Handler mainHandler) {
+            PipUiEventLogger pipUiEventLogger, PipTaskListener pipTaskListener,
+            @NonNull PipTransitionState pipTransitionState, ShellExecutor mainExecutor,
+            Handler mainHandler) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
         mMediaController = mediaController;
         mSystemWindows = systemWindows;
+        mPipTaskListener = pipTaskListener;
+        mPipTransitionState = pipTransitionState;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
         mPipUiEventLogger = pipUiEventLogger;
+
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
+
+        mPipTaskListener.addParamsChangedListener(new PipTaskListener.PipParamsChangedCallback() {
+            @Override
+            public void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
+                setAppActions(actions, closeAction);
+            }
+        });
     }
 
     public boolean isMenuVisible() {
@@ -438,8 +458,7 @@
      * Sets the menu actions to the actions provided by the current PiP menu.
      */
     @Override
-    public void setAppActions(List<RemoteAction> appActions,
-            RemoteAction closeAction) {
+    public void setAppActions(List<RemoteAction> appActions, RemoteAction closeAction) {
         mAppActions = appActions;
         mCloseAction = closeAction;
         updateMenuActions();
@@ -468,8 +487,8 @@
      */
     private void updateMenuActions() {
         if (mPipMenuView != null) {
-            mPipMenuView.setActions(mPipBoundsState.getBounds(),
-                    resolveMenuActions(), mCloseAction);
+            mPipMenuView.setActions(mPipBoundsState.getBounds(), resolveMenuActions(),
+                    mCloseAction);
         }
     }
 
@@ -567,6 +586,29 @@
         }
     }
 
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState, Bundle extra) {
+        switch (newState) {
+            case PipTransitionState.ENTERED_PIP:
+                attach(mPipTransitionState.mPinnedTaskLeash);
+                break;
+            case PipTransitionState.EXITED_PIP:
+                detach();
+                break;
+            case PipTransitionState.CHANGED_PIP_BOUNDS:
+                updateMenuLayout(mPipBoundsState.getBounds());
+                hideMenu();
+                break;
+            case PipTransitionState.CHANGING_PIP_BOUNDS:
+                hideMenu();
+                break;
+            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+                hideMenu();
+                break;
+        }
+    }
+
     void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
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 a29104c..0910919 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
@@ -447,7 +447,7 @@
                 final LayoutInflater inflater = LayoutInflater.from(mContext);
                 while (mActionsGroup.getChildCount() < mActions.size()) {
                     final PipMenuActionView actionView = (PipMenuActionView) inflater.inflate(
-                            R.layout.pip_menu_action, mActionsGroup, false);
+                            R.layout.pip2_menu_action, mActionsGroup, false);
                     mActionsGroup.addView(actionView);
                 }
 
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 0324fdb..268c3a2 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
@@ -336,7 +336,7 @@
         }
         cancelPhysicsAnimation();
         mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
-        // mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
+        mPipScheduler.scheduleExitPipViaExpand();
     }
 
     /**
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 f4defdc..d4f190e 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
@@ -52,7 +52,6 @@
 
     private final Context mContext;
     private final PipBoundsState mPipBoundsState;
-    private final PhonePipMenuController mPipMenuController;
     private final ShellExecutor mMainExecutor;
     private final PipTransitionState mPipTransitionState;
     private PipSchedulerReceiver mSchedulerReceiver;
@@ -97,12 +96,10 @@
 
     public PipScheduler(Context context,
             PipBoundsState pipBoundsState,
-            PhonePipMenuController pipMenuController,
             ShellExecutor mainExecutor,
             PipTransitionState pipTransitionState) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
-        mPipMenuController = pipMenuController;
         mMainExecutor = mainExecutor;
         mPipTransitionState = pipTransitionState;
 
@@ -263,7 +260,6 @@
             return;
         }
         mPipBoundsState.setBounds(newBounds);
-        mPipMenuController.updateMenuLayout(newBounds);
         maybeUpdateMovementBounds();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 262c14d..c58de2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -20,6 +20,7 @@
 
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
+import android.app.RemoteAction;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -37,6 +38,9 @@
 import com.android.wm.shell.pip2.animation.PipResizeAnimator;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A Task Listener implementation used only for CUJs and trigger paths that cannot be initiated via
  * Transitions framework directly.
@@ -57,6 +61,7 @@
             new PictureInPictureParams.Builder().build();
 
     private boolean mWaitingForAspectRatioChange = false;
+    private final List<PipParamsChangedCallback> mPipParamsChangedListeners = new ArrayList<>();
 
     public PipTaskListener(Context context,
             ShellTaskOrganizer shellTaskOrganizer,
@@ -85,10 +90,25 @@
         if (mPictureInPictureParams.equals(params)) {
             return;
         }
+        if (PipUtils.remoteActionsChanged(params.getActions(), mPictureInPictureParams.getActions())
+                || !PipUtils.remoteActionsMatch(params.getCloseAction(),
+                mPictureInPictureParams.getCloseAction())) {
+            for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
+                listener.onActionsChanged(params.getActions(), params.getCloseAction());
+            }
+        }
         mPictureInPictureParams.copyOnlySet(params != null ? params
                 : new PictureInPictureParams.Builder().build());
     }
 
+    /** Add a PipParamsChangedCallback listener. */
+    public void addParamsChangedListener(PipParamsChangedCallback listener) {
+        if (mPipParamsChangedListeners.contains(listener)) {
+            return;
+        }
+        mPipParamsChangedListeners.add(listener);
+    }
+
     @NonNull
     public PictureInPictureParams getPictureInPictureParams() {
         return mPictureInPictureParams;
@@ -164,4 +184,12 @@
                 break;
         }
     }
+
+    public interface PipParamsChangedCallback {
+        /**
+         * Called if either the actions or the close action changed.
+         */
+        default void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
index a231e38..176020f 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
@@ -69,116 +69,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Begin cleanup after gcl merges
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup1-src",
-    srcs: [
-        "src/**/A*.kt",
-        "src/**/B*.kt",
-        "src/**/C*.kt",
-        "src/**/D*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup2-src",
-    srcs: [
-        "src/**/E*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup3-src",
-    srcs: [
-        "src/**/S*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroupOther-src",
-    srcs: [
-        "src/**/*.kt",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup1",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup2",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup2-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup3",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup3-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroupOther",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroupOther-src",
-    ],
-    exclude_srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-        ":WMShellFlickerTestsSplitScreenGroup2-src",
-        ":WMShellFlickerTestsSplitScreenGroup3-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// End cleanup after gcl merges
-
-////////////////////////////////////////////////////////////////////////////////
 // Begin breakdowns for FlickerTestsRotation module
 
 test_module_config {
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
index 7412c1d..b016c9f 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
@@ -24,31 +24,6 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Begin cleanup after gcl merge
-
-filegroup {
-    name: "WMShellFlickerTestsAppCompat-src",
-    srcs: [
-        "src/**/*.kt",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsOther",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker",
-    instrumentation_target_package: "com.android.wm.shell.flicker",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [":WMShellFlickerTestsAppCompat-src"],
-    static_libs: ["WMShellFlickerTestsBase"],
-    data: ["trace_config/*"],
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// End cleanup after gcl merge
-
 android_test {
     name: "WMShellFlickerTestsAppCompat",
     defaults: ["WMShellFlickerTestsDefault"],
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index 16c2d47..27303c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test launching app in size compat mode.
  *
- * To run this test: `atest WMShellFlickerTestsOther:OpenAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsAppCompat:OpenAppInSizeCompatModeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
index d85b771..2980d51 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -32,7 +32,7 @@
 /**
  * Test launching app in size compat mode.
  *
- * To run this test: `atest WMShellFlickerTestsOther:OpenTransparentActivityTest`
+ * To run this test: `atest WMShellFlickerTestsAppCompat:OpenTransparentActivityTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
index 164534c..2484f67 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -36,7 +36,7 @@
 /**
  * Test quick switching to letterboxed app from launcher
  *
- * To run this test: `atest WMShellFlickerTestsOther:QuickSwitchLauncherToLetterboxAppTest`
+ * To run this test: `atest WMShellFlickerTestsAppCompat:QuickSwitchLauncherToLetterboxAppTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
index 034d54b..77423af 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -32,7 +32,7 @@
 /**
  * Test launching a fixed portrait letterboxed app in landscape and repositioning to the right.
  *
- * To run this test: `atest WMShellFlickerTestsOther:RepositionFixedPortraitAppTest`
+ * To run this test: `atest WMShellFlickerTestsAppCompat:RepositionFixedPortraitAppTest`
  *
  * Actions:
  *
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
index 443fac1..5459ef03 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test restarting app in size compat mode.
  *
- * To run this test: `atest WMShellFlickerTestsOther:RestartAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsAppCompat:RestartAppInSizeCompatModeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
index 22543aa..5bb9640 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
@@ -45,7 +45,7 @@
 /**
  * Test rotating an immersive app in fullscreen.
  *
- * To run this test: `atest WMShellFlickerTestsOther:RotateImmersiveAppInFullscreenTest`
+ * To run this test: `atest WMShellFlickerTestsAppCompat:RotateImmersiveAppInFullscreenTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 3c95d3a..ddbc681 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -29,92 +29,12 @@
     srcs: ["src/**/apps/*.kt"],
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Begin cleanup after gcl merges
-
-filegroup {
-    name: "WMShellFlickerTestsPip1-src",
-    srcs: [
-        "src/**/A*.kt",
-        "src/**/B*.kt",
-        "src/**/C*.kt",
-        "src/**/D*.kt",
-        "src/**/F*.kt",
-        "src/**/S*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPip2-src",
-    srcs: [
-        "src/**/E*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPip3-src",
-    srcs: ["src/**/*.kt"],
-}
-
 filegroup {
     name: "WMShellFlickerTestsPipCommon-src",
     srcs: ["src/**/common/*.kt"],
 }
 
 android_test {
-    name: "WMShellFlickerTestsPip1",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.pip",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsPip1-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-    static_libs: ["WMShellFlickerTestsBase"],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPip2",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.pip",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsPip2-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-    static_libs: ["WMShellFlickerTestsBase"],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPip3",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.pip",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsPip3-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-    exclude_srcs: [
-        ":WMShellFlickerTestsPip1-src",
-        ":WMShellFlickerTestsPip2-src",
-        ":WMShellFlickerTestsPipApps-src",
-    ],
-    static_libs: ["WMShellFlickerTestsBase"],
-    data: ["trace_config/*"],
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// End cleanup after gcl merges
-
-android_test {
     name: "WMShellFlickerTestsPip",
     defaults: ["WMShellFlickerTestsDefault"],
     manifest: "AndroidManifest.xml",
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index a9ed13a..a248303 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -38,7 +38,7 @@
 /**
  * Test entering pip from an app via auto-enter property when navigating to home.
  *
- * To run this test: `atest WMShellFlickerTestsPip1:AutoEnterPipOnGoToHomeTest`
+ * To run this test: `atest WMShellFlickerTestsPip:AutoEnterPipOnGoToHomeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
index d059211..df952c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -30,7 +30,7 @@
 /**
  * Test auto entering pip using a source rect hint.
  *
- * To run this test: `atest WMShellFlickerTestsPip1:AutoEnterPipWithSourceRectHintTest`
+ * To run this test: `atest WMShellFlickerTestsPip:AutoEnterPipWithSourceRectHintTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 3ffc9d7..302b8c4 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -32,7 +32,7 @@
 /**
  * Test closing a pip window by swiping it to the bottom-center of the screen
  *
- * To run this test: `atest WMShellFlickerTestsPip1:ClosePipBySwipingDownTest`
+ * To run this test: `atest WMShellFlickerTestsPip:ClosePipBySwipingDownTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index d177624..77a1edb 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -30,7 +30,7 @@
 /**
  * Test closing a pip window via the dismiss button
  *
- * To run this test: `atest WMShellFlickerTestsPip1:ClosePipWithDismissButtonTest`
+ * To run this test: `atest WMShellFlickerTestsPip:ClosePipWithDismissButtonTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index a86803d..6e32d64 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test entering pip from an app via [onUserLeaveHint] and by navigating to home.
  *
- * To run this test: `atest WMShellFlickerTestsPip2:EnterPipOnUserLeaveHintTest`
+ * To run this test: `atest WMShellFlickerTestsPip:EnterPipOnUserLeaveHintTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index d0e8215..9a6cb61 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -46,7 +46,7 @@
 /**
  * Test entering pip while changing orientation (from app in landscape to pip window in portrait)
  *
- * To run this test: `atest WMShellFlickerTestsPip2:EnterPipToOtherOrientation`
+ * To run this test: `atest WMShellFlickerTestsPip:EnterPipToOtherOrientation`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index d92f55a..6b4751c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -28,7 +28,7 @@
 /**
  * Test entering pip from an app by interacting with the app UI
  *
- * To run this test: `atest WMShellFlickerTestsPip2:EnterPipViaAppUiButtonTest`
+ * To run this test: `atest WMShellFlickerTestsPip:EnterPipViaAppUiButtonTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 8c0817d..8d0bc0f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -28,7 +28,7 @@
 /**
  * Test expanding a pip window back to full screen via the expand button
  *
- * To run this test: `atest WMShellFlickerTestsPip2:ExitPipToAppViaExpandButtonTest`
+ * To run this test: `atest WMShellFlickerTestsPip:ExitPipToAppViaExpandButtonTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index 90a9623..939f328 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -28,7 +28,7 @@
 /**
  * Test expanding a pip window back to full screen via an intent
  *
- * To run this test: `atest WMShellFlickerTestsPip2:ExitPipToAppViaIntentTest`
+ * To run this test: `atest WMShellFlickerTestsPip:ExitPipToAppViaIntentTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 9306c77..258663b 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -33,7 +33,7 @@
 /**
  * Test expanding a pip window by double-clicking it
  *
- * To run this test: `atest WMShellFlickerTestsPip2:ExpandPipOnDoubleClickTest`
+ * To run this test: `atest WMShellFlickerTestsPip:ExpandPipOnDoubleClickTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index cb8ee27..5f8ac2a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -39,7 +39,7 @@
 /**
  * Test entering pip from an app via auto-enter property when navigating to home from split screen.
  *
- * To run this test: `atest WMShellFlickerTestsPip1:FromSplitScreenAutoEnterPipOnGoToHomeTest`
+ * To run this test: `atest WMShellFlickerTestsPip:FromSplitScreenAutoEnterPipOnGoToHomeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index d1bf6ac..48c85a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -40,7 +40,7 @@
 /**
  * Test entering pip from an app via auto-enter property when navigating to home from split screen.
  *
- * To run this test: `atest WMShellFlickerTestsPip1:FromSplitScreenEnterPipOnUserLeaveHintTest`
+ * To run this test: `atest WMShellFlickerTestsPip:FromSplitScreenEnterPipOnUserLeaveHintTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 265eb44..ee62cf5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -32,7 +32,7 @@
 /**
  * Test Pip movement with Launcher shelf height change (increase).
  *
- * To run this test: `atest WMShellFlickerTestsPip3:MovePipDownOnShelfHeightChange`
+ * To run this test: `atest WMShellFlickerTestsPip:MovePipDownOnShelfHeightChange`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 8d6be64..4d643f7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -32,7 +32,7 @@
 /**
  * Test Pip movement with Launcher shelf height change (decrease).
  *
- * To run this test: `atest WMShellFlickerTestsPip3:MovePipUpOnShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTestsPip:MovePipUpOnShelfHeightChangeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 9109eaf..c6cf341 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -35,7 +35,7 @@
 /**
  * Test Pip Stack in bounds after rotations.
  *
- * To run this test: `atest WMShellFlickerTestsPip1:ShowPipAndRotateDisplay`
+ * To run this test: `atest WMShellFlickerTestsPip:ShowPipAndRotateDisplay`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index 1fc9d99..7b04b76 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -40,7 +40,7 @@
 /**
  * Test entering pip from Maps app by interacting with the app UI
  *
- * To run this test: `atest WMShellFlickerTests:MapsEnterPipTest`
+ * To run this test: `atest WMShellFlickerTestsPipAppsCSuite:MapsEnterPipTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 68fa7c7..6911946 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -38,7 +38,7 @@
 /**
  * Test entering pip from Netflix app by interacting with the app UI
  *
- * To run this test: `atest WMShellFlickerTests:NetflixEnterPipTest`
+ * To run this test: `atest WMShellFlickerTestsPipAppsCSuite:NetflixEnterPipTest`
  *
  * Actions:
  * ```
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 7873a85..5e54f30 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
@@ -34,7 +34,7 @@
 /**
  * Test entering pip from YouTube app by interacting with the app UI
  *
- * To run this test: `atest WMShellFlickerTests:YouTubeEnterPipTest`
+ * To run this test: `atest WMShellFlickerTestsPipAppsCSuite:YouTubeEnterPipTest`
  *
  * Actions:
  * ```
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 72be3d8..159cba4 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
@@ -38,7 +38,7 @@
 /**
  * Test entering pip from YouTube app by interacting with the app UI
  *
- * To run this test: `atest WMShellFlickerTests:YouTubeEnterPipTest`
+ * To run this test: `atest WMShellFlickerTestsPipAppsCSuite:YouTubeEnterPipTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
index 8a073ab..6a84b28 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
-/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTests:TvPipBasicTest` */
+/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTestsPip:TvPipBasicTest` */
 @RequiresDevice
 @RunWith(Parameterized::class)
 class TvPipBasicTest(private val radioButtonId: String, private val pipWindowRatio: Rational?) :
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index d4cd6da..09e8745 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -27,7 +27,7 @@
 import org.junit.Before
 import org.junit.Test
 
-/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTests:TvPipMenuTests` */
+/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTestsPip:TvPipMenuTests` */
 @RequiresDevice
 class TvPipMenuTests : TvPipTestBase() {
 
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 9e5c1a6..ae4772e 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
@@ -50,6 +50,7 @@
 import android.view.DragEvent
 import android.view.Gravity
 import android.view.SurfaceControl
+import android.view.WindowInsets
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_CLOSE
@@ -75,6 +76,7 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP
 import com.android.wm.shell.MockToken
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -142,6 +144,7 @@
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.times
@@ -3151,6 +3154,30 @@
     })
   }
 
+  @Test
+  @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+  fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() {
+    val task = setUpFreeformTask(DEFAULT_DISPLAY)
+    taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+
+    task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+    controller.onTaskInfoChanged(task)
+
+    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersive(eq(task), any())
+  }
+
+  @Test
+  @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+  fun onTaskInfoChanged_notInImmersiveUnrequestsImmersive_noReExit() {
+    val task = setUpFreeformTask(DEFAULT_DISPLAY)
+    taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = false)
+
+    task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+    controller.onTaskInfoChanged(task)
+
+    verify(mockDesktopFullImmersiveTransitionHandler, never()).exitImmersive(eq(task), any())
+  }
+
   /**
    * Assert that an unhandled drag event launches a PendingIntent with the
    * windowing mode and bounds we are expecting.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 956ef14..36e0427 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -42,6 +42,7 @@
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -75,6 +76,8 @@
     @Mock
     private DesktopRepository mDesktopRepository;
     @Mock
+    private DesktopTasksController mDesktopTasksController;
+    @Mock
     private LaunchAdjacentController mLaunchAdjacentController;
     private FreeformTaskListener mFreeformTaskListener;
     private StaticMockitoSession mMockitoSession;
@@ -90,6 +93,7 @@
                 mShellInit,
                 mTaskOrganizer,
                 Optional.of(mDesktopRepository),
+                Optional.of(mDesktopTasksController),
                 mLaunchAdjacentController,
                 mWindowDecorViewModel);
     }
@@ -177,6 +181,18 @@
         verify(mDesktopRepository).removeFreeformTask(task.displayId, task.taskId);
     }
 
+    @Test
+    public void onTaskInfoChanged_withDesktopController_forwards() {
+        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskInfoChanged(task);
+
+        verify(mDesktopTasksController).onTaskInfoChanged(task);
+    }
+
     @After
     public void tearDown() {
         mMockitoSession.finishMocking();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 86a8502..da95315 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 
@@ -161,6 +162,22 @@
     }
 
     @Test
+    public void toBackTransition_notifiesOnTaskMovingToBack() {
+        final TransitionInfo.Change change =
+                createChange(TRANSIT_TO_BACK, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_BACK, /* flags= */ 0)
+                .addChange(change).build();
+
+        final IBinder transition = mock(IBinder.class);
+        final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
+        mTransitionObserver.onTransitionStarting(transition);
+
+        verify(mTaskChangeListener).onTaskMovingToBack(change.getTaskInfo());
+    }
+
+    @Test
     public void changeTransition_notifiesOnTaskChanging() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CHANGE, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
index 641063c..205defe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.shared.bubbles
 
+import android.graphics.drawable.Icon
+import android.net.Uri
 import android.os.Parcel
 import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
 import android.testing.AndroidTestingRunner
@@ -42,7 +44,12 @@
                 "title",
                 "Some app",
                 true,
-                true
+                true,
+                ParcelableFlyoutMessage(
+                    Icon.createWithContentUri(Uri.parse("content://image/123")),
+                    "sender",
+                    "message"
+                )
             )
         val parcel = Parcel.obtain()
         bubbleInfo.writeToParcel(parcel, PARCELABLE_WRITE_RETURN_VALUE)
@@ -60,5 +67,10 @@
         assertThat(bubbleInfo.appName).isEqualTo(bubbleInfoFromParcel.appName)
         assertThat(bubbleInfo.isImportantConversation)
             .isEqualTo(bubbleInfoFromParcel.isImportantConversation)
+        with(bubbleInfo.parcelableFlyoutMessage!!) {
+            assertThat(icon!!.uri.toString()).isEqualTo("content://image/123")
+            assertThat(title).isEqualTo("sender")
+            assertThat(message).isEqualTo("message")
+        }
     }
 }
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index ede385a..9cd6e25 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -48,6 +48,7 @@
     minikinPaint.localeListId = paint->getMinikinLocaleListId();
     minikinPaint.fontStyle = resolvedFace->fStyle;
     minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
+    minikinPaint.fontVariationSettings = paint->getFontVariationOverride();
 
     const std::optional<minikin::FamilyVariant>& familyVariant = paint->getFamilyVariant();
     if (familyVariant.has_value()) {
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 6c05346..4563386 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -156,16 +156,47 @@
     return layout->layout.getFakery(i).isFakeItalic();
 }
 
+float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin::AxisTag tag) {
+    for (const minikin::FontVariation& fv : fakery.variationSettings()) {
+        if (fv.axisTag == tag) {
+            return fv.value;
+        }
+    }
+    return std::numeric_limits<float>::quiet_NaN();
+}
+
 // CriticalNative
 static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
     const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
-    return layout->layout.getFakery(i).wghtAdjustment();
+    if (text_feature::typeface_redesign()) {
+        float value =
+                findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght);
+        if (!std::isnan(value)) {
+            return value;
+        } else {
+            const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i);
+            return font->style().weight();
+        }
+    } else {
+        return layout->layout.getFakery(i).wghtAdjustment();
+    }
 }
 
 // CriticalNative
 static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
     const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
-    return layout->layout.getFakery(i).italAdjustment();
+    if (text_feature::typeface_redesign()) {
+        float value =
+                findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital);
+        if (!std::isnan(value)) {
+            return value;
+        } else {
+            const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i);
+            return font->style().isItalic();
+        }
+    } else {
+        return layout->layout.getFakery(i).italAdjustment();
+    }
 }
 
 // CriticalNative
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 47637b8..290d49b 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -15,10 +15,8 @@
  */
 package android.media.session;
 
-import static com.android.media.flags.Flags.FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE;
 
 import android.annotation.DrawableRes;
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.Nullable;
@@ -187,13 +185,21 @@
      */
     public static final long ACTION_SET_PLAYBACK_SPEED = 1 << 22;
 
-    /**
-     * @hide
-     */
-    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
-            STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING,
-            STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM,
-            STATE_PLAYBACK_SUPPRESSED})
+    /** @hide */
+    @IntDef({
+        STATE_NONE,
+        STATE_STOPPED,
+        STATE_PAUSED,
+        STATE_PLAYING,
+        STATE_FAST_FORWARDING,
+        STATE_REWINDING,
+        STATE_BUFFERING,
+        STATE_ERROR,
+        STATE_CONNECTING,
+        STATE_SKIPPING_TO_PREVIOUS,
+        STATE_SKIPPING_TO_NEXT,
+        STATE_SKIPPING_TO_QUEUE_ITEM
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
 
@@ -290,19 +296,6 @@
     public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
 
     /**
-     * State indicating that playback is paused due to an external transient interruption, like a
-     * phone call.
-     *
-     * <p>This state is different from {@link #STATE_PAUSED} in that it is deemed transitory,
-     * possibly allowing the service associated to the session in this state to run in the
-     * foreground.
-     *
-     * @see Builder#setState
-     */
-    @FlaggedApi(FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE)
-    public static final int STATE_PLAYBACK_SUPPRESSED = 12;
-
-    /**
      * Use this value for the position to indicate the position is not known.
      */
     public static final long PLAYBACK_POSITION_UNKNOWN = -1;
@@ -401,7 +394,6 @@
      * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
      * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
      * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
-     * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li>
      * </ul>
      */
     @State
@@ -525,7 +517,6 @@
      * <li>{@link #STATE_SKIPPING_TO_NEXT}</li>
      * <li>{@link #STATE_SKIPPING_TO_PREVIOUS}</li>
      * <li>{@link #STATE_SKIPPING_TO_QUEUE_ITEM}</li>
-     * <li>{@link #STATE_PLAYBACK_SUPPRESSED}</li>
      * </ul>
      */
     public boolean isActive() {
@@ -538,7 +529,6 @@
             case PlaybackState.STATE_BUFFERING:
             case PlaybackState.STATE_CONNECTING:
             case PlaybackState.STATE_PLAYING:
-            case PlaybackState.STATE_PLAYBACK_SUPPRESSED:
                 return true;
         }
         return false;
@@ -584,8 +574,6 @@
                 return "SKIPPING_TO_NEXT";
             case STATE_SKIPPING_TO_QUEUE_ITEM:
                 return "SKIPPING_TO_QUEUE_ITEM";
-            case STATE_PLAYBACK_SUPPRESSED:
-                return "STATE_PLAYBACK_SUPPRESSED";
             default:
                 return "UNKNOWN";
         }
@@ -823,7 +811,6 @@
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
-         * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li>
          * </ul>
          *
          * @param state The current state of playback.
@@ -868,7 +855,6 @@
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
-         * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li>
          * </ul>
          *
          * @param state The current state of playback.
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 4676dff..84a13ab 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -17,12 +17,14 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.BytesLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.media.AudioPresentation;
 import android.media.MediaCodec.LinearBlock;
+import android.media.tv.flags.Flags;
 
 import java.util.Collections;
 import java.util.List;
@@ -57,12 +59,16 @@
     private final int mScIndexMask;
     private final AudioDescriptor mExtraMetaData;
     private final List<AudioPresentation> mAudioPresentations;
+    private final int mNumDataPieces;
+    private final int mIndexInDataGroup;
+    private final int mDataGroupId;
 
     // This constructor is used by JNI code only
     private MediaEvent(int streamId, boolean isPtsPresent, long pts, boolean isDtsPresent, long dts,
             long dataLength, long offset, LinearBlock buffer, boolean isSecureMemory, long dataId,
             int mpuSequenceNumber, boolean isPrivateData, int scIndexMask,
-            AudioDescriptor extraMetaData, List<AudioPresentation> audioPresentations) {
+            AudioDescriptor extraMetaData, List<AudioPresentation> audioPresentations,
+            int numDataPieces, int indexInDataGroup, int dataGroupId) {
         mStreamId = streamId;
         mIsPtsPresent = isPtsPresent;
         mPts = pts;
@@ -78,6 +84,9 @@
         mScIndexMask = scIndexMask;
         mExtraMetaData = extraMetaData;
         mAudioPresentations = audioPresentations;
+        mNumDataPieces = numDataPieces;
+        mIndexInDataGroup = indexInDataGroup;
+        mDataGroupId = dataGroupId;
     }
 
     /**
@@ -235,6 +244,67 @@
     }
 
     /**
+     * Gets the number of data pieces into which the original data was split.
+     *
+     * <p>The {@link #getNumDataPieces()}, {@link #getIndexInDataGroup()} and
+     * {@link #getDataGroupId()} methods should be used together to reassemble the original data if
+     * it was split into pieces. Use {@link #getLinearBlock()} to get the memory where the data
+     * pieces are stored.
+     *
+     * @return 0 or 1 if this MediaEvent object contains the complete data; otherwise the number of
+     *         pieces into which the original data was split.
+     * @see #getIndexInDataGroup()
+     * @see #getDataGroupId()
+     * @see #getLinearBlock()
+     */
+    @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+    @IntRange(from = 0)
+    public int getNumDataPieces() {
+        return mNumDataPieces;
+    }
+
+    /**
+     * Gets the index of the data piece. The index in the data group indicates the order in which
+     * this {@link MediaEvent}'s data piece should be reassembled. The result should be within the
+     * range [0, {@link #getNumDataPieces()}).
+     *
+     * <p>The {@link #getNumDataPieces()}, {@link #getIndexInDataGroup()} and
+     * {@link #getDataGroupId()} methods should be used together to reassemble the original data if
+     * it was split into pieces. Use {@link #getLinearBlock()} to get the memory where the data
+     * pieces are stored.
+     *
+     * @return The index in the data group.
+     * @see #getNumDataPieces()
+     * @see #getDataGroupId()
+     * @see #getLinearBlock()
+     */
+    @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+    @IntRange(from = 0)
+    public int getIndexInDataGroup() {
+        return mIndexInDataGroup;
+    }
+
+    /**
+     * Gets the group ID for reassembling the complete data. {@link MediaEvent}s that have the same
+     * data group ID contain different pieces of the same data. This value should be ignored if
+     * {@link #getNumDataPieces()} returns 0 or 1.
+     *
+     * <p>The {@link #getNumDataPieces()}, {@link #getIndexInDataGroup()} and
+     * {@link #getDataGroupId()} methods should be used together to reassemble the original data if
+     * it was split into pieces. Use {@link #getLinearBlock()} to get the memory where the data
+     * pieces are stored.
+     *
+     * @return The data group ID.
+     * @see #getNumDataPieces()
+     * @see #getIndexInDataGroup()
+     * @see #getLinearBlock()
+     */
+    @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+    public int getDataGroupId() {
+        return mDataGroupId;
+    }
+
+    /**
      * Finalize the MediaEvent object.
      * @hide
      */
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 00b0e57..49e7941 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -686,12 +686,16 @@
     } else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) {
         sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
     }
+    jint numDataPieces = mediaEvent.numDataPieces;
+    jint indexInDataGroup = mediaEvent.indexInDataGroup;
+    jint dataGroupId = mediaEvent.dataGroupId;
 
     ScopedLocalRef obj(env, env->NewObject(mMediaEventClass, mMediaEventInitID, streamId,
                                            isPtsPresent, pts, isDtsPresent, dts, dataLength,
                                            offset, nullptr, isSecureMemory, avDataId,
                                            mpuSequenceNumber, isPesPrivateData, sc,
-                                           audioDescriptor.get(), presentationsJObj.get()));
+                                           audioDescriptor.get(), presentationsJObj.get(),
+                                           numDataPieces, indexInDataGroup, dataGroupId));
 
     // Protect mFilterClient from being set to null.
     android::Mutex::Autolock autoLock(mLock);
@@ -1048,7 +1052,7 @@
             "<init>",
             "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
             "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;"
-            "Ljava/util/List;)V");
+            "Ljava/util/List;III)V");
     mAudioDescriptorInitID = env->GetMethodID(mAudioDescriptorClass, "<init>", "(BBCBBB)V");
     mPesEventInitID = env->GetMethodID(mPesEventClass, "<init>", "(III)V");
     mTsRecordEventInitID = env->GetMethodID(mTsRecordEventClass, "<init>", "(IIIJJI)V");
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 0d4ef3c..8d6f0da4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml
index 1584b45..f82388c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml
index 15f2b5c..3b0cca0 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml
index 90eefbe..bdf9114 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
index f0da7b4..d56c843 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml
index 06be00d..9270cbe 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml
index fe88845..180564d 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
diff --git a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
index 203a395..2edc001 100644
--- a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
+++ b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
@@ -31,7 +31,6 @@
 
         <TextView
             android:id="@android:id/title"
-            android:text="Title"
             style="@style/SettingsLibEntityHeaderTitle"/>
 
         <com.android.settingslib.widget.CollapsableTextView
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml
index 7ffde25..b90de6b 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml
@@ -1,17 +1,18 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2024 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
   -->
 
 <resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_list_divider.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_list_divider.xml
new file mode 100644
index 0000000..eb588d9
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_list_divider.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/settingslib_list_divider_color" />
+    <size
+        android:height="1dp"
+        android:width="1dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml
index 16ca18a..9447653 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2024 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    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.
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml
new file mode 100644
index 0000000..c17f7ee
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/settingslib_materialColorOutline" />
+    <size
+        android:height="1dp"
+        android:width="1dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml
index 433d264..128f7a1 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml
@@ -29,7 +29,6 @@
         android:layout_height="wrap_content"
         android:layout_gravity="start"
         android:textAlignment="viewStart"
-        android:text="Title"
         android:maxLines="2"
         android:textAppearance="?android:attr/textAppearanceListItem"
         android:ellipsize="marquee"/>
@@ -41,7 +40,6 @@
         android:layout_below="@android:id/title"
         android:layout_alignLeft="@android:id/title"
         android:layout_alignStart="@android:id/title"
-        android:text="Summary summary summary"
         android:layout_gravity="start"
         android:textAlignment="viewStart"
         android:textColor="?android:attr/textColorSecondary"
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
deleted file mode 100644
index 4e23b65..0000000
--- a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2022 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
-    android:gravity="center_vertical"
-    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:background="?android:attr/selectableItemBackground"
-    android:clipToPadding="false"
-    android:baselineAligned="false">
-
-    <include layout="@layout/settingslib_icon_frame"/>
-
-    <include layout="@layout/settingslib_preference_frame"/>
-
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout
-        android:id="@android:id/widget_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="end|center_vertical"
-        android:paddingLeft="16dp"
-        android:paddingStart="16dp"
-        android:paddingRight="0dp"
-        android:paddingEnd="0dp"
-        android:orientation="vertical"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml
index f93e1b9..599e817 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml
@@ -29,7 +29,6 @@
         android:layout_height="wrap_content"
         android:layout_gravity="start"
         android:textAlignment="viewStart"
-        android:text="Title"
         android:maxLines="2"
         android:hyphenationFrequency="normalFast"
         android:lineBreakWordStyle="phrase"
@@ -47,7 +46,6 @@
         android:textAlignment="viewStart"
         android:textColor="?android:attr/textColorSecondary"
         android:maxLines="10"
-        android:text="Summary summary summary"
         android:hyphenationFrequency="normalFast"
         android:lineBreakWordStyle="phrase"
         style="@style/PreferenceSummaryTextStyle"/>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
index 4cc3c89..ea7baa4 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
@@ -25,6 +25,7 @@
     android:orientation="vertical"
     android:animateLayoutChanges="true"
     android:background="?android:attr/selectableItemBackground"
+    android:filterTouchesWhenObscured="false"
     android:clipToPadding="false">
 
     <TextView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml
index 2475dfd..511e2bb 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml
@@ -25,7 +25,8 @@
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:background="?android:attr/selectableItemBackground"
     android:clipToPadding="false"
-    android:baselineAligned="false">
+    android:baselineAligned="false"
+    android:filterTouchesWhenObscured="false">
 
     <include layout="@layout/settingslib_expressive_preference_icon_frame"/>
 
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
index f5017a5..ccdf37d 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
@@ -22,7 +22,8 @@
     android:minWidth="@dimen/settingslib_expressive_space_medium3"
     android:minHeight="@dimen/settingslib_expressive_space_medium3"
     android:gravity="center"
-    android:layout_marginEnd="-8dp">
+    android:layout_marginEnd="-8dp"
+    android:filterTouchesWhenObscured="false">
 
     <androidx.preference.internal.PreferenceImageView
         android:id="@android:id/icon"
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml
index 4cbdfd5..4abcd22 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml
@@ -19,4 +19,5 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:theme="@style/Theme.Material3.DynamicColors.DayNight"
     android:id="@+id/switchWidget"
+    android:filterTouchesWhenObscured="false"
     style="@style/SettingslibSwitchStyle.Expressive"/>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
index e3e689b..cc42dab 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
@@ -20,7 +20,8 @@
     android:layout_width="@dimen/settingslib_expressive_space_none"
     android:layout_height="wrap_content"
     android:layout_weight="1"
-    android:padding="@dimen/settingslib_expressive_space_small1">
+    android:paddingVertical="@dimen/settingslib_expressive_space_small1"
+    android:filterTouchesWhenObscured="false">
 
     <TextView
         android:id="@android:id/title"
@@ -28,8 +29,10 @@
         android:layout_height="wrap_content"
         android:layout_gravity="start"
         android:textAlignment="viewStart"
-        android:textAppearance="?android:attr/textAppearanceListItem"
         android:maxLines="2"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        android:textAppearance="?android:attr/textAppearanceListItem"
         android:ellipsize="marquee"/>
 
     <TextView
@@ -43,5 +46,7 @@
         android:textAlignment="viewStart"
         android:textAppearance="?android:attr/textAppearanceListItemSecondary"
         android:textColor="?android:attr/textColorSecondary"
-        android:maxLines="10"/>
-</RelativeLayout>
+        android:maxLines="10"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml
index 3f75181..9be9ec3 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2024 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    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.
   -->
 
 <LinearLayout
@@ -24,7 +24,8 @@
     android:orientation="horizontal"
     android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
     android:paddingLeft="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall7">
+    android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall7"
+    android:filterTouchesWhenObscured="false">
 
     <ImageView
         android:layout_width="wrap_content"
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
index 7f466f6..f69fcd2 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
@@ -18,5 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:baselineAligned="false">
+    android:baselineAligned="false"
+    android:filterTouchesWhenObscured="false">
 </LinearLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 0a36a4f..313748d 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -56,4 +56,6 @@
 
     <!--Deprecated. After sdk 35, don't use it-->
     <color name="settingslib_colorSurface">@color/settingslib_surface_dark</color>
+
+    <color name="settingslib_list_divider_color">@android:color/system_neutral1_700</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index 7706e0e..b99ee51 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -90,4 +90,6 @@
 
     <color name="settingslib_spinner_title_color">@android:color/system_neutral1_900</color>
     <color name="settingslib_spinner_dropdown_color">@android:color/system_neutral2_700</color>
+
+    <color name="settingslib_list_divider_color">@android:color/system_neutral1_200</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 698f21d..3ccbbc0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -27,6 +27,7 @@
         <item name="android:switchStyle">@style/Switch.SettingsLib</item>
         <item name="switchStyle">@style/SwitchCompat.SettingsLib</item>
         <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
+        <item name="android:listDivider">@drawable/settingslib_list_divider</item>
     </style>
 
     <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" />
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/strings.xml b/packages/SettingsLib/SettingsTheme/res/values/strings.xml
similarity index 94%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/strings.xml
rename to packages/SettingsLib/SettingsTheme/res/values/strings.xml
index 2273406..c36dcb8 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/strings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2021 The Android Open Source Project
+  Copyright (C) 2024 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
index f1cc4e95..7eb9840 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
@@ -248,8 +248,6 @@
     val url: String = "",
     val clickListener: View.OnClickListener) : URLSpan(url) {
     override fun onClick(widget: View) {
-        if (clickListener != null) {
-            clickListener.onClick(widget)
-        }
+        clickListener.onClick(widget)
     }
 }
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
index 10e5267..74f5441 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.os.Build
-import android.os.SystemProperties
 
 object SettingsThemeHelper {
     private const val IS_EXPRESSIVE_DESIGN_ENABLED = "is_expressive_design_enabled"
@@ -50,8 +49,7 @@
         expressiveThemeState =
             if (
                 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) &&
-                    (SystemProperties.getBoolean(IS_EXPRESSIVE_DESIGN_ENABLED, false) ||
-                        getPropBoolean(context, IS_EXPRESSIVE_DESIGN_ENABLED, false))
+                        getPropBoolean(context, IS_EXPRESSIVE_DESIGN_ENABLED, false)
             ) {
                 ExpressiveThemeState.ENABLED
             } else {
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java
index c8bcabf..261c722 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java
@@ -138,23 +138,37 @@
         }
 
         final PackageManager pmWrapper = mContext.getPackageManager();
+        // Add requesting apps, with full validation
         List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser(
                 new Intent(mIntentAction), flags, user);
         for (ResolveInfo resolveInfo : installedServices) {
             ServiceInfo info = resolveInfo.serviceInfo;
 
-            if (!mPermission.equals(info.permission)) {
-                Slog.w(mTag, "Skipping " + mNoun + " service "
-                        + info.packageName + "/" + info.name
-                        + ": it does not require the permission "
-                        + mPermission);
-                continue;
+            if (!mEnabledServices.contains(info.getComponentName())) {
+                if (!mPermission.equals(info.permission)) {
+                    Slog.w(mTag, "Skipping " + mNoun + " service "
+                            + info.packageName + "/" + info.name
+                            + ": it does not require the permission "
+                            + mPermission);
+                    continue;
+                }
+                if (mValidator != null && !mValidator.test(info)) {
+                    continue;
+                }
+                mServices.add(info);
             }
-            if (mValidator != null && !mValidator.test(info)) {
-                continue;
-            }
-            mServices.add(info);
         }
+
+        // Add all apps with access, in case prior approval was granted without full validation
+        for (ComponentName cn : mEnabledServices) {
+            List<ResolveInfo> enabledServices = pmWrapper.queryIntentServicesAsUser(
+                    new Intent().setComponent(cn), flags, user);
+            for (ResolveInfo resolveInfo : enabledServices) {
+                ServiceInfo info = resolveInfo.serviceInfo;
+                mServices.add(info);
+            }
+        }
+
         for (Callback callback : mCallbacks) {
             callback.onServicesReloaded(mServices);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
index 7ff0988..feef559 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -29,6 +30,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -42,6 +44,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
@@ -72,19 +75,26 @@
                 .build();
     }
 
+    private ArgumentMatcher<Intent> filterEquals(Intent intent) {
+        return (test) -> {
+            return intent.filterEquals(test);
+        };
+    }
+
     @Test
     public void testValidator() {
         ServiceInfo s1 = new ServiceInfo();
         s1.permission = "testPermission";
         s1.packageName = "pkg";
+        s1.name = "Service1";
         ServiceInfo s2 = new ServiceInfo();
         s2.permission = "testPermission";
         s2.packageName = "pkg2";
+        s2.name = "service2";
         ResolveInfo r1 = new ResolveInfo();
         r1.serviceInfo = s1;
         ResolveInfo r2 = new ResolveInfo();
         r2.serviceInfo = s2;
-
         when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(
                 ImmutableList.of(r1, r2));
 
@@ -118,9 +128,11 @@
         ServiceInfo s1 = new ServiceInfo();
         s1.permission = "testPermission";
         s1.packageName = "pkg";
+        s1.name = "Service1";
         ServiceInfo s2 = new ServiceInfo();
         s2.permission = "testPermission";
         s2.packageName = "pkg2";
+        s2.name = "service2";
         ResolveInfo r1 = new ResolveInfo();
         r1.serviceInfo = s1;
         ResolveInfo r2 = new ResolveInfo();
@@ -193,4 +205,56 @@
         assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
                 TEST_SETTING)).contains(testComponent2.flattenToString());
     }
+
+    @Test
+    public void testHasPermissionWithoutMeetingCurrentRegs() {
+        ServiceInfo s1 = new ServiceInfo();
+        s1.permission = "testPermission";
+        s1.packageName = "pkg";
+        s1.name = "Service1";
+        ServiceInfo s2 = new ServiceInfo();
+        s2.permission = "testPermission";
+        s2.packageName = "pkg2";
+        s2.name = "service2";
+        ResolveInfo r1 = new ResolveInfo();
+        r1.serviceInfo = s1;
+        ResolveInfo r2 = new ResolveInfo();
+        r2.serviceInfo = s2;
+
+        ComponentName approvedComponent = new ComponentName(s2.packageName, s2.name);
+
+        Settings.Secure.putString(
+                mContext.getContentResolver(), TEST_SETTING, approvedComponent.flattenToString());
+
+        when(mPm.queryIntentServicesAsUser(argThat(
+                filterEquals(new Intent(TEST_INTENT))), anyInt(), anyInt()))
+                .thenReturn(ImmutableList.of(r1));
+        when(mPm.queryIntentServicesAsUser(argThat(
+                filterEquals(new Intent().setComponent(approvedComponent))),
+                anyInt(), anyInt()))
+                .thenReturn(ImmutableList.of(r2));
+
+        mServiceListing = new ServiceListing.Builder(mContext)
+                .setTag("testTag")
+                .setSetting(TEST_SETTING)
+                .setNoun("testNoun")
+                .setIntentAction(TEST_INTENT)
+                .setValidator(info -> {
+                    if (info.packageName.equals("pkg")) {
+                        return true;
+                    }
+                    return false;
+                })
+                .setPermission("testPermission")
+                .build();
+        ServiceListing.Callback callback = mock(ServiceListing.Callback.class);
+        mServiceListing.addCallback(callback);
+        mServiceListing.reload();
+
+        verify(mPm, times(2)).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        ArgumentCaptor<List<ServiceInfo>> captor = ArgumentCaptor.forClass(List.class);
+        verify(callback, times(1)).onServicesReloaded(captor.capture());
+
+        assertThat(captor.getValue()).containsExactlyElementsIn(ImmutableList.of(s2, s1));
+    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 557257d..571b366 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -162,7 +162,6 @@
             initialScene = currentSceneKey,
             canChangeScene = { _ -> viewModel.canChangeScene() },
             transitions = sceneTransitions,
-            enableInterruptions = false,
         )
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index bcd3337..7fb4c53 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -148,6 +148,7 @@
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -165,6 +166,7 @@
 import com.android.internal.R.dimen.system_app_widget_background_radius
 import com.android.systemui.Flags
 import com.android.systemui.Flags.communalTimerFlickerFix
+import com.android.systemui.Flags.communalWidgetResizing
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -176,6 +178,7 @@
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.ResizeInfo
 import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
 import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
 import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -639,6 +642,38 @@
     }
 }
 
+@Composable
+private fun ResizableItemFrameWrapper(
+    key: String,
+    gridState: LazyGridState,
+    minItemSpan: Int,
+    gridContentPadding: PaddingValues,
+    verticalArrangement: Arrangement.Vertical,
+    enabled: Boolean,
+    modifier: Modifier = Modifier,
+    alpha: () -> Float = { 1f },
+    onResize: (info: ResizeInfo) -> Unit = {},
+    content: @Composable (modifier: Modifier) -> Unit,
+) {
+    if (!communalWidgetResizing()) {
+        content(modifier)
+    } else {
+        ResizableItemFrame(
+            key = key,
+            gridState = gridState,
+            minItemSpan = minItemSpan,
+            gridContentPadding = gridContentPadding,
+            verticalArrangement = verticalArrangement,
+            enabled = enabled,
+            alpha = alpha,
+            modifier = modifier,
+            onResize = onResize,
+        ) {
+            content(Modifier)
+        }
+    }
+}
+
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 private fun BoxScope.CommunalHubLazyGrid(
@@ -695,13 +730,14 @@
         gridModifier = gridModifier.height(hubDimensions.GridHeight)
     }
 
+    val itemArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing)
     LazyHorizontalGrid(
         modifier = gridModifier,
         state = gridState,
         rows = GridCells.Fixed(CommunalContentSize.FULL.span),
         contentPadding = contentPadding,
-        horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
-        verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
+        horizontalArrangement = itemArrangement,
+        verticalArrangement = itemArrangement,
     ) {
         itemsIndexed(
             items = list,
@@ -710,35 +746,54 @@
             span = { _, item -> GridItemSpan(item.size.span) },
         ) { index, item ->
             val size = SizeF(Dimensions.CardWidth.value, item.size.dp().value)
-            val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
+            val selected = item.key == selectedKey.value
+            val dpSize = DpSize(size.width.dp, size.height.dp)
+
             if (viewModel.isEditMode && dragDropState != null) {
-                val selected = item.key == selectedKey.value
-                DraggableItem(
+                val outlineAlpha by
+                    animateFloatAsState(
+                        targetValue = if (selected) 1f else 0f,
+                        animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
+                        label = "Widget resizing outline alpha",
+                    )
+                ResizableItemFrameWrapper(
+                    key = item.key,
+                    gridState = gridState,
+                    minItemSpan = CommunalContentSize.HALF.span,
+                    gridContentPadding = contentPadding,
+                    verticalArrangement = itemArrangement,
+                    enabled = selected,
+                    alpha = { outlineAlpha },
                     modifier =
-                        if (dragDropState.draggingItemIndex == index) {
-                            Modifier
-                        } else {
+                        Modifier.requiredSize(dpSize).thenIf(
+                            dragDropState.draggingItemIndex != index
+                        ) {
                             Modifier.animateItem(
                                 placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
                             )
                         },
-                    dragDropState = dragDropState,
-                    selected = selected,
-                    enabled = item.isWidgetContent(),
-                    index = index,
-                ) { isDragging ->
-                    CommunalContent(
-                        modifier = cardModifier,
-                        model = item,
-                        viewModel = viewModel,
-                        size = size,
-                        selected = selected && !isDragging,
-                        widgetConfigurator = widgetConfigurator,
+                    onResize = { resizeInfo -> contentListState.resize(index, resizeInfo) },
+                ) { modifier ->
+                    DraggableItem(
+                        modifier = modifier,
+                        dragDropState = dragDropState,
+                        selected = selected,
+                        enabled = item.isWidgetContent(),
                         index = index,
-                        contentListState = contentListState,
-                        interactionHandler = interactionHandler,
-                        widgetSection = widgetSection,
-                    )
+                    ) { isDragging ->
+                        CommunalContent(
+                            modifier = Modifier.fillMaxSize(),
+                            model = item,
+                            viewModel = viewModel,
+                            size = size,
+                            selected = selected && !isDragging,
+                            widgetConfigurator = widgetConfigurator,
+                            index = index,
+                            contentListState = contentListState,
+                            interactionHandler = interactionHandler,
+                            widgetSection = widgetSection,
+                        )
+                    }
                 }
             } else {
                 CommunalContent(
@@ -746,7 +801,7 @@
                     viewModel = viewModel,
                     size = size,
                     selected = false,
-                    modifier = cardModifier.animateItem(),
+                    modifier = Modifier.requiredSize(dpSize).animateItem(),
                     index = index,
                     contentListState = contentListState,
                     interactionHandler = interactionHandler,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 1137357..6e30575 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -21,8 +21,12 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.toMutableStateList
+import com.android.systemui.Flags.communalWidgetResizing
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.DragHandle
+import com.android.systemui.communal.ui.viewmodel.ResizeInfo
 import com.android.systemui.communal.widgets.WidgetConfigurator
 
 @Composable
@@ -35,15 +39,11 @@
         ContentListState(
             communalContent,
             { componentName, user, rank ->
-                viewModel.onAddWidget(
-                    componentName,
-                    user,
-                    rank,
-                    widgetConfigurator,
-                )
+                viewModel.onAddWidget(componentName, user, rank, widgetConfigurator)
             },
             viewModel::onDeleteWidget,
             viewModel::onReorderWidgets,
+            viewModel::onResizeWidget,
         )
     }
 }
@@ -59,6 +59,7 @@
     private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
     private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
     private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
+    private val onResizeWidget: (id: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) -> Unit,
 ) {
     var list = communalContent.toMutableStateList()
         private set
@@ -77,6 +78,36 @@
         }
     }
 
+    /** Resize a widget, possibly re-ordering widgets if needed. */
+    fun resize(index: Int, resizeInfo: ResizeInfo) {
+        val item = list[index]
+        val currentSpan = item.size.span
+        val newSpan = currentSpan + resizeInfo.spans
+        // Only widgets can be resized
+        if (
+            !communalWidgetResizing() ||
+                currentSpan == newSpan ||
+                item !is CommunalContentModel.WidgetContent.Widget
+        ) {
+            return
+        }
+        list[index] = item.copy(size = CommunalContentSize.toSize(newSpan))
+        val prevItem = list.getOrNull(index - 1)
+        // Check if we have to update indices of items to accommodate the resize.
+        val widgetIdToRankMap: Map<Int, Int> =
+            if (
+                resizeInfo.isExpanding &&
+                    resizeInfo.fromHandle == DragHandle.TOP &&
+                    prevItem is CommunalContentModel.WidgetContent.Widget
+            ) {
+                onMove(index - 1, index)
+                mapOf(prevItem.appWidgetId to index, item.appWidgetId to index - 1)
+            } else {
+                emptyMap()
+            }
+        onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap)
+    }
+
     /**
      * Persists the new order with all the movements happened during drag operations & the new
      * widget drop (if applicable).
@@ -91,7 +122,7 @@
     fun onSaveList(
         newItemComponentName: ComponentName? = null,
         newItemUser: UserHandle? = null,
-        newItemIndex: Int? = null
+        newItemIndex: Int? = null,
     ) {
         // New widget added to the grid. Other widgets are shifted as needed at the database level.
         if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
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 20ee131..101385f 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
@@ -42,6 +42,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
+import com.android.systemui.Flags.communalWidgetResizing
 import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
 import com.android.systemui.communal.ui.compose.extensions.plus
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
@@ -65,7 +66,7 @@
                 state = gridState,
                 contentListState = contentListState,
                 scope = scope,
-                updateDragPositionForRemove = updateDragPositionForRemove
+                updateDragPositionForRemove = updateDragPositionForRemove,
             )
         }
     LaunchedEffect(state) {
@@ -90,7 +91,7 @@
     private val state: LazyGridState,
     private val contentListState: ContentListState,
     private val scope: CoroutineScope,
-    private val updateDragPositionForRemove: (offset: Offset) -> Boolean
+    private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
 ) {
     var draggingItemIndex by mutableStateOf<Int?>(null)
         private set
@@ -122,12 +123,12 @@
         offset: Offset,
         screenWidth: Int,
         layoutDirection: LayoutDirection,
-        contentOffset: Offset
+        contentOffset: Offset,
     ): Boolean {
         val normalizedOffset =
             Offset(
                 if (layoutDirection == LayoutDirection.Ltr) offset.x else screenWidth - offset.x,
-                offset.y
+                offset.y,
             )
         state.layoutInfo.visibleItemsInfo
             .filter { item -> contentListState.isItemEditable(item.index) }
@@ -248,7 +249,7 @@
                             offset,
                             screenWidth,
                             layoutDirection,
-                            contentOffset
+                            contentOffset,
                         )
                     ) {
                         viewModel.onReorderWidgetStart()
@@ -261,7 +262,7 @@
                 onDragCancel = {
                     dragDropState.onDragInterrupted()
                     viewModel.onReorderWidgetCancel()
-                }
+                },
             )
         }
     )
@@ -276,7 +277,7 @@
     enabled: Boolean,
     selected: Boolean,
     modifier: Modifier = Modifier,
-    content: @Composable (isDragging: Boolean) -> Unit
+    content: @Composable (isDragging: Boolean) -> Unit,
 ) {
     if (!enabled) {
         return content(false)
@@ -286,7 +287,7 @@
     val itemAlpha: Float by
         animateFloatAsState(
             targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f,
-            label = "DraggableItemAlpha"
+            label = "DraggableItemAlpha",
         )
     val direction = LocalLayoutDirection.current
     val draggingModifier =
@@ -303,12 +304,17 @@
 
     // Animate the highlight alpha manually as alpha modifier (and AnimatedVisibility) clips the
     // widget to bounds, which cuts off the highlight as we are drawing outside the widget bounds.
+    val highlightSelected = !communalWidgetResizing() && selected
     val alpha by
         animateFloatAsState(
             targetValue =
-                if ((dragging || selected) && !dragDropState.isDraggingToRemove) 1f else 0f,
+                if ((dragging || highlightSelected) && !dragDropState.isDraggingToRemove) {
+                    1f
+                } else {
+                    0f
+                },
             animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
-            label = "Widget outline alpha"
+            label = "Widget outline alpha",
         )
 
     Box(modifier) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
index fda46b8..ef62eb7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
@@ -30,6 +30,8 @@
 import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -52,12 +54,11 @@
 import com.android.systemui.lifecycle.rememberViewModel
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filterNotNull
 
 @Composable
 private fun UpdateGridLayoutInfo(
     viewModel: ResizeableItemFrameViewModel,
-    index: Int,
+    key: String,
     gridState: LazyGridState,
     minItemSpan: Int,
     gridContentPadding: PaddingValues,
@@ -67,7 +68,7 @@
     LaunchedEffect(
         density,
         viewModel,
-        index,
+        key,
         gridState,
         minItemSpan,
         gridContentPadding,
@@ -85,9 +86,8 @@
                 snapshotFlow { gridState.layoutInfo.maxSpan },
                 snapshotFlow { gridState.layoutInfo.viewportSize.height },
                 snapshotFlow {
-                        gridState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }
-                    }
-                    .filterNotNull(),
+                    gridState.layoutInfo.visibleItemsInfo.firstOrNull { it.key == key }
+                },
                 ::Triple,
             )
             .collectLatest { (maxItemSpan, viewportHeightPx, itemInfo) ->
@@ -97,8 +97,8 @@
                     viewportHeightPx,
                     maxItemSpan,
                     minItemSpan,
-                    itemInfo.row,
-                    itemInfo.span,
+                    itemInfo?.row,
+                    itemInfo?.span,
                 )
             }
     }
@@ -161,7 +161,7 @@
  */
 @Composable
 fun ResizableItemFrame(
-    index: Int,
+    key: String,
     gridState: LazyGridState,
     minItemSpan: Int,
     gridContentPadding: PaddingValues,
@@ -177,6 +177,7 @@
     content: @Composable () -> Unit,
 ) {
     val brush = SolidColor(outlineColor)
+    val onResizeUpdated by rememberUpdatedState(onResize)
     val viewModel =
         rememberViewModel(traceName = "ResizeableItemFrame.viewModel") {
             ResizeableItemFrameViewModel()
@@ -230,13 +231,15 @@
 
             UpdateGridLayoutInfo(
                 viewModel,
-                index,
+                key,
                 gridState,
                 minItemSpan,
                 gridContentPadding,
                 verticalArrangement,
             )
-            LaunchedEffect(viewModel) { viewModel.resizeInfo.collectLatest(onResize) }
+            LaunchedEffect(viewModel) {
+                viewModel.resizeInfo.collectLatest { info -> onResizeUpdated(info) }
+            }
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 97d89a2..afa92f2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -83,11 +83,7 @@
             }
 
         val state = remember {
-            MutableSceneTransitionLayoutState(
-                currentScene,
-                ClockTransition.defaultClockTransitions,
-                enableInterruptions = false,
-            )
+            MutableSceneTransitionLayoutState(currentScene, ClockTransition.defaultClockTransitions)
         }
 
         // Update state whenever currentSceneKey has changed.
@@ -102,7 +98,7 @@
                 scene(splitShadeLargeClockScene) {
                     LargeClockWithSmartSpace(
                         smartSpacePaddingTop = smartSpacePaddingTop,
-                        shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation
+                        shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation,
                     )
                 }
 
@@ -114,21 +110,15 @@
                 }
 
                 scene(smallClockScene) {
-                    SmallClockWithSmartSpace(
-                        smartSpacePaddingTop = smartSpacePaddingTop,
-                    )
+                    SmallClockWithSmartSpace(smartSpacePaddingTop = smartSpacePaddingTop)
                 }
 
                 scene(largeClockScene) {
-                    LargeClockWithSmartSpace(
-                        smartSpacePaddingTop = smartSpacePaddingTop,
-                    )
+                    LargeClockWithSmartSpace(smartSpacePaddingTop = smartSpacePaddingTop)
                 }
 
                 scene(WeatherClockScenes.largeClockScene) {
-                    WeatherLargeClockWithSmartSpace(
-                        smartSpacePaddingTop = smartSpacePaddingTop,
-                    )
+                    WeatherLargeClockWithSmartSpace(smartSpacePaddingTop = smartSpacePaddingTop)
                 }
 
                 scene(WeatherClockScenes.splitShadeLargeClockScene) {
@@ -154,7 +144,7 @@
                 SmallClock(
                     burnInParams = burnIn.parameters,
                     onTopChanged = burnIn.onSmallClockTopChanged,
-                    modifier = Modifier.wrapContentSize()
+                    modifier = Modifier.wrapContentSize(),
                 )
             }
             with(smartSpaceSection) {
@@ -202,7 +192,7 @@
                                     y = 0,
                                 )
                             }
-                        }
+                        },
                 )
             }
         }
@@ -226,10 +216,7 @@
         Column(modifier = modifier) {
             val currentClock = currentClockState.value ?: return@Column
             with(weatherClockSection) {
-                Time(
-                    clock = currentClock,
-                    burnInParams = burnIn.parameters,
-                )
+                Time(clock = currentClock, burnInParams = burnIn.parameters)
             }
             val density = LocalDensity.current
             val context = LocalContext.current
@@ -242,7 +229,7 @@
                     modifier =
                         Modifier.heightIn(
                             min = getDimen(context, "enhanced_smartspace_height", density)
-                        )
+                        ),
                 )
             }
             with(weatherClockSection) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index dc3135d..aa70a0c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -42,10 +42,8 @@
 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
 import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.node.ObserverModifierNode
 import androidx.compose.ui.node.PointerInputModifierNode
 import androidx.compose.ui.node.currentValueOf
-import androidx.compose.ui.node.observeReads
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Velocity
@@ -79,7 +77,6 @@
 @Stable
 internal fun Modifier.multiPointerDraggable(
     orientation: Orientation,
-    enabled: () -> Boolean,
     startDragImmediately: (startedPosition: Offset) -> Boolean,
     onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
     onFirstPointerDown: () -> Unit = {},
@@ -89,7 +86,6 @@
     this.then(
         MultiPointerDraggableElement(
             orientation,
-            enabled,
             startDragImmediately,
             onDragStarted,
             onFirstPointerDown,
@@ -100,7 +96,6 @@
 
 private data class MultiPointerDraggableElement(
     private val orientation: Orientation,
-    private val enabled: () -> Boolean,
     private val startDragImmediately: (startedPosition: Offset) -> Boolean,
     private val onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
@@ -111,7 +106,6 @@
     override fun create(): MultiPointerDraggableNode =
         MultiPointerDraggableNode(
             orientation = orientation,
-            enabled = enabled,
             startDragImmediately = startDragImmediately,
             onDragStarted = onDragStarted,
             onFirstPointerDown = onFirstPointerDown,
@@ -121,7 +115,6 @@
 
     override fun update(node: MultiPointerDraggableNode) {
         node.orientation = orientation
-        node.enabled = enabled
         node.startDragImmediately = startDragImmediately
         node.onDragStarted = onDragStarted
         node.onFirstPointerDown = onFirstPointerDown
@@ -131,27 +124,23 @@
 
 internal class MultiPointerDraggableNode(
     orientation: Orientation,
-    enabled: () -> Boolean,
     var startDragImmediately: (startedPosition: Offset) -> Boolean,
     var onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
     var onFirstPointerDown: () -> Unit,
-    var swipeDetector: SwipeDetector = DefaultSwipeDetector,
+    swipeDetector: SwipeDetector = DefaultSwipeDetector,
     private val dispatcher: NestedScrollDispatcher,
 ) :
     DelegatingNode(),
     PointerInputModifierNode,
     CompositionLocalConsumerModifierNode,
-    ObserverModifierNode,
     SpaceVectorConverter {
     private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
     private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
     private val velocityTracker = VelocityTracker()
-    private var previousEnabled: Boolean = false
 
-    var enabled: () -> Boolean = enabled
+    var swipeDetector: SwipeDetector = swipeDetector
         set(value) {
-            // Reset the pointer input whenever enabled changed.
             if (value != field) {
                 field = value
                 pointerInput.resetPointerInputHandler()
@@ -178,21 +167,6 @@
             }
         }
 
-    override fun onAttach() {
-        previousEnabled = enabled()
-        onObservedReadsChanged()
-    }
-
-    override fun onObservedReadsChanged() {
-        observeReads {
-            val newEnabled = enabled()
-            if (newEnabled != previousEnabled) {
-                pointerInput.resetPointerInputHandler()
-            }
-            previousEnabled = newEnabled
-        }
-    }
-
     override fun onCancelPointerInput() {
         pointerTracker.onCancelPointerInput()
         pointerInput.onCancelPointerInput()
@@ -254,9 +228,7 @@
                         velocityTracker.resetTracking()
                         velocityTracker.addPointerInputChange(firstPointerDown)
                         startedPosition = firstPointerDown.position
-                        if (enabled()) {
-                            onFirstPointerDown()
-                        }
+                        onFirstPointerDown()
                     }
 
                     // Changes with at least one pointer
@@ -295,10 +267,6 @@
     }
 
     private suspend fun PointerInputScope.pointerInput() {
-        if (!enabled()) {
-            return
-        }
-
         val currentContext = currentCoroutineContext()
         awaitPointerEventScope {
             while (currentContext.isActive) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 98d4aaa..061583a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -41,22 +41,62 @@
     draggableHandler: DraggableHandlerImpl,
     swipeDetector: SwipeDetector,
 ): Modifier {
-    return this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
+    return if (draggableHandler.enabled()) {
+        this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
+    } else {
+        this
+    }
+}
+
+private fun DraggableHandlerImpl.enabled(): Boolean {
+    return isDrivingTransition || contentForSwipes().shouldEnableSwipes(orientation)
+}
+
+private fun DraggableHandlerImpl.contentForSwipes(): Content {
+    return layoutImpl.contentForUserActions()
+}
+
+/** Whether swipe should be enabled in the given [orientation]. */
+private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
+    if (userActions.isEmpty()) {
+        return false
+    }
+
+    return userActions.keys.any { it is Swipe.Resolved && it.direction.orientation == orientation }
 }
 
 private data class SwipeToSceneElement(
     val draggableHandler: DraggableHandlerImpl,
     val swipeDetector: SwipeDetector,
-) : ModifierNodeElement<SwipeToSceneNode>() {
-    override fun create(): SwipeToSceneNode = SwipeToSceneNode(draggableHandler, swipeDetector)
+) : ModifierNodeElement<SwipeToSceneRootNode>() {
+    override fun create(): SwipeToSceneRootNode =
+        SwipeToSceneRootNode(draggableHandler, swipeDetector)
 
-    override fun update(node: SwipeToSceneNode) {
-        node.draggableHandler = draggableHandler
+    override fun update(node: SwipeToSceneRootNode) {
+        node.update(draggableHandler, swipeDetector)
+    }
+}
+
+private class SwipeToSceneRootNode(
+    draggableHandler: DraggableHandlerImpl,
+    swipeDetector: SwipeDetector,
+) : DelegatingNode() {
+    private var delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
+
+    fun update(draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector) {
+        if (draggableHandler == delegate.draggableHandler) {
+            // Simple update, just update the swipe detector directly and keep the node.
+            delegate.swipeDetector = swipeDetector
+        } else {
+            // The draggableHandler changed, force recreate the underlying SwipeToSceneNode.
+            undelegate(delegate)
+            delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
+        }
     }
 }
 
 private class SwipeToSceneNode(
-    draggableHandler: DraggableHandlerImpl,
+    val draggableHandler: DraggableHandlerImpl,
     swipeDetector: SwipeDetector,
 ) : DelegatingNode(), PointerInputModifierNode {
     private val dispatcher = NestedScrollDispatcher()
@@ -64,7 +104,6 @@
         delegate(
             MultiPointerDraggableNode(
                 orientation = draggableHandler.orientation,
-                enabled = ::enabled,
                 startDragImmediately = ::startDragImmediately,
                 onDragStarted = draggableHandler::onDragStarted,
                 onFirstPointerDown = ::onFirstPointerDown,
@@ -73,18 +112,10 @@
             )
         )
 
-    private var _draggableHandler = draggableHandler
-    var draggableHandler: DraggableHandlerImpl
-        get() = _draggableHandler
+    var swipeDetector: SwipeDetector
+        get() = multiPointerDraggableNode.swipeDetector
         set(value) {
-            if (_draggableHandler != value) {
-                _draggableHandler = value
-
-                // Make sure to update the delegate orientation. Note that this will automatically
-                // reset the underlying pointer input handler, so previous gestures will be
-                // cancelled.
-                multiPointerDraggableNode.orientation = value.orientation
-            }
+            multiPointerDraggableNode.swipeDetector = value
         }
 
     private val nestedScrollHandlerImpl =
@@ -124,22 +155,6 @@
 
     override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()
 
-    private fun enabled(): Boolean {
-        return draggableHandler.isDrivingTransition ||
-            contentForSwipes().shouldEnableSwipes(multiPointerDraggableNode.orientation)
-    }
-
-    private fun contentForSwipes(): Content {
-        return draggableHandler.layoutImpl.contentForUserActions()
-    }
-
-    /** Whether swipe should be enabled in the given [orientation]. */
-    private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
-        return userActions.keys.any {
-            it is Swipe.Resolved && it.direction.orientation == orientation
-        }
-    }
-
     private fun startDragImmediately(startedPosition: Offset): Boolean {
         // Immediately start the drag if the user can't swipe in the other direction and the gesture
         // handler can intercept it.
@@ -152,7 +167,7 @@
                 Orientation.Vertical -> Orientation.Horizontal
                 Orientation.Horizontal -> Orientation.Vertical
             }
-        return contentForSwipes().shouldEnableSwipes(oppositeOrientation)
+        return draggableHandler.contentForSwipes().shouldEnableSwipes(oppositeOrientation)
     }
 }
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 493f3a1..c8f6e6d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -45,6 +45,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.modifiers.thenIf
 import com.android.compose.nestedscroll.SuspendedValue
 import com.google.common.truth.Truth.assertThat
 import kotlin.properties.Delegates
@@ -94,19 +95,20 @@
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
                     .nestedScrollDispatcher()
-                    .multiPointerDraggable(
-                        orientation = Orientation.Vertical,
-                        enabled = { enabled },
-                        startDragImmediately = { false },
-                        onDragStarted = { _, _, _ ->
-                            started = true
-                            SimpleDragController(
-                                onDrag = { dragged = true },
-                                onStop = { stopped = true },
-                            )
-                        },
-                        dispatcher = defaultDispatcher,
-                    )
+                    .thenIf(enabled) {
+                        Modifier.multiPointerDraggable(
+                            orientation = Orientation.Vertical,
+                            startDragImmediately = { false },
+                            onDragStarted = { _, _, _ ->
+                                started = true
+                                SimpleDragController(
+                                    onDrag = { dragged = true },
+                                    onStop = { stopped = true },
+                                )
+                            },
+                            dispatcher = defaultDispatcher,
+                        )
+                    }
             )
         }
 
@@ -164,7 +166,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         // We want to start a drag gesture immediately
                         startDragImmediately = { true },
                         onDragStarted = { _, _, _ ->
@@ -238,7 +239,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
@@ -358,7 +358,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
@@ -464,7 +463,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             verticalStarted = true
@@ -477,7 +475,6 @@
                     )
                     .multiPointerDraggable(
                         orientation = Orientation.Horizontal,
-                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             horizontalStarted = true
@@ -570,7 +567,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         startDragImmediately = { false },
                         swipeDetector =
                             object : SwipeDetector {
@@ -672,7 +668,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             SimpleDragController(
@@ -744,7 +739,6 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
-                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             SimpleDragController(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 25e8713..28d0a47 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -22,11 +22,15 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
@@ -36,9 +40,14 @@
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeRight
+import androidx.compose.ui.test.swipeUp
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
@@ -844,4 +853,62 @@
         assertThat(transition.progress).isEqualTo(1f)
         assertThat(availableOnPostScroll).isEqualTo(ovescrollPx)
     }
+
+    @Test
+    fun sceneWithoutSwipesDoesNotConsumeGestures() {
+        val buttonTag = "button"
+
+        rule.setContent {
+            Box {
+                var count by remember { mutableStateOf(0) }
+                Button(onClick = { count++ }, Modifier.testTag(buttonTag).align(Alignment.Center)) {
+                    Text("Count: $count")
+                }
+
+                SceneTransitionLayout(remember { MutableSceneTransitionLayoutState(SceneA) }) {
+                    scene(SceneA) { Box(Modifier.fillMaxSize()) }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 0")
+
+        // Click on the root at its center, where the button is located. Clicks should go through
+        // the STL and reach the button given that there is no swipes for the current scene.
+        repeat(3) { rule.onRoot().performClick() }
+        rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 3")
+    }
+
+    @Test
+    fun swipeToSceneSupportsUpdates() {
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                // SceneA only has vertical actions, so only one vertical Modifier.swipeToScene()
+                // is composed.
+                scene(SceneA, mapOf(Swipe.Up to SceneB)) { Box(Modifier.fillMaxSize()) }
+
+                // SceneB only has horizontal actions, so only one vertical Modifier.swipeToScene()
+                // is composed, which will be force update it with a new draggableHandler.
+                scene(SceneB, mapOf(Swipe.Right to SceneC)) { Box(Modifier.fillMaxSize()) }
+                scene(SceneC) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneA)
+
+        // Swipe up to scene B.
+        rule.onRoot().performTouchInput { swipeUp() }
+        rule.waitForIdle()
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneB)
+
+        // Swipe right to scene C.
+        rule.onRoot().performTouchInput { swipeRight() }
+        rule.waitForIdle()
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneC)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 3d30ecc..8ae9d2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -25,8 +25,10 @@
 import android.graphics.Bitmap
 import android.os.UserHandle
 import android.os.userManager
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.data.repository.fakePackageChangeRepository
 import com.android.systemui.common.shared.model.PackageInstallSession
@@ -156,6 +158,7 @@
                         appWidgetId = communalWidgetItemEntry.widgetId,
                         providerInfo = providerInfoA,
                         rank = communalItemRankEntry.rank,
+                        spanY = communalWidgetItemEntry.spanY,
                     )
                 )
 
@@ -188,11 +191,13 @@
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
                         rank = 1,
+                        spanY = 3,
                     ),
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 2,
                         providerInfo = providerInfoB,
                         rank = 2,
+                        spanY = 3,
                     ),
                 )
         }
@@ -219,11 +224,13 @@
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
                         rank = 1,
+                        spanY = 3,
                     ),
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 2,
                         providerInfo = providerInfoB,
                         rank = 2,
+                        spanY = 3,
                     ),
                 )
 
@@ -238,11 +245,13 @@
                         // Verify that provider info updated
                         providerInfo = providerInfoC,
                         rank = 1,
+                        spanY = 3,
                     ),
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 2,
                         providerInfo = providerInfoB,
                         rank = 2,
+                        spanY = 3,
                     ),
                 )
         }
@@ -681,6 +690,7 @@
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
                         rank = 1,
+                        spanY = 3,
                     ),
                     CommunalWidgetContentModel.Pending(
                         appWidgetId = 2,
@@ -688,6 +698,7 @@
                         componentName = ComponentName("pk_2", "cls_2"),
                         icon = fakeIcon,
                         user = mainUser,
+                        spanY = 3,
                     ),
                 )
         }
@@ -723,6 +734,7 @@
                         componentName = ComponentName("pk_1", "cls_1"),
                         icon = fakeIcon,
                         user = mainUser,
+                        spanY = 3,
                     )
                 )
 
@@ -740,20 +752,23 @@
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
                         rank = 1,
+                        spanY = 3,
                     )
                 )
         }
 
     @Test
+    @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
     fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup() =
         testScope.runTest {
             val widgetId = 1
             val newSpanY = 6
+            val widgetIdToRankMap = emptyMap<Int, Int>()
 
-            underTest.updateWidgetSpanY(widgetId, newSpanY)
+            underTest.resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
             runCurrent()
 
-            verify(communalWidgetDao).updateWidgetSpanY(widgetId, newSpanY)
+            verify(communalWidgetDao).resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
             verify(backupManager).dataChanged()
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index b96e40f..611a61a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -24,6 +24,7 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.os.userManager
+import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
 import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
 import android.widget.RemoteViews
@@ -31,6 +32,7 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
@@ -1078,6 +1080,108 @@
             assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
         }
 
+    @Test
+    @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
+    fun resizeWidget_withoutUpdatingOrder() =
+        testScope.runTest {
+            val userInfos = listOf(MAIN_USER_INFO)
+            userRepository.setUserInfos(userInfos)
+            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+            runCurrent()
+
+            // Widgets available.
+            widgetRepository.addWidget(
+                appWidgetId = 1,
+                userId = MAIN_USER_INFO.id,
+                rank = 0,
+                spanY = CommunalContentSize.HALF.span,
+            )
+            widgetRepository.addWidget(
+                appWidgetId = 2,
+                userId = MAIN_USER_INFO.id,
+                rank = 1,
+                spanY = CommunalContentSize.HALF.span,
+            )
+            widgetRepository.addWidget(
+                appWidgetId = 3,
+                userId = MAIN_USER_INFO.id,
+                rank = 2,
+                spanY = CommunalContentSize.HALF.span,
+            )
+
+            val widgetContent by collectLastValue(underTest.widgetContent)
+
+            assertThat(widgetContent?.map { it.appWidgetId to it.size })
+                .containsExactly(
+                    1 to CommunalContentSize.HALF,
+                    2 to CommunalContentSize.HALF,
+                    3 to CommunalContentSize.HALF,
+                )
+                .inOrder()
+
+            underTest.resizeWidget(2, CommunalContentSize.FULL.span, emptyMap())
+
+            // Widget 2 should have been resized to FULL
+            assertThat(widgetContent?.map { it.appWidgetId to it.size })
+                .containsExactly(
+                    1 to CommunalContentSize.HALF,
+                    2 to CommunalContentSize.FULL,
+                    3 to CommunalContentSize.HALF,
+                )
+                .inOrder()
+        }
+
+    @Test
+    @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
+    fun resizeWidget_andUpdateOrder() =
+        testScope.runTest {
+            val userInfos = listOf(MAIN_USER_INFO)
+            userRepository.setUserInfos(userInfos)
+            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+            runCurrent()
+
+            // Widgets available.
+            widgetRepository.addWidget(
+                appWidgetId = 1,
+                userId = MAIN_USER_INFO.id,
+                rank = 0,
+                spanY = CommunalContentSize.HALF.span,
+            )
+            widgetRepository.addWidget(
+                appWidgetId = 2,
+                userId = MAIN_USER_INFO.id,
+                rank = 1,
+                spanY = CommunalContentSize.HALF.span,
+            )
+            widgetRepository.addWidget(
+                appWidgetId = 3,
+                userId = MAIN_USER_INFO.id,
+                rank = 2,
+                spanY = CommunalContentSize.HALF.span,
+            )
+
+            val widgetContent by collectLastValue(underTest.widgetContent)
+
+            assertThat(widgetContent?.map { it.appWidgetId to it.size })
+                .containsExactly(
+                    1 to CommunalContentSize.HALF,
+                    2 to CommunalContentSize.HALF,
+                    3 to CommunalContentSize.HALF,
+                )
+                .inOrder()
+
+            underTest.resizeWidget(2, CommunalContentSize.FULL.span, mapOf(2 to 0, 1 to 1))
+
+            // Widget 2 should have been resized to FULL and moved to the front of the list
+            assertThat(widgetContent?.map { it.appWidgetId to it.size })
+                .containsExactly(
+                    2 to CommunalContentSize.FULL,
+                    1 to CommunalContentSize.HALF,
+                    3 to CommunalContentSize.HALF,
+                )
+                .inOrder()
+        }
+
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
             .thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
index e1946fc..f0d88ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
@@ -254,6 +254,23 @@
         assertThat(resizeInfo).isEqualTo(ResizeInfo(-1, DragHandle.BOTTOM))
     }
 
+    @Test
+    fun testRowInfoBecomesNull_revertsBackToDefault() =
+        testScope.runTest {
+            val gridLayout = singleSpanGrid.copy(maxItemSpan = 3, currentRow = 1)
+            updateGridLayout(gridLayout)
+
+            val topState = underTest.topDragState
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f, -1 to -30f)
+
+            val bottomState = underTest.bottomDragState
+            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f, 1 to 30f)
+
+            updateGridLayout(gridLayout.copy(currentRow = null))
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
+        }
+
     @Test(expected = IllegalArgumentException::class)
     fun testIllegalState_maxSpanSmallerThanMinSpan() =
         testScope.runTest {
@@ -317,7 +334,7 @@
         val viewportHeightPx: Int,
         val maxItemSpan: Int,
         val minItemSpan: Int,
-        val currentRow: Int,
-        val currentSpan: Int,
+        val currentRow: Int?,
+        val currentSpan: Int?,
     )
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 40b2a08..a0c56b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -139,7 +139,7 @@
                 TransitionStep(
                     KeyguardState.OFF,
                     KeyguardState.LOCKSCREEN,
-                    transitionState = TransitionState.STARTED
+                    transitionState = TransitionState.STARTED,
                 )
             )
 
@@ -181,7 +181,7 @@
                 TransitionStep(
                     KeyguardState.AOD,
                     KeyguardState.LOCKSCREEN,
-                    transitionState = TransitionState.STARTED
+                    transitionState = TransitionState.STARTED,
                 )
             )
 
@@ -206,7 +206,7 @@
                 TransitionStep(
                     KeyguardState.DOZING,
                     KeyguardState.LOCKSCREEN,
-                    transitionState = TransitionState.STARTED
+                    transitionState = TransitionState.STARTED,
                 )
             )
 
@@ -229,7 +229,7 @@
                 TransitionStep(
                     KeyguardState.DOZING,
                     KeyguardState.LOCKSCREEN,
-                    transitionState = TransitionState.STARTED
+                    transitionState = TransitionState.STARTED,
                 )
             )
 
@@ -244,12 +244,14 @@
     fun faceAuthLockedOutStateIsUpdatedAfterUserSwitch() =
         testScope.runTest {
             underTest.start()
+            runCurrent()
+            fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
 
             // User switching has started
             fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
             fakeUserRepository.setSelectedUserInfo(
                 primaryUser,
-                SelectionStatus.SELECTION_IN_PROGRESS
+                SelectionStatus.SELECTION_IN_PROGRESS,
             )
             runCurrent()
 
@@ -258,7 +260,7 @@
             facePropertyRepository.setLockoutMode(secondaryUser.id, LockoutMode.NONE)
             fakeUserRepository.setSelectedUserInfo(
                 secondaryUser,
-                SelectionStatus.SELECTION_COMPLETE
+                SelectionStatus.SELECTION_COMPLETE,
             )
             runCurrent()
 
@@ -316,7 +318,7 @@
                 .isEqualTo(
                     Pair(
                         FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
-                        false
+                        false,
                     )
                 )
         }
@@ -600,7 +602,7 @@
 
             faceAuthRepository.requestAuthenticate(
                 FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
-                true
+                true,
             )
             facePropertyRepository.setCameraIno(CameraInfo("0", "1", null))
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 17e3006..047d8c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -42,7 +42,8 @@
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
 import com.android.systemui.kosmos.testScope
@@ -57,7 +58,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito
 import org.mockito.Mockito.reset
 
 @ExperimentalCoroutinesApi
@@ -66,7 +66,7 @@
 class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = Mockito.spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
     private val testScope = kosmos.testScope
     private lateinit var underTest: FromAlternateBouncerTransitionInteractor
@@ -74,7 +74,7 @@
 
     @Before
     fun setup() {
-        transitionRepository = kosmos.fakeKeyguardTransitionRepository
+        transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
         underTest = kosmos.fromAlternateBouncerTransitionInteractor
         underTest.start()
     }
@@ -86,7 +86,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.ALTERNATE_BOUNCER,
-                testScope
+                testScope,
             )
             reset(transitionRepository)
 
@@ -111,7 +111,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.ALTERNATE_BOUNCER,
-                testScope
+                testScope,
             )
             reset(transitionRepository)
 
@@ -129,7 +129,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.ALTERNATE_BOUNCER,
-                testScope
+                testScope,
             )
             reset(transitionRepository)
 
@@ -158,7 +158,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.ALTERNATE_BOUNCER,
-                testScope
+                testScope,
             )
             reset(transitionRepository)
 
@@ -168,7 +168,7 @@
             assertThat(transitionRepository)
                 .startedTransition(
                     from = KeyguardState.ALTERNATE_BOUNCER,
-                    to = KeyguardState.OCCLUDED
+                    to = KeyguardState.OCCLUDED,
                 )
         }
 
@@ -183,7 +183,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.GLANCEABLE_HUB,
                 to = KeyguardState.ALTERNATE_BOUNCER,
-                testScope
+                testScope,
             )
             reset(transitionRepository)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 33f3cd4..9300964 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -42,8 +42,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -69,7 +70,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -77,7 +77,7 @@
 class FromAodTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
 
     private val testScope = kosmos.testScope
@@ -89,7 +89,7 @@
     @Before
     fun setup() {
         powerInteractor = kosmos.powerInteractor
-        transitionRepository = kosmos.fakeKeyguardTransitionRepository
+        transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
         underTest = kosmos.fromAodTransitionInteractor
 
         underTest.start()
@@ -101,7 +101,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.AOD,
-                testScope
+                testScope,
             )
             kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.NONE)
             reset(transitionRepository)
@@ -117,10 +117,7 @@
 
             // Under default conditions, we should transition to LOCKSCREEN when waking up.
             assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.LOCKSCREEN,
-                )
+                .startedTransition(from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN)
         }
 
     @Test
@@ -133,10 +130,7 @@
 
             // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
             assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.OCCLUDED,
-                )
+                .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED)
         }
 
     @Test
@@ -363,13 +357,13 @@
                         from = KeyguardState.GONE,
                         to = KeyguardState.AOD,
                         transitionState = TransitionState.STARTED,
-                        value = 0f
+                        value = 0f,
                     ),
                     TransitionStep(
                         from = KeyguardState.GONE,
                         to = KeyguardState.AOD,
                         transitionState = TransitionState.RUNNING,
-                        value = 0.1f
+                        value = 0.1f,
                     ),
                 ),
                 testScope = testScope,
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 ff0a4a1..3b6e5d0 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
@@ -36,8 +36,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -79,7 +80,7 @@
 class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
             this.fakeCommunalSceneRepository =
                 spy(FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope))
         }
@@ -105,7 +106,7 @@
     @Before
     fun setup() {
         powerInteractor = kosmos.powerInteractor
-        transitionRepository = kosmos.fakeKeyguardTransitionRepository
+        transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
         underTest = kosmos.fromDozingTransitionInteractor
 
         underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index fa304c9..9ca3ce6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -32,7 +32,9 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
@@ -53,7 +55,6 @@
 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
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
@@ -77,14 +78,21 @@
 
     private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.fakeKeyguardTransitionRepository =
+                FakeKeyguardTransitionRepository(
+                    // This test sends transition steps manually in the test cases.
+                    sendTransitionStepsOnStartTransition = false,
+                    testScope = testScope,
+                )
+
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
 
     private val testScope = kosmos.testScope
     private val underTest by lazy { kosmos.fromDreamingTransitionInteractor }
 
     private val powerInteractor = kosmos.powerInteractor
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
 
     @Before
     fun setup() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
index af76b08..57b1299 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -23,10 +23,10 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -41,18 +41,17 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FromGoneTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
     private val testScope = kosmos.testScope
     private val underTest = kosmos.fromGoneTransitionInteractor
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
 
     @Before
     fun setUp() {
@@ -101,9 +100,7 @@
 
             // We're in the middle of a GONE -> LOCKSCREEN transition.
             assertThat(keyguardTransitionRepository)
-                .startedTransition(
-                    to = KeyguardState.LOCKSCREEN,
-                )
+                .startedTransition(to = KeyguardState.LOCKSCREEN)
         }
 
     @Test
@@ -121,15 +118,13 @@
             kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags(
                 AuthenticationFlags(
                     0,
-                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
                 )
             )
             runCurrent()
 
             // We're in the middle of a GONE -> LOCKSCREEN transition.
             assertThat(keyguardTransitionRepository)
-                .startedTransition(
-                    to = KeyguardState.LOCKSCREEN,
-                )
+                .startedTransition(to = KeyguardState.LOCKSCREEN)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 4d81317..9c2e631 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -27,9 +27,11 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -42,10 +44,10 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -53,15 +55,20 @@
 class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
 
     private val testScope = kosmos.testScope
     private val underTest = kosmos.fromLockscreenTransitionInteractor
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private lateinit var transitionRepository: FakeKeyguardTransitionRepository
     private val shadeRepository = kosmos.fakeShadeRepository
     private val keyguardRepository = kosmos.fakeKeyguardRepository
 
+    @Before
+    fun setup() {
+        transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
+    }
+
     @Test
     fun testSurfaceBehindVisibility() =
         testScope.runTest {
@@ -256,4 +263,43 @@
             assertThatRepository(transitionRepository)
                 .startedTransition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.DREAMING)
         }
+
+    @Test
+    fun testTransitionsBackToOccluded_ifOccluded_andCanceledSwipe() =
+        testScope.runTest {
+            underTest.start()
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            keyguardRepository.setKeyguardDismissible(false)
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            reset(transitionRepository)
+
+            shadeRepository.setLegacyShadeTracking(true)
+            runCurrent()
+            shadeRepository.setLegacyShadeExpansion(0.5f)
+            runCurrent()
+
+            assertThatRepository(transitionRepository)
+                .startedTransition(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                )
+            reset(transitionRepository)
+
+            runCurrent()
+
+            shadeRepository.setLegacyShadeExpansion(0.6f)
+            shadeRepository.setLegacyShadeExpansion(0.7f)
+            runCurrent()
+
+            shadeRepository.setLegacyShadeExpansion(1f)
+            runCurrent()
+
+            assertThatRepository(transitionRepository)
+                .startedTransition(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = KeyguardState.LOCKSCREEN,
+                )
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
index 7424320..4a90722 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -42,9 +42,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
 import com.android.systemui.kosmos.testScope
@@ -60,7 +60,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -68,14 +67,14 @@
 class FromOccludedTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
 
     private val testScope = kosmos.testScope
     private val underTest = kosmos.fromOccludedTransitionInteractor
 
     private val powerInteractor = kosmos.powerInteractor
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
 
     @Before
     fun setup() {
@@ -88,7 +87,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.OCCLUDED,
-                testScope
+                testScope,
             )
             reset(transitionRepository)
         }
@@ -102,10 +101,7 @@
             runCurrent()
 
             assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.LOCKSCREEN,
-                )
+                .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.LOCKSCREEN)
         }
 
     @Test
@@ -122,9 +118,6 @@
             runCurrent()
 
             assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                )
+                .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 14f2d65..a7da230 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -44,20 +44,19 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FromPrimaryBouncerTransitionInteractorTest : SysuiTestCase() {
-    val kosmos =
+    private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
     val underTest = kosmos.fromPrimaryBouncerTransitionInteractor
     val testScope = kosmos.testScope
     val selectedUserInteractor = kosmos.selectedUserInteractor
-    val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+    val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
     val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
 
     @Test
@@ -67,12 +66,7 @@
             runCurrent()
 
             // Transition-specific surface visibility should be null ("don't care") initially.
-            assertEquals(
-                listOf(
-                    null,
-                ),
-                values
-            )
+            assertEquals(listOf(null), values)
 
             transitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -86,9 +80,9 @@
 
             assertEquals(
                 listOf(
-                    null, // PRIMARY_BOUNCER -> LOCKSCREEN does not have any specific visibility.
+                    null // PRIMARY_BOUNCER -> LOCKSCREEN does not have any specific visibility.
                 ),
-                values
+                values,
             )
 
             transitionRepository.sendTransitionStep(
@@ -117,7 +111,7 @@
                     null,
                     false, // Surface is only made visible once the bouncer UI animates out.
                 ),
-                values
+                values,
             )
 
             transitionRepository.sendTransitionStep(
@@ -137,7 +131,7 @@
                     false,
                     true, // Surface should eventually be visible.
                 ),
-                values
+                values,
             )
         }
 
@@ -150,7 +144,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.PRIMARY_BOUNCER,
-                testScope
+                testScope,
             )
 
             reset(transitionRepository)
@@ -161,7 +155,7 @@
             assertThat(transitionRepository)
                 .startedTransition(
                     from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.LOCKSCREEN
+                    to = KeyguardState.LOCKSCREEN,
                 )
         }
 
@@ -177,7 +171,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.PRIMARY_BOUNCER,
-                testScope
+                testScope,
             )
 
             reset(transitionRepository)
@@ -188,7 +182,7 @@
             assertThat(transitionRepository)
                 .startedTransition(
                     from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GLANCEABLE_HUB
+                    to = KeyguardState.GLANCEABLE_HUB,
                 )
         }
 
@@ -201,7 +195,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.PRIMARY_BOUNCER,
-                testScope
+                testScope,
             )
 
             reset(transitionRepository)
@@ -218,7 +212,7 @@
             assertThat(transitionRepository)
                 .startedTransition(
                     from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.OCCLUDED
+                    to = KeyguardState.OCCLUDED,
                 )
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index a617484..8f3d549 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -41,9 +41,9 @@
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
@@ -76,7 +76,6 @@
 import org.mockito.Mock
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
 import org.mockito.MockitoAnnotations
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
@@ -91,7 +90,7 @@
 class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
         }
     private val testScope = kosmos.testScope
 
@@ -99,7 +98,7 @@
     private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
     private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
     private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
-    private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+    private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepositorySpy }
     private lateinit var featureFlags: FakeFeatureFlags
 
     // Used to verify transition requests for test output
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt
new file mode 100644
index 0000000..22677b2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+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.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryBackgroundViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest: DeviceEntryBackgroundViewModel by lazy {
+        kosmos.deviceEntryBackgroundViewModel
+    }
+
+    @Test
+    fun lockscreenToDozingTransitionChangesBackgroundViewAlphaToZero() =
+        testScope.runTest {
+            kosmos.fingerprintPropertyRepository.supportsUdfps()
+            val alpha by collectLastValue(underTest.alpha)
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                listOf(dozingToLockscreen(0f, STARTED), dozingToLockscreen(0.1f)),
+                testScope,
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(1.0f)
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                listOf(lockscreenToDozing(0f, STARTED)),
+                testScope,
+            )
+            runCurrent()
+
+            assertThat(alpha).isEqualTo(0.0f)
+        }
+
+    private fun lockscreenToDozing(value: Float, state: TransitionState = RUNNING): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.DOZING,
+            value = value,
+            transitionState = state,
+            ownerName = "DeviceEntryBackgroundViewModelTest",
+        )
+    }
+
+    private fun dozingToLockscreen(value: Float, state: TransitionState = RUNNING): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.DOZING,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+            ownerName = "DeviceEntryBackgroundViewModelTest",
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index 3e5dee6..a1edfc1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -53,7 +53,7 @@
     private val bgExecutor = kosmos.fakeExecutor
     private val userContextProvider: UserContextProvider = kosmos.userTracker
     private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
-    private lateinit var traceurMessageSender: TraceurMessageSender
+    private lateinit var traceurConnection: TraceurConnection
     private val issueRecordingState =
         IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
 
@@ -65,13 +65,13 @@
 
     @Before
     fun setup() {
-        traceurMessageSender = mock<TraceurMessageSender>()
+        traceurConnection = mock<TraceurConnection>()
         underTest =
             IssueRecordingServiceSession(
                 bgExecutor,
                 dialogTransitionAnimator,
                 panelInteractor,
-                traceurMessageSender,
+                traceurConnection,
                 issueRecordingState,
                 iActivityManager,
                 notificationManager,
@@ -85,7 +85,7 @@
         bgExecutor.runAllReady()
 
         Truth.assertThat(issueRecordingState.isRecording).isTrue()
-        verify(traceurMessageSender).startTracing(any<TraceConfig>())
+        verify(traceurConnection).startTracing(any<TraceConfig>())
     }
 
     @Test
@@ -94,12 +94,12 @@
         bgExecutor.runAllReady()
 
         Truth.assertThat(issueRecordingState.isRecording).isFalse()
-        verify(traceurMessageSender).stopTracing()
+        verify(traceurConnection).stopTracing()
     }
 
     @Test
     fun cancelsNotification_afterReceivingShareCommand() {
-        underTest.share(0, null, mContext)
+        underTest.share(0, null)
         bgExecutor.runAllReady()
 
         verify(notificationManager).cancelAsUser(isNull(), anyInt(), any<UserHandle>())
@@ -110,7 +110,7 @@
         issueRecordingState.takeBugreport = true
         val uri = mock<Uri>()
 
-        underTest.share(0, uri, mContext)
+        underTest.share(0, uri)
         bgExecutor.runAllReady()
 
         verify(iActivityManager).requestBugReportWithExtraAttachment(uri)
@@ -121,17 +121,17 @@
         issueRecordingState.takeBugreport = false
         val uri = mock<Uri>()
 
-        underTest.share(0, uri, mContext)
+        underTest.share(0, uri)
         bgExecutor.runAllReady()
 
-        verify(traceurMessageSender).shareTraces(mContext, uri)
+        verify(traceurConnection).shareTraces(uri)
     }
 
     @Test
     fun closesShade_afterReceivingShareCommand() {
         val uri = mock<Uri>()
 
-        underTest.share(0, uri, mContext)
+        underTest.share(0, uri)
         bgExecutor.runAllReady()
 
         verify(panelInteractor).collapsePanels()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 8d84c3e..9639735 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -78,7 +78,6 @@
     @Mock private lateinit var sysuiState: SysUiState
     @Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock private lateinit var traceurMessageSender: TraceurMessageSender
     private val systemClock = FakeSystemClock()
     private val bgExecutor = FakeExecutor(systemClock)
     private val mainExecutor = FakeExecutor(systemClock)
@@ -104,7 +103,7 @@
                     systemUIDialogManager,
                     sysuiState,
                     broadcastDispatcher,
-                    mDialogTransitionAnimator
+                    mDialogTransitionAnimator,
                 )
             )
 
@@ -120,7 +119,6 @@
                     mediaProjectionMetricsLogger,
                     screenCaptureDisabledDialogDelegate,
                     state,
-                    traceurMessageSender
                 ) {
                     latch.countDown()
                 }
@@ -166,7 +164,7 @@
         verify(mediaProjectionMetricsLogger, never())
             .notifyProjectionInitiated(
                 anyInt(),
-                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
             )
         assertThat(screenRecordSwitch.isChecked).isFalse()
     }
@@ -188,7 +186,7 @@
         verify(mediaProjectionMetricsLogger)
             .notifyProjectionInitiated(
                 anyInt(),
-                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
             )
         verify(factory, times(2)).create(any(SystemUIDialog.Delegate::class.java))
     }
@@ -208,7 +206,7 @@
         verify(mediaProjectionMetricsLogger)
             .notifyProjectionInitiated(
                 anyInt(),
-                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
             )
         verify(factory, never()).create()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt
new file mode 100644
index 0000000..d90cca9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.os.IBinder
+import android.os.Looper
+import android.os.Messenger
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserContextProvider
+import com.android.traceur.PresetTraceConfigs
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class TraceurConnectionTest : SysuiTestCase() {
+
+    @Mock private lateinit var userContextProvider: UserContextProvider
+
+    private lateinit var underTest: TraceurConnection
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        whenever(userContextProvider.userContext).thenReturn(mContext)
+        underTest = TraceurConnection.Provider(userContextProvider, Looper.getMainLooper()).create()
+    }
+
+    @Test
+    fun onBoundRunnables_areRun_whenServiceIsBound() {
+        val latch = CountDownLatch(1)
+        underTest.onBound.add { latch.countDown() }
+
+        underTest.onServiceConnected(
+            InstrumentationRegistry.getInstrumentation().componentName,
+            mock(IBinder::class.java),
+        )
+
+        latch.await()
+    }
+
+    @Test
+    fun startTracing_sendsMsg_toStartTracing() {
+        underTest.binder = mock(Messenger::class.java)
+
+        underTest.startTracing(PresetTraceConfigs.getThermalConfig())
+
+        verify(underTest.binder)!!.send(any())
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/UserAwareConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/UserAwareConnectionTest.kt
new file mode 100644
index 0000000..f671bf4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/UserAwareConnectionTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.content.Context
+import android.content.Intent
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserContextProvider
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class UserAwareConnectionTest : SysuiTestCase() {
+
+    @Mock private lateinit var userContextProvider: UserContextProvider
+    @Mock private lateinit var mockContext: Context
+
+    private lateinit var underTest: UserAwareConnection
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        whenever(userContextProvider.userContext).thenReturn(mockContext)
+        whenever(mockContext.bindService(any(), any(), anyInt())).thenReturn(true)
+        underTest = UserAwareConnection(userContextProvider, Intent())
+    }
+
+    @Test
+    fun doBindService_requestToBindToTheService_viaTheCorrectUserContext() {
+        underTest.doBind()
+
+        verify(userContextProvider).userContext
+    }
+
+    @Test
+    fun doBindService_DoesntRequestToBindToTheService_IfAlreadyRequested() {
+        underTest.doBind()
+        underTest.doBind()
+        underTest.doBind()
+
+        verify(userContextProvider, times(1)).userContext
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 5dd4c1c..02bf9db 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -23,6 +23,7 @@
 import androidx.room.Query
 import androidx.room.RoomDatabase
 import androidx.room.Transaction
+import androidx.room.Update
 import androidx.sqlite.db.SupportSQLiteDatabase
 import com.android.systemui.communal.nano.CommunalHubState
 import com.android.systemui.communal.shared.model.CommunalContentSize
@@ -171,8 +172,7 @@
     @Query("UPDATE communal_item_rank_table SET rank = :order WHERE uid = :itemUid")
     fun updateItemRank(itemUid: Long, order: Int)
 
-    @Query("UPDATE communal_widget_table SET span_y = :spanY WHERE widget_id = :widgetId")
-    fun updateWidgetSpanY(widgetId: Int, spanY: Int)
+    @Update fun updateWidget(widget: CommunalWidgetItem)
 
     @Query("DELETE FROM communal_widget_table") fun clearCommunalWidgetsTable()
 
@@ -189,6 +189,15 @@
     }
 
     @Transaction
+    fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+        val widget = getWidgetByIdNow(appWidgetId)
+        if (widget != null) {
+            updateWidget(widget.copy(spanY = spanY))
+        }
+        updateWidgetOrder(widgetIdToRankMap)
+    }
+
+    @Transaction
     fun addWidget(
         widgetId: Int,
         provider: ComponentName,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 3312f3c..0e94289 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -21,6 +21,7 @@
 import android.content.ComponentName
 import android.os.UserHandle
 import android.os.UserManager
+import com.android.systemui.Flags.communalWidgetResizing
 import com.android.systemui.common.data.repository.PackageChangeRepository
 import com.android.systemui.common.shared.model.PackageInstallSession
 import com.android.systemui.communal.data.backup.CommunalBackupUtils
@@ -82,7 +83,7 @@
      *
      * @param widgetIdToRankMap mapping of the widget ids to the rank of the widget.
      */
-    fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {}
+    fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>)
 
     /**
      * Restores the database by reading a state file from disk and updating the widget ids according
@@ -96,10 +97,13 @@
     /**
      * Update the spanY of a widget in the database.
      *
-     * @param widgetId id of the widget to update.
+     * @param appWidgetId id of the widget to update.
      * @param spanY new spanY value for the widget.
+     * @param widgetIdToRankMap mapping of the widget ids to its rank. Allows re-ordering widgets
+     *   alongside the resize, in case resizing also requires re-ordering. This ensures the
+     *   re-ordering is done in the same database transaction as the resize.
      */
-    fun updateWidgetSpanY(widgetId: Int, spanY: Int)
+    fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>)
 }
 
 @SysUISingleton
@@ -135,15 +139,17 @@
                     componentName = widget.componentName,
                     rank = rank.rank,
                     providerInfo = providers[widget.widgetId],
+                    spanY = widget.spanY,
                 )
             }
         }
 
-    override fun updateWidgetSpanY(widgetId: Int, spanY: Int) {
+    override fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+        if (!communalWidgetResizing()) return
         bgScope.launch {
-            communalWidgetDao.updateWidgetSpanY(widgetId, spanY)
+            communalWidgetDao.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
             logger.i({ "Updated spanY of widget $int1 to $int2." }) {
-                int1 = widgetId
+                int1 = appWidgetId
                 int2 = spanY
             }
             backupManager.dataChanged()
@@ -445,7 +451,7 @@
         val appWidgetId: Int,
         val componentName: String,
         val rank: Int,
+        val spanY: Int,
         var providerInfo: AppWidgetProviderInfo? = null,
-        var spanY: Int = 3,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index a687734..3a04d02 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -399,6 +399,10 @@
     fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) =
         widgetRepository.updateWidgetOrder(widgetIdToRankMap)
 
+    fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+        widgetRepository.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+    }
+
     /** Request to unpause work profile that is currently in quiet mode. */
     fun unpauseWorkProfile() {
         managedProfileController.setWorkModeEnabled(true)
@@ -449,6 +453,7 @@
                             providerInfo = widget.providerInfo,
                             appWidgetHost = appWidgetHost,
                             inQuietMode = isQuietModeEnabled(widget.providerInfo.profile),
+                            size = CommunalContentSize.toSize(widget.spanY),
                         )
                     }
                     is CommunalWidgetContentModel.Pending -> {
@@ -457,6 +462,7 @@
                             rank = widget.rank,
                             componentName = widget.componentName,
                             icon = widget.icon,
+                            size = CommunalContentSize.toSize(widget.spanY),
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index c2f6e85..4c2c094 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -62,11 +62,10 @@
             val providerInfo: AppWidgetProviderInfo,
             val appWidgetHost: CommunalAppWidgetHost,
             val inQuietMode: Boolean,
+            override val size: CommunalContentSize,
         ) : WidgetContent {
             override val key = KEY.widget(appWidgetId)
             override val componentName: ComponentName = providerInfo.provider
-            // Widget size is always half.
-            override val size = CommunalContentSize.HALF
 
             /** Whether this widget can be reconfigured after it has already been added. */
             val reconfigurable: Boolean
@@ -79,11 +78,10 @@
             override val appWidgetId: Int,
             override val rank: Int,
             val providerInfo: AppWidgetProviderInfo,
+            override val size: CommunalContentSize,
         ) : WidgetContent {
             override val key = KEY.disabledWidget(appWidgetId)
             override val componentName: ComponentName = providerInfo.provider
-            // Widget size is always half.
-            override val size = CommunalContentSize.HALF
 
             val appInfo: ApplicationInfo?
                 get() = providerInfo.providerInfo?.applicationInfo
@@ -93,11 +91,10 @@
             override val appWidgetId: Int,
             override val rank: Int,
             override val componentName: ComponentName,
+            override val size: CommunalContentSize,
             val icon: Bitmap? = null,
         ) : WidgetContent {
             override val key = KEY.pendingWidget(appWidgetId)
-            // Widget size is always half.
-            override val size = CommunalContentSize.HALF
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index bcbc8f6..0c9ea78 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -25,13 +25,14 @@
 sealed interface CommunalWidgetContentModel {
     val appWidgetId: Int
     val rank: Int
+    val spanY: Int
 
     /** Widget is ready to display */
     data class Available(
         override val appWidgetId: Int,
         val providerInfo: AppWidgetProviderInfo,
         override val rank: Int,
-        val spanY: Int = 3,
+        override val spanY: Int,
     ) : CommunalWidgetContentModel
 
     /** Widget is pending installation */
@@ -41,6 +42,6 @@
         val componentName: ComponentName,
         val icon: Bitmap?,
         val user: UserHandle,
-        val spanY: Int = 3,
+        override val spanY: Int,
     ) : CommunalWidgetContentModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 0929d3e..e25ea4c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -105,7 +105,7 @@
         scene: SceneKey,
         loggingReason: String,
         transitionKey: TransitionKey? = null,
-        keyguardState: KeyguardState? = null
+        keyguardState: KeyguardState? = null,
     ) {
         communalSceneInteractor.changeScene(scene, loggingReason, transitionKey, keyguardState)
     }
@@ -155,17 +155,10 @@
     ) {}
 
     /** Called as the UI requests deleting a widget. */
-    open fun onDeleteWidget(
-        id: Int,
-        componentName: ComponentName,
-        rank: Int,
-    ) {}
+    open fun onDeleteWidget(id: Int, componentName: ComponentName, rank: Int) {}
 
     /** Called as the UI detects a tap event on the widget. */
-    open fun onTapWidget(
-        componentName: ComponentName,
-        rank: Int,
-    ) {}
+    open fun onTapWidget(componentName: ComponentName, rank: Int) {}
 
     /**
      * Called as the UI requests reordering widgets.
@@ -176,10 +169,19 @@
      */
     open fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) {}
 
+    /**
+     * Called as the UI requests resizing a widget.
+     *
+     * @param appWidgetId The id of the widget being resized.
+     * @param spanY The new size of the widget, in grid spans.
+     * @param widgetIdToRankMap Mapping of the widget ids to its rank. Allows re-ordering widgets
+     *   alongside the resize, in case resizing also requires re-ordering. This ensures the
+     *   re-ordering is done in the same database transaction as the resize.
+     */
+    open fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {}
+
     /** Called as the UI requests opening the widget editor with an optional preselected widget. */
-    open fun onOpenWidgetEditor(
-        shouldOpenWidgetPickerOnStart: Boolean = false,
-    ) {}
+    open fun onOpenWidgetEditor(shouldOpenWidgetPickerOnStart: Boolean = false) {}
 
     /** Called as the UI requests to dismiss the CTA tile. */
     open fun onDismissCtaTile() {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 65f0679..3ae9250 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -107,9 +107,9 @@
         allOf(
                 keyguardTransitionInteractor.isFinishedIn(
                     scene = Scenes.Gone,
-                    stateWithoutSceneContainer = KeyguardState.GONE
+                    stateWithoutSceneContainer = KeyguardState.GONE,
                 ),
-                communalInteractor.editModeOpen
+                communalInteractor.editModeOpen,
             )
             .filter { it }
 
@@ -128,17 +128,13 @@
         componentName: ComponentName,
         user: UserHandle,
         rank: Int?,
-        configurator: WidgetConfigurator?
+        configurator: WidgetConfigurator?,
     ) {
         communalInteractor.addWidget(componentName, user, rank, configurator)
         metricsLogger.logAddWidget(componentName.flattenToString(), rank)
     }
 
-    override fun onDeleteWidget(
-        id: Int,
-        componentName: ComponentName,
-        rank: Int,
-    ) {
+    override fun onDeleteWidget(id: Int, componentName: ComponentName, rank: Int) {
         communalInteractor.deleteWidget(id)
         metricsLogger.logRemoveWidget(componentName.flattenToString(), rank)
     }
@@ -146,6 +142,10 @@
     override fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) =
         communalInteractor.updateWidgetOrder(widgetIdToRankMap)
 
+    override fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+        communalInteractor.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+    }
+
     override fun onReorderWidgetStart() {
         // Clear selection status
         setSelectedKey(null)
@@ -173,7 +173,7 @@
         val announcementText =
             context.getString(
                 R.string.accessibility_announcement_communal_widget_added,
-                widgetLabel
+                widgetLabel,
             )
         accessibilityManager.sendAccessibilityEvent(
             AccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT).apply {
@@ -187,7 +187,7 @@
     /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */
     suspend fun onOpenWidgetPicker(
         resources: Resources,
-        activityLauncher: ActivityResultLauncher<Intent>
+        activityLauncher: ActivityResultLauncher<Intent>,
     ): Boolean =
         withContext(backgroundDispatcher) {
             val widgets = communalInteractor.widgetContent.first()
@@ -210,21 +210,21 @@
 
     private fun getWidgetPickerActivityIntent(
         resources: Resources,
-        excludeList: ArrayList<AppWidgetProviderInfo>
+        excludeList: ArrayList<AppWidgetProviderInfo>,
     ): Intent? {
         return Intent(Intent.ACTION_PICK).apply {
             setPackage(launcherPackage)
             putExtra(
                 EXTRA_DESIRED_WIDGET_WIDTH,
-                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
+                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width),
             )
             putExtra(
                 EXTRA_DESIRED_WIDGET_HEIGHT,
-                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
+                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height),
             )
             putExtra(
                 AppWidgetManager.EXTRA_CATEGORY_FILTER,
-                CommunalWidgetCategories.defaultCategories
+                CommunalWidgetCategories.defaultCategories,
             )
 
             communalSettingsInteractor.workProfileUserDisallowedByDevicePolicy.value?.let {
@@ -234,7 +234,7 @@
             putExtra(EXTRA_PICKER_TITLE, resources.getString(R.string.communal_widget_picker_title))
             putExtra(
                 EXTRA_PICKER_DESCRIPTION,
-                resources.getString(R.string.communal_widget_picker_description)
+                resources.getString(R.string.communal_widget_picker_description),
             )
             putParcelableArrayListExtra(EXTRA_ADDED_APP_WIDGETS_KEY, excludeList)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
index 7aad33d..87fcdd7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
@@ -25,8 +25,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.dropWhile
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
@@ -45,7 +44,10 @@
     val spans: Int,
     /** The drag handle which was used to resize the element. */
     val fromHandle: DragHandle,
-)
+) {
+    /** Whether we are expanding. If false, then we are shrinking. */
+    val isExpanding = spans > 0
+}
 
 class ResizeableItemFrameViewModel : ExclusiveActivatable() {
     private data class GridLayoutInfo(
@@ -73,7 +75,7 @@
                 snapshotFlow { bottomDragState.settledValue }
                     .map { ResizeInfo(it, DragHandle.BOTTOM) },
             )
-            .dropWhile { it.spans == 0 }
+            .filter { it.spans != 0 }
             .distinctUntilChanged()
 
     /**
@@ -86,9 +88,13 @@
         viewportHeightPx: Int,
         maxItemSpan: Int,
         minItemSpan: Int,
-        currentRow: Int,
-        currentSpan: Int,
+        currentRow: Int?,
+        currentSpan: Int?,
     ) {
+        if (currentSpan == null || currentRow == null) {
+            gridLayoutInfo.value = null
+            return
+        }
         require(maxItemSpan >= minItemSpan) {
             "Maximum item span of $maxItemSpan cannot be less than the minimum span of $minItemSpan"
         }
@@ -114,10 +120,10 @@
 
     private fun calculateAnchorsForHandle(
         handle: DragHandle,
-        layoutInfo: GridLayoutInfo,
+        layoutInfo: GridLayoutInfo?,
     ): DraggableAnchors<Int> {
 
-        if (!isDragAllowed(handle, layoutInfo)) {
+        if (layoutInfo == null || !isDragAllowed(handle, layoutInfo)) {
             return DraggableAnchors { 0 at 0f }
         }
 
@@ -188,7 +194,6 @@
     override suspend fun onActivated(): Nothing {
         coroutineScope("ResizeableItemFrameViewModel.onActivated") {
             gridLayoutInfo
-                .filterNotNull()
                 .onEach { layoutInfo ->
                     topDragState.updateAnchors(
                         calculateAnchorsForHandle(DragHandle.TOP, layoutInfo)
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 3b5d5a8..b19b2d9 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -114,7 +114,7 @@
                 faceAuthenticationLogger.bouncerVisibilityChanged()
                 runFaceAuth(
                     FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
-                    fallbackToDetect = false
+                    fallbackToDetect = false,
                 )
             }
             .launchIn(applicationScope)
@@ -125,7 +125,7 @@
                 faceAuthenticationLogger.alternateBouncerVisibilityChanged()
                 runFaceAuth(
                     FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
-                    fallbackToDetect = false
+                    fallbackToDetect = false,
                 )
             }
             .launchIn(applicationScope)
@@ -153,7 +153,7 @@
                     it.lastWakeReason.powerManagerWakeReason
                 runFaceAuth(
                     FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
-                    fallbackToDetect = true
+                    fallbackToDetect = true,
                 )
             }
             .launchIn(applicationScope)
@@ -193,13 +193,16 @@
             .map { (_, curr) -> curr.userInfo.id }
             .sample(isBouncerVisible, ::Pair)
             .onEach { (userId, isBouncerCurrentlyVisible) ->
+                if (!isFaceAuthEnabledAndEnrolled()) {
+                    return@onEach
+                }
                 resetLockedOutState(userId)
                 yield()
                 runFaceAuth(
                     FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
                     // Fallback to detection if bouncer is not showing so that we can detect a
                     // face and then show the bouncer to the user if face auth can't run
-                    fallbackToDetect = !isBouncerCurrentlyVisible
+                    fallbackToDetect = !isBouncerCurrentlyVisible,
                 )
             }
             .launchIn(applicationScope)
@@ -210,7 +213,7 @@
                     repository.cancel()
                     runFaceAuth(
                         FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED,
-                        fallbackToDetect = true
+                        fallbackToDetect = true,
                     )
                 }
             }
@@ -321,7 +324,7 @@
             faceAuthenticationStatusOverride.value =
                 ErrorFaceAuthenticationStatus(
                     BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
-                    context.resources.getString(R.string.keyguard_face_unlock_unavailable)
+                    context.resources.getString(R.string.keyguard_face_unlock_unavailable),
                 )
         } else {
             faceAuthenticationStatusOverride.value = null
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
index 0e2d9b6..43e39cf 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
@@ -30,7 +30,7 @@
 import java.time.Clock
 import javax.inject.Inject
 import kotlin.time.Duration
-import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.days
 import kotlin.time.DurationUnit
 import kotlin.time.toDuration
 import kotlinx.coroutines.CoroutineScope
@@ -64,7 +64,7 @@
             get() =
                 SystemProperties.getLong(
                         "persist.contextual_edu.initial_delay_sec",
-                        /* defaultValue= */ 72.hours.inWholeSeconds
+                        /* defaultValue= */ 7.days.inWholeSeconds,
                     )
                     .toDuration(DurationUnit.SECONDS)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index ca1a800..68244d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -60,6 +60,7 @@
     primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
     primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel,
     primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
+    lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
 ) {
     val color: Flow<Int> =
         deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground ->
@@ -103,7 +104,9 @@
                         offToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         primaryBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         primaryBouncerToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
-                        primaryBouncerToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                        primaryBouncerToLockscreenTransitionViewModel
+                            .deviceEntryBackgroundViewAlpha,
+                        lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
                     )
                     .merge()
                     .onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index d3eefca..7abf35d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -55,16 +55,16 @@
             onCancel = { 1f },
         )
 
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
             isUdfpsEnrolledAndEnabled ->
             if (isUdfpsEnrolledAndEnabled) {
                 transitionAnimation.immediatelyTransitionTo(1f)
             } else {
-                transitionAnimation.sharedFlow(
-                    duration = 250.milliseconds,
-                    onStep = { 1f - it },
-                )
+                transitionAnimation.sharedFlow(duration = 250.milliseconds, onStep = { 1f - it })
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
index 331aabb..0dedfe1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
@@ -46,6 +46,8 @@
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import platform.test.motion.compose.values.MotionTestValueKey
+import platform.test.motion.compose.values.motionTestValues
 
 @Composable
 fun PagerDots(
@@ -93,13 +95,22 @@
     }
 
     Row(
-        modifier = modifier.wrapContentWidth().pagerDotsSemantics(pagerState, coroutineScope),
+        modifier =
+            modifier
+                .motionTestValues { activeMarkerWidth exportAs PagerDotsMotionKeys.indicatorWidth }
+                .wrapContentWidth()
+                .pagerDotsSemantics(pagerState, coroutineScope),
         horizontalArrangement = spacedBy(spaceSize),
         verticalAlignment = Alignment.CenterVertically,
     ) {
         // This means that the active rounded rect has to be drawn between the current page
         // and the previous one (as we are animating back), or the current one if not transitioning
-        val withPrevious = pagerState.currentPageOffsetFraction <= 0 || pagerState.isOverscrolling()
+        val withPrevious by
+            remember(pagerState) {
+                derivedStateOf {
+                    pagerState.currentPageOffsetFraction <= 0 || pagerState.isOverscrolling()
+                }
+            }
         repeat(pagerState.pageCount) { page ->
             Canvas(Modifier.size(dotSize)) {
                 val rtl = layoutDirection == LayoutDirection.Rtl
@@ -127,6 +138,10 @@
     }
 }
 
+object PagerDotsMotionKeys {
+    val indicatorWidth = MotionTestValueKey<Dp>("indicatorWidth")
+}
+
 private fun Modifier.pagerDotsSemantics(
     pagerState: PagerState,
     coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index d89e73d..fb406d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -45,10 +45,11 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
 import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
+import com.android.systemui.recordissue.IssueRecordingServiceConnection
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.recordissue.RecordIssueDialogDelegate
 import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
-import com.android.systemui.recordissue.TraceurMessageSender
+import com.android.systemui.recordissue.TraceurConnection
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.RecordingService
@@ -66,7 +67,7 @@
 constructor(
     host: QSHost,
     uiEventLogger: QsEventLogger,
-    @Background backgroundLooper: Looper,
+    @Background private val backgroundLooper: Looper,
     @Main mainHandler: Handler,
     falsingManager: FalsingManager,
     metricsLogger: MetricsLogger,
@@ -78,7 +79,8 @@
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val panelInteractor: PanelInteractor,
     private val userContextProvider: UserContextProvider,
-    private val traceurMessageSender: TraceurMessageSender,
+    irsConnProvider: IssueRecordingServiceConnection.Provider,
+    traceurConnProvider: TraceurConnection.Provider,
     @Background private val bgExecutor: Executor,
     private val issueRecordingState: IssueRecordingState,
     private val delegateFactory: RecordIssueDialogDelegate.Factory,
@@ -93,11 +95,20 @@
         metricsLogger,
         statusBarStateController,
         activityStarter,
-        qsLogger
+        qsLogger,
     ) {
 
     private val onRecordingChangeListener = Runnable { refreshState() }
 
+    private val irsConnection: IssueRecordingServiceConnection = irsConnProvider.create()
+    private val traceurConnection =
+        traceurConnProvider.create().apply {
+            onBound.add {
+                getTags(issueRecordingState)
+                doUnBind()
+            }
+        }
+
     override fun handleSetListening(listening: Boolean) {
         super.handleSetListening(listening)
         if (listening) {
@@ -109,7 +120,7 @@
 
     override fun handleDestroy() {
         super.handleDestroy()
-        bgExecutor.execute { traceurMessageSender.unbindFromTraceur(mContext) }
+        bgExecutor.execute { irsConnection.doUnBind() }
     }
 
     override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label)
@@ -142,7 +153,7 @@
             DELAY_MS,
             INTERVAL_MS,
             pendingServiceIntent(getStartIntent(userContextProvider.userContext)),
-            pendingServiceIntent(getStopIntent(userContextProvider.userContext))
+            pendingServiceIntent(getStopIntent(userContextProvider.userContext)),
         )
 
     private fun stopIssueRecordingService() =
@@ -154,10 +165,19 @@
             userContextProvider.userContext,
             RecordingService.REQUEST_CODE,
             action,
-            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
         )
 
     private fun showPrompt(expandable: Expandable?) {
+        bgExecutor.execute {
+            // We only want to get the tags once per session, as this is not likely to change, if at
+            // all on a month to month basis. Using onBound's size is a way to verify if the tag
+            // retrieval has already happened or not.
+            if (traceurConnection.onBound.isNotEmpty()) {
+                traceurConnection.doBind()
+            }
+            irsConnection.doBind()
+        }
         val dialog: AlertDialog =
             delegateFactory
                 .create {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 4d2bc91..3f875bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -23,9 +23,12 @@
 import android.content.res.Resources
 import android.net.Uri
 import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
 import android.util.Log
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.LongRunning
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
@@ -42,6 +45,7 @@
 @Inject
 constructor(
     controller: RecordingController,
+    @Background private val bgLooper: Looper,
     @LongRunning private val bgExecutor: Executor,
     @Main handler: Handler,
     uiEventLogger: UiEventLogger,
@@ -50,8 +54,8 @@
     keyguardDismissUtil: KeyguardDismissUtil,
     dialogTransitionAnimator: DialogTransitionAnimator,
     panelInteractor: PanelInteractor,
-    traceurMessageSender: TraceurMessageSender,
     private val issueRecordingState: IssueRecordingState,
+    traceurConnectionProvider: TraceurConnection.Provider,
     iActivityManager: IActivityManager,
 ) :
     RecordingService(
@@ -64,18 +68,37 @@
         keyguardDismissUtil,
     ) {
 
+    private val traceurConnection: TraceurConnection = traceurConnectionProvider.create()
+
     private val session =
         IssueRecordingServiceSession(
             bgExecutor,
             dialogTransitionAnimator,
             panelInteractor,
-            traceurMessageSender,
+            traceurConnection,
             issueRecordingState,
             iActivityManager,
             notificationManager,
             userContextProvider,
         )
 
+    /**
+     * It is necessary to bind to IssueRecordingService from the Record Issue Tile because there are
+     * instances where this service is not created in the same user profile as the record issue tile
+     * aka, headless system user mode. In those instances, the TraceurConnection will be considered
+     * a leak in between notification actions unless the tile is bound to this service to keep it
+     * alive.
+     */
+    override fun onBind(intent: Intent): IBinder? {
+        traceurConnection.doBind()
+        return super.onBind(intent)
+    }
+
+    override fun onUnbind(intent: Intent?): Boolean {
+        traceurConnection.doUnBind()
+        return super.onUnbind(intent)
+    }
+
     override fun getTag(): String = TAG
 
     override fun getChannelId(): String = CHANNEL_ID
@@ -99,7 +122,6 @@
                 session.share(
                     intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId),
                     intent.getParcelableExtra(EXTRA_PATH, Uri::class.java),
-                    this,
                 )
                 // Unlike all other actions, action_share has different behavior for the screen
                 // recording qs tile than it does for the record issue qs tile. Return sticky to
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt
new file mode 100644
index 0000000..85a5805
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.content.Intent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserContextProvider
+import com.android.traceur.MessageConstants.SYSTEM_UI_PACKAGE_NAME
+import javax.inject.Inject
+
+/**
+ * It is necessary to bind to IssueRecordingService from the Record Issue Tile because there are
+ * instances where this service is not created in the same user profile as the record issue tile
+ * aka, headless system user mode. In those instances, the TraceurConnection will be considered a
+ * leak in between notification actions unless the tile is bound to this service to keep it alive.
+ */
+class IssueRecordingServiceConnection(userContextProvider: UserContextProvider) :
+    UserAwareConnection(
+        userContextProvider,
+        Intent().setClassName(SYSTEM_UI_PACKAGE_NAME, IssueRecordingService::class.java.name),
+    ) {
+    @SysUISingleton
+    class Provider @Inject constructor(private val userContextProvider: UserContextProvider) {
+        fun create() = IssueRecordingServiceConnection(userContextProvider)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
index e4d3e6c..ad9b4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
@@ -19,7 +19,6 @@
 import android.app.IActivityManager
 import android.app.NotificationManager
 import android.content.ContentResolver
-import android.content.Context
 import android.net.Uri
 import android.os.UserHandle
 import android.provider.Settings
@@ -42,7 +41,7 @@
     private val bgExecutor: Executor,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val panelInteractor: PanelInteractor,
-    private val traceurMessageSender: TraceurMessageSender,
+    private val traceurConnection: TraceurConnection,
     private val issueRecordingState: IssueRecordingState,
     private val iActivityManager: IActivityManager,
     private val notificationManager: NotificationManager,
@@ -50,7 +49,7 @@
 ) {
 
     fun start() {
-        bgExecutor.execute { traceurMessageSender.startTracing(issueRecordingState.traceConfig) }
+        bgExecutor.execute { traceurConnection.startTracing(issueRecordingState.traceConfig) }
         issueRecordingState.isRecording = true
     }
 
@@ -59,12 +58,12 @@
             if (issueRecordingState.traceConfig.longTrace) {
                 Settings.Global.putInt(contentResolver, NOTIFY_SESSION_ENDED_SETTING, DISABLED)
             }
-            traceurMessageSender.stopTracing()
+            traceurConnection.stopTracing()
         }
         issueRecordingState.isRecording = false
     }
 
-    fun share(notificationId: Int, screenRecording: Uri?, context: Context) {
+    fun share(notificationId: Int, screenRecording: Uri?) {
         bgExecutor.execute {
             notificationManager.cancelAsUser(
                 null,
@@ -75,7 +74,7 @@
             if (issueRecordingState.takeBugreport) {
                 iActivityManager.requestBugReportWithExtraAttachment(screenRecording)
             } else {
-                traceurMessageSender.shareTraces(context, screenRecording)
+                traceurConnection.shareTraces(screenRecording)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index ed67e64..6758c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -64,7 +64,6 @@
     private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
     private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate,
     private val state: IssueRecordingState,
-    private val traceurMessageSender: TraceurMessageSender,
     @Assisted private val onStarted: Runnable,
 ) : SystemUIDialog.Delegate {
 
@@ -87,10 +86,6 @@
             setNegativeButton(R.string.cancel) { _, _ -> }
             setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() }
         }
-        bgExecutor.execute {
-            traceurMessageSender.onBoundToTraceur.add { traceurMessageSender.getTags(state) }
-            traceurMessageSender.bindToTraceur(dialog.context)
-        }
     }
 
     override fun createDialog(): SystemUIDialog = factory.create(this)
@@ -151,7 +146,7 @@
 
         mediaProjectionMetricsLogger.notifyProjectionInitiated(
             userTracker.userId,
-            SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+            SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER,
         )
 
         if (!state.hasUserApprovedScreenRecording) {
@@ -189,7 +184,7 @@
                         CustomTraceSettingsDialogDelegate(
                                 factory,
                                 state.customTraceState,
-                                state.tagTitles
+                                state.tagTitles,
                             ) {
                                 onMenuItemClickListener.onMenuItemClick(it)
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt
new file mode 100644
index 0000000..81529b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.content.ComponentName
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
+import android.util.Log
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
+import com.android.systemui.settings.UserContextProvider
+import com.android.traceur.FileSender
+import com.android.traceur.MessageConstants
+import com.android.traceur.MessageConstants.TRACING_APP_ACTIVITY
+import com.android.traceur.MessageConstants.TRACING_APP_PACKAGE_NAME
+import com.android.traceur.TraceConfig
+import java.util.concurrent.CopyOnWriteArrayList
+import javax.inject.Inject
+
+private const val TAG = "TraceurConnection"
+
+class TraceurConnection
+private constructor(userContextProvider: UserContextProvider, private val bgLooper: Looper) :
+    UserAwareConnection(
+        userContextProvider,
+        Intent().setClassName(TRACING_APP_PACKAGE_NAME, TRACING_APP_ACTIVITY),
+    ) {
+
+    @SysUISingleton
+    class Provider
+    @Inject
+    constructor(
+        private val userContextProvider: UserContextProvider,
+        @Background private val bgLooper: Looper,
+    ) {
+        fun create() = TraceurConnection(userContextProvider, bgLooper)
+    }
+
+    val onBound: MutableList<Runnable> = CopyOnWriteArrayList(mutableListOf())
+
+    override fun onServiceConnected(className: ComponentName, service: IBinder) {
+        super.onServiceConnected(className, service)
+        onBound.forEach(Runnable::run)
+        onBound.clear()
+    }
+
+    @WorkerThread
+    fun startTracing(traceType: TraceConfig) {
+        val data =
+            Bundle().apply { putParcelable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) }
+        sendMessage(MessageConstants.START_WHAT, data)
+    }
+
+    @WorkerThread fun stopTracing() = sendMessage(MessageConstants.STOP_WHAT)
+
+    @WorkerThread
+    fun shareTraces(screenRecord: Uri?) {
+        val replyHandler = Messenger(ShareFilesHandler(screenRecord, userContextProvider, bgLooper))
+        sendMessage(MessageConstants.SHARE_WHAT, replyTo = replyHandler)
+    }
+
+    @WorkerThread
+    fun getTags(state: IssueRecordingState) =
+        sendMessage(MessageConstants.TAGS_WHAT, replyTo = Messenger(TagsHandler(bgLooper, state)))
+
+    @WorkerThread
+    private fun sendMessage(what: Int, data: Bundle = Bundle(), replyTo: Messenger? = null) =
+        try {
+            val msg =
+                Message.obtain().apply {
+                    this.what = what
+                    this.data = data
+                    this.replyTo = replyTo
+                }
+            binder?.send(msg) ?: onBound.add { binder!!.send(msg) }
+        } catch (e: Exception) {
+            Log.e(TAG, "failed to notify Traceur", e)
+        }
+}
+
+private class ShareFilesHandler(
+    private val screenRecord: Uri?,
+    private val userContextProvider: UserContextProvider,
+    looper: Looper,
+) : Handler(looper) {
+
+    override fun handleMessage(msg: Message) {
+        if (MessageConstants.SHARE_WHAT == msg.what) {
+            shareTraces(
+                msg.data.getParcelable(MessageConstants.EXTRA_PERFETTO, Uri::class.java),
+                msg.data.getParcelable(MessageConstants.EXTRA_WINSCOPE, Uri::class.java),
+            )
+        } else {
+            throw IllegalArgumentException("received unknown msg.what: " + msg.what)
+        }
+    }
+
+    private fun shareTraces(perfetto: Uri?, winscope: Uri?) {
+        val uris: ArrayList<Uri> =
+            ArrayList<Uri>().apply {
+                perfetto?.let { add(it) }
+                winscope?.let { add(it) }
+                screenRecord?.let { add(it) }
+            }
+        val fileSharingIntent =
+            FileSender.buildSendIntent(userContextProvider.userContext, uris)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
+        userContextProvider.userContext.startActivity(fileSharingIntent)
+    }
+}
+
+private class TagsHandler(looper: Looper, private val state: IssueRecordingState) :
+    Handler(looper) {
+
+    override fun handleMessage(msg: Message) {
+        if (MessageConstants.TAGS_WHAT == msg.what) {
+            val keys = msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAGS)
+            val values = msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAG_DESCRIPTIONS)
+            if (keys == null || values == null) {
+                throw IllegalArgumentException(
+                    "Neither keys: $keys, nor values: $values can be null"
+                )
+            }
+            state.tagTitles =
+                keys.zip(values).map { it.first + TAG_TITLE_DELIMITER + it.second }.toSet()
+        } else {
+            throw IllegalArgumentException("received unknown msg.what: " + msg.what)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
deleted file mode 100644
index 8bfd14a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recordissue
-
-import android.annotation.SuppressLint
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.ServiceConnection
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.os.Handler
-import android.os.IBinder
-import android.os.Looper
-import android.os.Message
-import android.os.Messenger
-import android.util.Log
-import androidx.annotation.WorkerThread
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
-import com.android.traceur.FileSender
-import com.android.traceur.MessageConstants
-import com.android.traceur.TraceConfig
-import javax.inject.Inject
-
-private const val TAG = "TraceurMessageSender"
-
-@SysUISingleton
-class TraceurMessageSender @Inject constructor(@Background private val backgroundLooper: Looper) {
-    private var binder: Messenger? = null
-    private var isBound: Boolean = false
-
-    val onBoundToTraceur = mutableListOf<Runnable>()
-
-    private val traceurConnection =
-        object : ServiceConnection {
-            override fun onServiceConnected(className: ComponentName, service: IBinder) {
-                binder = Messenger(service)
-                isBound = true
-                onBoundToTraceur.forEach(Runnable::run)
-                onBoundToTraceur.clear()
-            }
-
-            override fun onServiceDisconnected(className: ComponentName) {
-                binder = null
-                isBound = false
-            }
-        }
-
-    @SuppressLint("WrongConstant")
-    @WorkerThread
-    fun bindToTraceur(context: Context) {
-        if (isBound) {
-            // Binding needs to happen after the phone has been unlocked. The RecordIssueTile is
-            // initialized before this happens though, so binding is placed at a later time, during
-            // normal operations that can be repeated. This check avoids calling "bindService" 2x+
-            return
-        }
-        try {
-            val info =
-                context.packageManager.getPackageInfo(
-                    MessageConstants.TRACING_APP_PACKAGE_NAME,
-                    PackageManager.MATCH_SYSTEM_ONLY
-                )
-            val intent =
-                Intent().setClassName(info.packageName, MessageConstants.TRACING_APP_ACTIVITY)
-            val flags =
-                Context.BIND_AUTO_CREATE or
-                    Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE or
-                    Context.BIND_WAIVE_PRIORITY
-            context.bindService(intent, traceurConnection, flags)
-        } catch (e: Exception) {
-            Log.e(TAG, "failed to bind to Traceur's service", e)
-        }
-    }
-
-    @WorkerThread
-    fun unbindFromTraceur(context: Context) {
-        if (isBound) {
-            context.unbindService(traceurConnection)
-        }
-    }
-
-    @WorkerThread
-    fun startTracing(traceType: TraceConfig) {
-        val data =
-            Bundle().apply { putParcelable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) }
-        notifyTraceur(MessageConstants.START_WHAT, data)
-    }
-
-    @WorkerThread fun stopTracing() = notifyTraceur(MessageConstants.STOP_WHAT)
-
-    @WorkerThread
-    fun shareTraces(context: Context, screenRecord: Uri?) {
-        val replyHandler = Messenger(ShareFilesHandler(context, screenRecord, backgroundLooper))
-        notifyTraceur(MessageConstants.SHARE_WHAT, replyTo = replyHandler)
-    }
-
-    @WorkerThread
-    fun getTags(state: IssueRecordingState) {
-        val replyHandler = Messenger(TagsHandler(backgroundLooper, state))
-        notifyTraceur(MessageConstants.TAGS_WHAT, replyTo = replyHandler)
-    }
-
-    @WorkerThread
-    private fun notifyTraceur(what: Int, data: Bundle = Bundle(), replyTo: Messenger? = null) {
-        try {
-            binder!!.send(
-                Message.obtain().apply {
-                    this.what = what
-                    this.data = data
-                    this.replyTo = replyTo
-                }
-            )
-        } catch (e: Exception) {
-            Log.e(TAG, "failed to notify Traceur", e)
-        }
-    }
-
-    private class ShareFilesHandler(
-        private val context: Context,
-        private val screenRecord: Uri?,
-        looper: Looper,
-    ) : Handler(looper) {
-
-        override fun handleMessage(msg: Message) {
-            if (MessageConstants.SHARE_WHAT == msg.what) {
-                shareTraces(
-                    msg.data.getParcelable(MessageConstants.EXTRA_PERFETTO, Uri::class.java),
-                    msg.data.getParcelable(MessageConstants.EXTRA_WINSCOPE, Uri::class.java)
-                )
-            } else {
-                throw IllegalArgumentException("received unknown msg.what: " + msg.what)
-            }
-        }
-
-        private fun shareTraces(perfetto: Uri?, winscope: Uri?) {
-            val uris: List<Uri> =
-                mutableListOf<Uri>().apply {
-                    perfetto?.let { add(it) }
-                    winscope?.let { add(it) }
-                    screenRecord?.let { add(it) }
-                }
-            val fileSharingIntent =
-                FileSender.buildSendIntent(context, uris)
-                    .addFlags(
-                        Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
-                    )
-            context.startActivity(fileSharingIntent)
-        }
-    }
-
-    private class TagsHandler(looper: Looper, private val state: IssueRecordingState) :
-        Handler(looper) {
-
-        override fun handleMessage(msg: Message) {
-            if (MessageConstants.TAGS_WHAT == msg.what) {
-                val keys = msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAGS)
-                val values =
-                    msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAG_DESCRIPTIONS)
-                if (keys == null || values == null) {
-                    throw IllegalArgumentException(
-                        "Neither keys: $keys, nor values: $values can be null"
-                    )
-                }
-                state.tagTitles =
-                    keys.zip(values).map { it.first + TAG_TITLE_DELIMITER + it.second }.toSet()
-            } else {
-                throw IllegalArgumentException("received unknown msg.what: " + msg.what)
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
new file mode 100644
index 0000000..6aaa27d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.annotation.SuppressLint
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
+import android.os.Messenger
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.systemui.settings.UserContextProvider
+
+private const val TAG = "UserAwareConnection"
+private const val BIND_FLAGS =
+    Context.BIND_AUTO_CREATE or
+        Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE or
+        Context.BIND_WAIVE_PRIORITY
+
+/** ServiceConnection class that can be used to keep an IntentService alive. */
+open class UserAwareConnection(
+    protected val userContextProvider: UserContextProvider,
+    private val intent: Intent,
+) : ServiceConnection {
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) var binder: Messenger? = null
+    private var shouldUnBind = false
+
+    override fun onServiceConnected(className: ComponentName, service: IBinder) {
+        binder = Messenger(service)
+    }
+
+    override fun onServiceDisconnected(className: ComponentName) {
+        binder = null
+    }
+
+    @SuppressLint("WrongConstant")
+    @WorkerThread
+    fun doBind() {
+        if (shouldUnBind) {
+            // Binding needs to happen after the phone has been unlocked. The RecordIssueTile is
+            // initialized before this happens though, so binding is placed at a later time, during
+            // normal operations that can be repeated. This check avoids calling "bindService" 2x+
+            return
+        }
+        try {
+            shouldUnBind = userContextProvider.userContext.bindService(intent, this, BIND_FLAGS)
+        } catch (e: Exception) {
+            Log.e(TAG, "failed to bind to the service", e)
+        }
+    }
+
+    @WorkerThread
+    fun doUnBind() {
+        if (shouldUnBind) {
+            userContextProvider.userContext.unbindService(this)
+            shouldUnBind = false
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index 0806be8..a762d84 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -97,12 +97,13 @@
     private val window: ScreenshotWindow
     private val actionExecutor: ActionExecutor
     private val copyBroadcastReceiver: BroadcastReceiver
+    private val currentRequestCallbacks: MutableList<TakeScreenshotService.RequestCallback> =
+        mutableListOf()
 
     private var screenshotSoundController: ScreenshotSoundController? = null
     private var screenBitmap: Bitmap? = null
     private var screenshotTakenInPortrait = false
     private var screenshotAnimation: Animator? = null
-    private var currentRequestCallback: TakeScreenshotService.RequestCallback? = null
     private var packageName = ""
 
     /** Tracks config changes that require re-creating UI */
@@ -169,8 +170,8 @@
         requestCallback: TakeScreenshotService.RequestCallback,
     ) {
         Assert.isMainThread()
+        screenshotHandler.resetTimeout()
 
-        currentRequestCallback = requestCallback
         if (screenshot.type == TAKE_SCREENSHOT_FULLSCREEN && screenshot.bitmap == null) {
             val bounds = fullScreenRect
             screenshot.bitmap = imageCapture.captureDisplay(display.displayId, bounds)
@@ -181,7 +182,7 @@
         if (currentBitmap == null) {
             Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
             notificationController.notifyScreenshotError(R.string.screenshot_failed_to_capture_text)
-            currentRequestCallback?.reportError()
+            requestCallback.reportError()
             return
         }
 
@@ -194,8 +195,10 @@
             // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
             // and sharing shouldn't be exposed to the user.
             saveScreenshotAndToast(screenshot, finisher)
+            requestCallback.onFinish()
             return
         }
+        currentRequestCallbacks.add(requestCallback)
 
         broadcastSender.sendBroadcast(
             Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
@@ -495,8 +498,8 @@
         Log.d(TAG, "finishDismiss")
         actionsController.endScreenshotSession()
         scrollCaptureExecutor.close()
-        currentRequestCallback?.onFinish()
-        currentRequestCallback = null
+        currentRequestCallbacks.forEach { it.onFinish() }
+        currentRequestCallbacks.clear()
         viewProxy.reset()
         removeWindow()
         screenshotHandler.cancelTimeout()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b466bf0..b171e87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1160,11 +1160,13 @@
 
     @Override
     public void addHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mHeadsUpHeightChangedListeners.addIfAbsent(runnable);
     }
 
     @Override
     public void removeHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mHeadsUpHeightChangedListeners.remove(runnable);
     }
 
@@ -1240,11 +1242,13 @@
 
     @Override
     public void setScrolledToTop(boolean scrolledToTop) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mScrollViewFields.setScrolledToTop(scrolledToTop);
     }
 
     @Override
     public void setStackTop(float stackTop) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         if (mAmbientState.getStackTop() != stackTop) {
             mAmbientState.setStackTop(stackTop);
             onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
@@ -1253,51 +1257,54 @@
 
     @Override
     public void setStackCutoff(float stackCutoff) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mAmbientState.setStackCutoff(stackCutoff);
     }
 
     @Override
     public void setHeadsUpTop(float headsUpTop) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mAmbientState.setHeadsUpTop(headsUpTop);
         requestChildrenUpdate();
     }
 
     @Override
     public void setHeadsUpBottom(float headsUpBottom) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mAmbientState.setHeadsUpBottom(headsUpBottom);
         mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
     }
 
     @Override
     public void closeGutsOnSceneTouch() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mController.closeControlsDueToOutsideTouch();
     }
 
     @Override
     public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mScrollViewFields.setSyntheticScrollConsumer(consumer);
     }
 
     @Override
     public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer);
     }
 
     @Override
     public void setCurrentGestureInGutsConsumer(@Nullable Consumer<Boolean> consumer) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mScrollViewFields.setCurrentGestureInGutsConsumer(consumer);
     }
 
     @Override
     public void setRemoteInputRowBottomBoundConsumer(@Nullable Consumer<Float> consumer) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mScrollViewFields.setRemoteInputRowBottomBoundConsumer(consumer);
     }
 
-    @Override
-    public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
-        mScrollViewFields.setHeadsUpHeightConsumer(consumer);
-    }
-
     /**
      * @param listener to be notified after the location of Notification children might have
      *                 changed.
@@ -2621,11 +2628,13 @@
 
     @Override
     public int getTopHeadsUpHeight() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
         return getTopHeadsUpIntrinsicHeight();
     }
 
     @Override
     public int getHeadsUpInset() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
         return mHeadsUpInset;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index c08ed61..f6e8b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -86,9 +86,6 @@
     fun sendRemoteInputRowBottomBound(bottomY: Float?) =
         remoteInputRowBottomBoundConsumer?.accept(bottomY)
 
-    /** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
-    fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
-
     fun dump(pw: IndentingPrintWriter) {
         pw.printSection("StackViewStates") {
             pw.println("scrimClippingShape", scrimClippingShape)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index dbe81c1..6ad9f01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -77,9 +77,6 @@
     /** Set a consumer for current remote input notification row bottom bound events */
     fun setRemoteInputRowBottomBoundConsumer(consumer: Consumer<Float?>?)
 
-    /** Set a consumer for heads up height changed events */
-    fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
-
     /** sets that scrolling is allowed */
     fun setScrollingEnabled(enabled: Boolean)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt
deleted file mode 100644
index 4e5ecfe..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.policy.CallbackController
-import java.lang.ref.WeakReference
-import javax.inject.Inject
-
-/**
- * Publishes updates to the status bar's margins.
- *
- * While the status bar view consumes the entire width of the device, the status bar
- * contents are laid out with margins for rounded corners, padding from the absolute
- * edges, and potentially display cutouts in the corner.
- */
-@SysUISingleton
-class StatusBarLocationPublisher @Inject constructor()
-: CallbackController<StatusBarMarginUpdatedListener> {
-    private val listeners = mutableSetOf<WeakReference<StatusBarMarginUpdatedListener>>()
-
-    var marginLeft: Int = 0
-        private set
-    var marginRight: Int = 0
-        private set
-
-    override fun addCallback(listener: StatusBarMarginUpdatedListener) {
-        listeners.add(WeakReference(listener))
-    }
-
-    override fun removeCallback(listener: StatusBarMarginUpdatedListener) {
-        var toRemove: WeakReference<StatusBarMarginUpdatedListener>? = null
-        for (l in listeners) {
-            if (l.get() == listener) {
-                toRemove = l
-            }
-        }
-
-        if (toRemove != null) {
-            listeners.remove(toRemove)
-        }
-    }
-
-    fun updateStatusBarMargin(left: Int, right: Int) {
-        marginLeft = left
-        marginRight = right
-
-        notifyListeners()
-    }
-
-    private fun notifyListeners() {
-        var listenerList: List<WeakReference<StatusBarMarginUpdatedListener>>
-        synchronized(this) {
-            listenerList = listeners.toList()
-        }
-
-        listenerList.forEach { wrapper ->
-            if (wrapper.get() == null) {
-                listeners.remove(wrapper)
-            }
-
-            wrapper.get()?.onStatusBarMarginUpdated(marginLeft, marginRight)
-        }
-    }
-}
-
-interface StatusBarMarginUpdatedListener {
-    fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int)
-}
\ No newline at end of file
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 a8b4728..c258095 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
@@ -65,7 +65,6 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
 import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -140,7 +139,6 @@
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private final OngoingCallController mOngoingCallController;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
-    private final StatusBarLocationPublisher mLocationPublisher;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
     private final StatusBarIconController mStatusBarIconController;
     private final CarrierConfigTracker mCarrierConfigTracker;
@@ -243,7 +241,6 @@
             StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
-            StatusBarLocationPublisher locationPublisher,
             ShadeExpansionStateManager shadeExpansionStateManager,
             StatusBarIconController statusBarIconController,
             DarkIconManager.Factory darkIconManagerFactory,
@@ -267,7 +264,6 @@
         mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
-        mLocationPublisher = locationPublisher;
         mShadeExpansionStateManager = shadeExpansionStateManager;
         mStatusBarIconController = statusBarIconController;
         mCollapsedStatusBarViewModel = collapsedStatusBarViewModel;
@@ -349,9 +345,6 @@
         }
 
         mStatusBar = (PhoneStatusBarView) view;
-        View contents = mStatusBar.findViewById(R.id.status_bar_contents);
-        contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
-        updateStatusBarLocation(contents.getLeft(), contents.getRight());
         if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
             mStatusBar.restoreHierarchyState(
                     savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
@@ -977,13 +970,6 @@
         }, /*isAnimationRunning*/ false);
     }
 
-    private void updateStatusBarLocation(int left, int right) {
-        int leftMargin = left - mStatusBar.getLeft();
-        int rightMargin = mStatusBar.getRight() - right;
-
-        mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin);
-    }
-
     private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean selfChange) {
@@ -991,14 +977,6 @@
         }
     };
 
-    // Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
-    private View.OnLayoutChangeListener mStatusBarLayoutListener =
-            (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                if (left != oldLeft || right != oldRight) {
-                    updateStatusBarLocation(left, right);
-                }
-            };
-
     @Override
     public void dump(PrintWriter printWriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */"  ");
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index 6c92754..f7d6d90 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -38,7 +38,7 @@
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.update
 
-private val maxDialogShowTime: Duration = 3.seconds
+private val MAX_DIALOG_SHOW_TIME: Duration = 3.seconds
 
 /**
  * Handles Volume Dialog visibility state. It might change from several sources:
@@ -65,7 +65,7 @@
     init {
         merge(
                 mutableDismissDialogEvents.mapLatest {
-                    delay(maxDialogShowTime)
+                    delay(MAX_DIALOG_SHOW_TIME)
                     VolumeDialogEventModel.DismissRequested(Events.DISMISS_REASON_TIMEOUT)
                 },
                 callbacksInteractor.event,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
new file mode 100644
index 0000000..db19634
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.settings.domain
+
+import android.app.ActivityManager
+import com.android.app.tracing.coroutines.flow.map
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.domain.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.stateIn
+
+@VolumeDialogScope
+class VolumeDialogSettingsButtonInteractor
+@Inject
+constructor(
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+    private val deviceProvisionedController: DeviceProvisionedController,
+    private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
+    private val visibilityInteractor: VolumeDialogVisibilityInteractor,
+) {
+
+    val isVisible: StateFlow<Boolean> =
+        visibilityInteractor.dialogVisibility
+            .filterIsInstance(VolumeDialogVisibilityModel.Visible::class)
+            .map { model ->
+                deviceProvisionedController.isCurrentUserSetup() &&
+                    model.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE
+            }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, false)
+
+    fun onButtonClicked() {
+        volumePanelGlobalStateInteractor.setVisible(true)
+        visibilityInteractor.dismissDialog(Events.DISMISS_REASON_SETTINGS_CLICKED)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
new file mode 100644
index 0000000..ba08876
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.settings.ui.binder
+
+import android.view.View
+import com.android.systemui.lifecycle.WindowLifecycleState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
+
+@VolumeDialogScope
+class VolumeDialogSettingsButtonViewBinder
+@Inject
+constructor(private val viewModelFactory: VolumeDialogSettingsButtonViewModel.Factory) {
+
+    fun bind(view: View) {
+        with(view) {
+            val button = requireViewById<View>(R.id.settings)
+            repeatWhenAttached {
+                viewModel(
+                    traceName = "VolumeDialogViewBinder",
+                    minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                    factory = { viewModelFactory.create() },
+                ) { viewModel ->
+                    setSnapshotBinding {
+                        visibility = if (viewModel.isVisible) View.VISIBLE else View.GONE
+                        button.setOnClickListener { viewModel.onButtonClicked() }
+                    }
+
+                    awaitCancellation()
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
new file mode 100644
index 0000000..2acc33b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -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.systemui.volume.dialog.settings.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class VolumeDialogSettingsButtonViewModel
+@AssistedInject
+constructor(private val interactor: VolumeDialogSettingsButtonInteractor) : ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("VolumeDialog_settings_button")
+
+    val isVisible by hydrator.hydratedStateOf("isVisible", interactor.isVisible)
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    fun onButtonClicked() {
+        interactor.onButtonClicked()
+    }
+
+    @VolumeDialogScope
+    @AssistedFactory
+    interface Factory {
+
+        fun create(): VolumeDialogSettingsButtonViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
index 3f2c39b..9c88303 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
@@ -20,21 +20,18 @@
 import android.graphics.Color
 import android.graphics.PixelFormat
 import android.graphics.drawable.ColorDrawable
-import android.view.View
 import android.view.ViewGroup
 import android.view.Window
 import android.view.WindowManager
-import androidx.lifecycle.lifecycleScope
-import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
 import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
 
 @VolumeDialogScope
 class VolumeDialogBinder
@@ -42,6 +39,7 @@
 constructor(
     @VolumeDialog private val coroutineScope: CoroutineScope,
     private val volumeDialogViewBinder: VolumeDialogViewBinder,
+    private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
     private val gravityViewModel: VolumeDialogGravityViewModel,
 ) {
 
@@ -50,10 +48,8 @@
             setupWindow(window!!)
             dialog.setContentView(R.layout.volume_dialog)
 
-            val volumeDialogView: View = dialog.requireViewById(R.id.volume_dialog_container)
-            volumeDialogView.repeatWhenAttached {
-                lifecycleScope.launch { volumeDialogViewBinder.bind(volumeDialogView) }
-            }
+            settingsButtonViewBinder.bind(dialog.requireViewById(R.id.settings_container))
+            volumeDialogViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_container))
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 700225d..600d176 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -21,15 +21,17 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
 import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.awaitCancellation
 
+@VolumeDialogScope
 class VolumeDialogViewBinder
 @Inject
 constructor(private val volumeDialogViewModelFactory: VolumeDialogViewModel.Factory) {
 
-    suspend fun bind(view: View) {
+    fun bind(view: View) {
         view.repeatWhenAttached {
             view.viewModel(
                 traceName = "VolumeDialogViewBinder",
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index 329a947..8aa0d09 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -52,7 +52,7 @@
                 .mapLatest { visibilityModel ->
                     with(visibilityModel) {
                         if (this is VolumeDialogVisibilityModel.Visible) {
-                            showDialog(reason, keyguardLocked, lockTaskModeState)
+                            showDialog(reason, keyguardLocked)
                         }
                         if (this is VolumeDialogVisibilityModel.Dismissed) {
                             Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
@@ -65,24 +65,23 @@
         awaitCancellation()
     }
 
-    suspend fun showDialog(reason: Int, keyguardLocked: Boolean, lockTaskModeState: Int): Unit =
-        coroutineScope {
-            logger.onShow(reason)
+    suspend fun showDialog(reason: Int, keyguardLocked: Boolean): Unit = coroutineScope {
+        logger.onShow(reason)
 
-            controller.notifyVisible(true)
+        controller.notifyVisible(true)
 
-            val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
-            val dialog =
-                volumeDialogComponent.volumeDialog().apply {
-                    setOnDismissListener {
-                        volumeDialogComponent.coroutineScope().cancel()
-                        dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
-                    }
+        val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
+        val dialog =
+            volumeDialogComponent.volumeDialog().apply {
+                setOnDismissListener {
+                    volumeDialogComponent.coroutineScope().cancel()
+                    dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
                 }
-            launch { dialog.awaitShow() }
+            }
+        launch { dialog.awaitShow() }
 
-            Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
-        }
+        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
+    }
 }
 
 /** Shows [Dialog] until suspend function is cancelled. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index ca518f9..c7da03d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles
 
 import android.os.Handler
+import android.os.Looper
 import android.service.quicksettings.Tile
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,9 +32,10 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.recordissue.IssueRecordingServiceConnection
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.recordissue.RecordIssueDialogDelegate
-import com.android.systemui.recordissue.TraceurMessageSender
+import com.android.systemui.recordissue.TraceurConnection
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.settings.UserContextProvider
@@ -75,13 +77,14 @@
     @Mock private lateinit var panelInteractor: PanelInteractor
     @Mock private lateinit var userContextProvider: UserContextProvider
     @Mock private lateinit var issueRecordingState: IssueRecordingState
-    @Mock private lateinit var traceurMessageSender: TraceurMessageSender
     @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
     @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
     @Mock private lateinit var dialog: SystemUIDialog
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: RecordIssueTile
+    private lateinit var irsConnProvider: IssueRecordingServiceConnection.Provider
+    private lateinit var traceurConnProvider: TraceurConnection.Provider
 
     @Before
     fun setUp() {
@@ -90,6 +93,10 @@
         whenever(delegateFactory.create(any())).thenReturn(dialogDelegate)
         whenever(dialogDelegate.createDialog()).thenReturn(dialog)
 
+        irsConnProvider = IssueRecordingServiceConnection.Provider(userContextProvider)
+        traceurConnProvider =
+            TraceurConnection.Provider(userContextProvider, Looper.getMainLooper())
+
         testableLooper = TestableLooper.get(this)
         tile =
             RecordIssueTile(
@@ -107,7 +114,8 @@
                 dialogLauncherAnimator,
                 panelInteractor,
                 userContextProvider,
-                traceurMessageSender,
+                irsConnProvider,
+                traceurConnProvider,
                 Executors.newSingleThreadExecutor(),
                 issueRecordingState,
                 delegateFactory,
@@ -169,7 +177,7 @@
             .executeWhenUnlocked(
                 isA(ActivityStarter.OnDismissAction::class.java),
                 eq(false),
-                eq(true)
+                eq(true),
             )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index eb1bcc7..d717fe4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -48,7 +48,7 @@
     private val kosmos =
         testKosmos().apply {
             fakeKeyguardTransitionRepository =
-                FakeKeyguardTransitionRepository(initInLockscreen = false)
+                FakeKeyguardTransitionRepository(initInLockscreen = false, testScope = testScope)
         }
     private val testScope = kosmos.testScope
     private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
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 63a560f..e57e8d1 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
@@ -66,7 +66,6 @@
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
-import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ui.DarkIconManager;
@@ -98,7 +97,6 @@
     private ShadeExpansionStateManager mShadeExpansionStateManager;
     private OngoingCallController mOngoingCallController;
     private SystemStatusAnimationScheduler mAnimationScheduler;
-    private StatusBarLocationPublisher mLocationPublisher;
     // Set in instantiate()
     private StatusBarIconController mStatusBarIconController;
     private KeyguardStateController mKeyguardStateController;
@@ -1181,7 +1179,6 @@
         setUpDaggerComponent();
         mOngoingCallController = mock(OngoingCallController.class);
         mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
-        mLocationPublisher = mock(StatusBarLocationPublisher.class);
         mStatusBarIconController = mock(StatusBarIconController.class);
         mStatusBarStateController = mock(StatusBarStateController.class);
         mKeyguardStateController = mock(KeyguardStateController.class);
@@ -1200,7 +1197,6 @@
                 mStatusBarFragmentComponentFactory,
                 mOngoingCallController,
                 mAnimationScheduler,
-                mLocationPublisher,
                 mShadeExpansionStateManager,
                 mStatusBarIconController,
                 mIconManagerFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 1302faa..62d221d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -6,6 +6,7 @@
 import android.content.pm.ApplicationInfo
 import android.graphics.Bitmap
 import android.os.UserHandle
+import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import kotlinx.coroutines.CoroutineScope
@@ -23,6 +24,10 @@
 
     private var nextWidgetId = 1
 
+    private fun updateListFromDatabase() {
+        _communalWidgets.value = fakeDatabase.values.sortedWith(compareBy { it.rank }).toList()
+    }
+
     fun setCommunalWidgets(inventory: List<CommunalWidgetContentModel>) {
         _communalWidgets.value = inventory
     }
@@ -49,6 +54,7 @@
         rank: Int = 0,
         category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
         userId: Int = 0,
+        spanY: Int = CommunalContentSize.HALF.span,
     ) {
         fakeDatabase[appWidgetId] =
             CommunalWidgetContentModel.Available(
@@ -66,8 +72,9 @@
                                     }
                             }
                     },
+                spanY = spanY,
             )
-        _communalWidgets.value = fakeDatabase.values.toList()
+        updateListFromDatabase()
     }
 
     fun addPendingWidget(
@@ -84,8 +91,9 @@
                 componentName = ComponentName.unflattenFromString(componentName)!!,
                 icon = icon,
                 user = UserHandle(userId),
+                spanY = CommunalContentSize.HALF.span,
             )
-        _communalWidgets.value = fakeDatabase.values.toList()
+        updateListFromDatabase()
     }
 
     override fun deleteWidget(widgetId: Int) {
@@ -93,28 +101,54 @@
         _communalWidgets.value = fakeDatabase.values.toList()
     }
 
-    override fun updateWidgetSpanY(widgetId: Int, spanY: Int) {
-        coroutineScope.launch {
-            fakeDatabase[widgetId]?.let { widget ->
+    private fun reorderDatabase(widgetIdToRankMap: Map<Int, Int>) {
+        for ((id, rank) in widgetIdToRankMap) {
+            val widget = fakeDatabase[id] ?: continue
+            fakeDatabase[id] =
                 when (widget) {
                     is CommunalWidgetContentModel.Available -> {
-                        fakeDatabase[widgetId] = widget.copy(spanY = spanY)
+                        widget.copy(rank = rank)
                     }
                     is CommunalWidgetContentModel.Pending -> {
-                        fakeDatabase[widgetId] = widget.copy(spanY = spanY)
+                        widget.copy(rank = rank)
                     }
                 }
-                _communalWidgets.value = fakeDatabase.values.toList()
-            }
         }
     }
 
+    override fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {
+        reorderDatabase(widgetIdToRankMap)
+        updateListFromDatabase()
+    }
+
+    override fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+        val widget = fakeDatabase[appWidgetId] ?: return
+
+        fakeDatabase[appWidgetId] =
+            when (widget) {
+                is CommunalWidgetContentModel.Available -> {
+                    widget.copy(spanY = spanY)
+                }
+                is CommunalWidgetContentModel.Pending -> {
+                    widget.copy(spanY = spanY)
+                }
+            }
+        reorderDatabase(widgetIdToRankMap)
+        updateListFromDatabase()
+    }
+
     override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {}
 
     override fun abortRestoreWidgets() {}
 
     private fun onConfigured(id: Int, providerInfo: AppWidgetProviderInfo, rank: Int) {
-        _communalWidgets.value +=
-            listOf(CommunalWidgetContentModel.Available(id, providerInfo, rank))
+        fakeDatabase[id] =
+            CommunalWidgetContentModel.Available(
+                appWidgetId = id,
+                providerInfo = providerInfo,
+                rank = rank,
+                spanY = CommunalContentSize.HALF.span,
+            )
+        updateListFromDatabase()
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 4d0e603..70b4f79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -48,13 +48,35 @@
  * with OFF -> GONE. Construct with initInLockscreen = false if your test requires this behavior.
  */
 @SysUISingleton
-class FakeKeyguardTransitionRepository(private val initInLockscreen: Boolean = true) :
-    KeyguardTransitionRepository {
+class FakeKeyguardTransitionRepository(
+    private val initInLockscreen: Boolean = true,
+
+    /**
+     * If true, calls to [startTransition] will automatically emit STARTED, RUNNING, and FINISHED
+     * transition steps from/to the given states.
+     *
+     * [startTransition] is what the From*TransitionInteractors call, so this more closely emulates
+     * the behavior of the real KeyguardTransitionRepository, and reduces the work needed to
+     * manually set up the repository state in each test. For example, setting dreaming=true will
+     * automatically cause FromDreamingTransitionInteractor to call startTransition(DREAMING), and
+     * then we'll send STARTED/RUNNING/FINISHED DREAMING TransitionSteps.
+     *
+     * If your test needs to make assertions at specific points between STARTED/FINISHED, or if it's
+     * difficult to set up all of the conditions to make the transition interactors actually call
+     * startTransition, then construct a FakeKeyguardTransitionRepository with this value false.
+     */
+    private val sendTransitionStepsOnStartTransition: Boolean = true,
+    private val testScope: TestScope,
+) : KeyguardTransitionRepository {
+
     private val _transitions =
         MutableSharedFlow<TransitionStep>(replay = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
     override val transitions: SharedFlow<TransitionStep> = _transitions
 
-    @Inject constructor() : this(initInLockscreen = true)
+    @Inject
+    constructor(
+        testScope: TestScope
+    ) : this(initInLockscreen = true, sendTransitionStepsOnStartTransition = true, testScope)
 
     private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
         MutableStateFlow(
@@ -287,6 +309,11 @@
 
     override suspend fun startTransition(info: TransitionInfo): UUID? {
         _currentTransitionInfo.value = info
+
+        if (sendTransitionStepsOnStartTransition) {
+            sendTransitionSteps(from = info.from, to = info.to, testScope = testScope)
+        }
+
         return if (info.animator == null) UUID.randomUUID() else null
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
index 3e69e87..e9eea83 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
@@ -18,9 +18,14 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import org.mockito.Mockito.spy
 
 var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by
     Kosmos.Fixture { fakeKeyguardTransitionRepository }
-var Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() }
+var Kosmos.fakeKeyguardTransitionRepository by
+    Kosmos.Fixture { FakeKeyguardTransitionRepository(testScope = testScope) }
+var Kosmos.fakeKeyguardTransitionRepositorySpy: FakeKeyguardTransitionRepository by
+    Kosmos.Fixture { spy(fakeKeyguardTransitionRepository) }
 var Kosmos.realKeyguardTransitionRepository: KeyguardTransitionRepository by
     Kosmos.Fixture { KeyguardTransitionRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index ef789d1..93a59eb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
@@ -27,7 +27,7 @@
 val Kosmos.fromAodTransitionInteractor by
     Kosmos.Fixture {
         FromAodTransitionInteractor(
-            transitionRepository = fakeKeyguardTransitionRepository,
+            transitionRepository = keyguardTransitionRepository,
             transitionInteractor = keyguardTransitionInteractor,
             internalTransitionInteractor = internalKeyguardTransitionInteractor,
             scope = applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index c694114..700d7e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -18,8 +18,8 @@
 
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
@@ -29,7 +29,7 @@
 val Kosmos.fromGoneTransitionInteractor by
     Kosmos.Fixture {
         FromGoneTransitionInteractor(
-            transitionRepository = fakeKeyguardTransitionRepository,
+            transitionRepository = keyguardTransitionRepository,
             transitionInteractor = keyguardTransitionInteractor,
             internalTransitionInteractor = internalKeyguardTransitionInteractor,
             scope = applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
new file mode 100644
index 0000000..fc4f3a5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.deviceEntryBackgroundViewModel by Fixture {
+    DeviceEntryBackgroundViewModel(
+        context = applicationContext,
+        deviceEntryIconViewModel = deviceEntryIconViewModel,
+        configurationInteractor = configurationInteractor,
+        keyguardTransitionInteractor = keyguardTransitionInteractor,
+        alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
+        alternateBouncerToDozingTransitionViewModel = alternateBouncerToDozingTransitionViewModel,
+        aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+        dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+        dreamingToAodTransitionViewModel = dreamingToAodTransitionViewModel,
+        dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
+        goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+        goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+        goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
+        lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
+        occludedToAodTransitionViewModel = occludedToAodTransitionViewModel,
+        occludedToDozingTransitionViewModel = occludedToDozingTransitionViewModel,
+        occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+        offToLockscreenTransitionViewModel = offToLockscreenTransitionViewModel,
+        primaryBouncerToAodTransitionViewModel = primaryBouncerToAodTransitionViewModel,
+        primaryBouncerToDozingTransitionViewModel = primaryBouncerToDozingTransitionViewModel,
+        primaryBouncerToLockscreenTransitionViewModel =
+            primaryBouncerToLockscreenTransitionViewModel,
+        lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
+    )
+}
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 81ae717..3bcca1c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -99,6 +99,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
@@ -203,6 +204,7 @@
     private IVirtualDeviceSoundEffectListener mSoundEffectListener;
     private final DisplayManagerGlobal mDisplayManager;
     private final DisplayManagerInternal mDisplayManagerInternal;
+    private final PowerManager mPowerManager;
     @GuardedBy("mVirtualDeviceLock")
     private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
     @NonNull
@@ -213,6 +215,10 @@
     @GuardedBy("mVirtualDeviceLock")
     @Nullable
     private LocaleList mLocaleList = null;
+    @GuardedBy("mVirtualDeviceLock")
+    private boolean mLockdownActive = false;
+    @GuardedBy("mVirtualDeviceLock")
+    private boolean mRequestedToBeAwake = true;
 
     @NonNull
     private final VirtualDevice mPublicVirtualDeviceObject;
@@ -418,6 +424,7 @@
         mDevicePolicies = params.getDevicePolicies();
         mDisplayManager = displayManager;
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+        mPowerManager = context.getSystemService(PowerManager.class);
         if (inputController == null) {
             mInputController = new InputController(
                     context.getMainThreadHandler(),
@@ -475,6 +482,20 @@
         }
     }
 
+    void onLockdownChanged(boolean lockdownActive) {
+        synchronized (mVirtualDeviceLock) {
+            if (lockdownActive != mLockdownActive) {
+                mLockdownActive = lockdownActive;
+                if (mLockdownActive) {
+                    goToSleepInternal(PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF);
+                } else if (mRequestedToBeAwake) {
+                    wakeUpInternal(PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
+                            "android.server.companion.virtual:LOCKDOWN_ENDED");
+                }
+            }
+        }
+    }
+
     @VisibleForTesting
     SensorController getSensorControllerForTest() {
         return mSensorController;
@@ -576,8 +597,45 @@
     }
 
     @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void goToSleep() {
+        super.goToSleep_enforcePermission();
+        synchronized (mVirtualDeviceLock) {
+            mRequestedToBeAwake = false;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            goToSleepInternal(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void wakeUp() {
+        super.wakeUp_enforcePermission();
+        synchronized (mVirtualDeviceLock) {
+            mRequestedToBeAwake = true;
+            if (mLockdownActive) {
+                Slog.w(TAG, "Cannot wake up device during lockdown.");
+                return;
+            }
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            wakeUpInternal(PowerManager.WAKE_REASON_POWER_BUTTON,
+                    "android.server.companion.virtual:DEVICE_ON");
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
             ResultReceiver resultReceiver) {
+        super.launchPendingIntent_enforcePermission();
         Objects.requireNonNull(pendingIntent);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(displayId)) {
@@ -1042,7 +1100,9 @@
     }
 
     @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public int getInputDeviceId(IBinder token) {
+        super.getInputDeviceId_enforcePermission();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.getInputDeviceId(token);
@@ -1125,7 +1185,9 @@
     }
 
     @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public PointF getCursorPosition(IBinder token) {
+        super.getCursorPosition_enforcePermission();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.getCursorPosition(token);
@@ -1411,18 +1473,24 @@
         return gwpc;
     }
 
-    int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
-            @NonNull IVirtualDisplayCallback callback, String packageName) {
+    @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
+            @NonNull IVirtualDisplayCallback callback) {
+        super.createVirtualDisplay_enforcePermission();
         GenericWindowPolicyController gwpc;
         synchronized (mVirtualDeviceLock) {
             gwpc = createWindowPolicyControllerLocked(virtualDisplayConfig.getDisplayCategories());
         }
         int displayId;
         displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, callback,
-                this, gwpc, packageName);
+                this, gwpc, mOwnerPackageName);
         boolean isMirrorDisplay =
                 mDisplayManagerInternal.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY;
         gwpc.setDisplayId(displayId, isMirrorDisplay);
+        boolean isTrustedDisplay =
+                (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
+                        == Display.FLAG_TRUSTED;
 
         boolean showPointer;
         synchronized (mVirtualDeviceLock) {
@@ -1433,7 +1501,8 @@
             }
 
             PowerManager.WakeLock wakeLock = createAndAcquireWakeLockForDisplay(displayId);
-            mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock));
+            mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock,
+                    isTrustedDisplay, isMirrorDisplay));
             showPointer = mDefaultShowPointerIcon;
         }
 
@@ -1444,8 +1513,7 @@
             mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
                     displayId);
             // WM throws a SecurityException if the display is untrusted.
-            if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
-                    == Display.FLAG_TRUSTED) {
+            if (isTrustedDisplay) {
                 mInputController.setDisplayImePolicy(displayId,
                         WindowManager.DISPLAY_IME_POLICY_LOCAL);
             }
@@ -1511,7 +1579,6 @@
         return result;
     }
 
-
     void onVirtualDisplayRemoved(int displayId) {
         /* This is callback invoked by VirtualDeviceManagerService when VirtualDisplay was released
          * by DisplayManager (most probably caused by someone calling VirtualDisplay.close()).
@@ -1562,6 +1629,34 @@
         }
     }
 
+    void goToSleepInternal(@PowerManager.GoToSleepReason int reason) {
+        final long now = SystemClock.uptimeMillis();
+        synchronized (mVirtualDeviceLock) {
+            for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+                if (!wrapper.isTrusted() || wrapper.isMirror()) {
+                    continue;
+                }
+                int displayId = mVirtualDisplays.keyAt(i);
+                mPowerManager.goToSleep(displayId, now, reason, /* flags= */ 0);
+            }
+        }
+    }
+
+    void wakeUpInternal(@PowerManager.WakeReason int reason, String details) {
+        final long now = SystemClock.uptimeMillis();
+        synchronized (mVirtualDeviceLock) {
+            for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+                if (!wrapper.isTrusted() || wrapper.isMirror()) {
+                    continue;
+                }
+                int displayId = mVirtualDisplays.keyAt(i);
+                mPowerManager.wakeUp(now, reason, details, displayId);
+            }
+        }
+    }
+
     /**
      * Release resources tied to virtual display owned by this VirtualDevice instance.
      *
@@ -1665,6 +1760,7 @@
         return mInputController.getInputDeviceDescriptors().values().stream().anyMatch(
                 inputDeviceDescriptor -> inputDeviceDescriptor.getInputDeviceId() == inputDeviceId);
     }
+
     void playSoundEffect(int effectType) {
         try {
             mSoundEffectListener.onPlaySoundEffect(effectType);
@@ -1732,13 +1828,17 @@
         private final IVirtualDisplayCallback mToken;
         private final GenericWindowPolicyController mWindowPolicyController;
         private final PowerManager.WakeLock mWakeLock;
+        private final boolean mIsTrusted;
+        private final boolean mIsMirror;
 
         VirtualDisplayWrapper(@NonNull IVirtualDisplayCallback token,
                 @NonNull GenericWindowPolicyController windowPolicyController,
-                @NonNull PowerManager.WakeLock wakeLock) {
+                @NonNull PowerManager.WakeLock wakeLock, boolean isTrusted, boolean isMirror) {
             mToken = Objects.requireNonNull(token);
             mWindowPolicyController = Objects.requireNonNull(windowPolicyController);
             mWakeLock = Objects.requireNonNull(wakeLock);
+            mIsTrusted = isTrusted;
+            mIsMirror = isMirror;
         }
 
         GenericWindowPolicyController getWindowPolicyController() {
@@ -1749,6 +1849,14 @@
             return mWakeLock;
         }
 
+        boolean isTrusted() {
+            return mIsTrusted;
+        }
+
+        boolean isMirror() {
+            return mIsMirror;
+        }
+
         IVirtualDisplayCallback getToken() {
             return mToken;
         }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 3cd1ca4..41b6a85 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -44,8 +44,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.IVirtualDisplayCallback;
-import android.hardware.display.VirtualDisplayConfig;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -68,6 +66,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.modules.expresslog.Counter;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -129,6 +128,26 @@
                 }
             };
 
+    private class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+        final Set<Integer> mUsersInLockdown = new ArraySet<>();
+
+        StrongAuthTracker(Context context) {
+            super(context);
+        }
+
+        @Override
+        public synchronized void onStrongAuthRequiredChanged(int userId) {
+            if ((getStrongAuthForUser(userId) & STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN) > 0) {
+                if (mUsersInLockdown.add(userId) && mUsersInLockdown.size() == 1) {
+                    onLockdownChanged(true);
+                }
+            } else if (mUsersInLockdown.remove(userId) && mUsersInLockdown.isEmpty()) {
+                onLockdownChanged(false);
+            }
+        }
+    }
+    private StrongAuthTracker mStrongAuthTracker;
+
     private final RemoteCallbackList<IVirtualDeviceListener> mVirtualDeviceListeners =
             new RemoteCallbackList<>();
 
@@ -201,6 +220,20 @@
                         + " will be available.");
             }
         }
+        if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()) {
+            mStrongAuthTracker = new StrongAuthTracker(getContext());
+            new LockPatternUtils(getContext()).registerStrongAuthTracker(mStrongAuthTracker);
+        }
+    }
+
+    // Called when the global lockdown state changes, i.e. lockdown is considered active if any user
+    // is in lockdown mode, and inactive if no users are in lockdown mode.
+    void onLockdownChanged(boolean lockdownActive) {
+        synchronized (mVirtualDeviceManagerLock) {
+            for (int i = 0; i < mVirtualDevices.size(); i++) {
+                mVirtualDevices.valueAt(i).onLockdownChanged(lockdownActive);
+            }
+        }
     }
 
     void onCameraAccessBlocked(int appUid) {
@@ -505,37 +538,6 @@
         }
 
         @Override // Binder call
-        public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
-                IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)
-                throws RemoteException {
-            Objects.requireNonNull(virtualDisplayConfig);
-            final int callingUid = getCallingUid();
-            if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
-                throw new SecurityException(
-                        "Package name " + packageName + " does not belong to calling uid "
-                                + callingUid);
-            }
-            VirtualDeviceImpl virtualDeviceImpl;
-            synchronized (mVirtualDeviceManagerLock) {
-                virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId());
-                if (virtualDeviceImpl == null) {
-                    throw new SecurityException(
-                            "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId()
-                                    + ")");
-                }
-            }
-            if (virtualDeviceImpl.getOwnerUid() != callingUid) {
-                throw new SecurityException(
-                        "uid " + callingUid
-                                + " is not the owner of the supplied VirtualDevice (deviceId = "
-                                + virtualDevice.getDeviceId() + ")");
-            }
-
-            return virtualDeviceImpl.createVirtualDisplay(
-                    virtualDisplayConfig, callback, packageName);
-        }
-
-        @Override // Binder call
         public List<VirtualDevice> getVirtualDevices() {
             List<VirtualDevice> virtualDevices = new ArrayList<>();
             synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 7daf158..f8857d3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -2064,12 +2064,14 @@
 
     private void updateComputedNightModeLocked(boolean activate) {
         boolean newComputedValue = activate;
+        boolean appliedOverrides = false;
         if (mNightMode.get() != MODE_NIGHT_YES && mNightMode.get() != UiModeManager.MODE_NIGHT_NO) {
             if (mOverrideNightModeOn && !newComputedValue) {
                 newComputedValue = true;
             } else if (mOverrideNightModeOff && newComputedValue) {
                 newComputedValue = false;
             }
+            appliedOverrides = true;
         }
 
         if (modesApi()) {
@@ -2079,8 +2081,10 @@
                 case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY) -> false;
                 default -> newComputedValue; // case OFF
             };
-        } else {
-            mComputedNightMode = newComputedValue;
+        }
+
+        if (appliedOverrides) {
+            return;
         }
 
         if (mNightMode.get() != MODE_NIGHT_AUTO || (mTwilightManager != null
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9219cc12..3c95b9a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -130,6 +130,7 @@
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
+import static android.security.Flags.preventIntentRedirect;
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -420,6 +421,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.BootReceiver;
 import com.android.server.DeviceIdleInternal;
@@ -2420,6 +2422,7 @@
         mBroadcastController = new BroadcastController(mContext, this, mBroadcastQueue);
         mComponentAliasResolver = new ComponentAliasResolver(this);
         mApplicationSharedMemoryReadOnlyFd = null;
+        sCreatorTokenCacheCleaner = new Handler(mHandlerThread.getLooper());
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2526,6 +2529,7 @@
         mPendingStartActivityUids = new PendingStartActivityUids();
         mTraceErrorLogger = new TraceErrorLogger();
         mComponentAliasResolver = new ComponentAliasResolver(this);
+        sCreatorTokenCacheCleaner = new Handler(mHandlerThread.getLooper());
         try {
             mApplicationSharedMemoryReadOnlyFd =
                     ApplicationSharedMemory.getInstance().getReadOnlyFileDescriptor();
@@ -5532,6 +5536,7 @@
     public int sendIntentSender(IApplicationThread caller, IIntentSender target,
             IBinder allowlistToken, int code, Intent intent, String resolvedType,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+        addCreatorToken(intent);
         if (target instanceof PendingIntentRecord) {
             final PendingIntentRecord originalRecord = (PendingIntentRecord) target;
 
@@ -13610,6 +13615,7 @@
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
+        addCreatorToken(service);
         if (service != null) {
             // Refuse possible leaked file descriptors
             if (service.hasFileDescriptors()) {
@@ -13871,6 +13877,7 @@
 
         validateServiceInstanceName(instanceName);
 
+        addCreatorToken(service);
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                 final ComponentName cn = service.getComponent();
@@ -17148,6 +17155,7 @@
                 Slog.v(TAG_SERVICE,
                         "startServiceInPackage: " + service + " type=" + resolvedType);
             }
+            addCreatorToken(service);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res;
             try {
@@ -17973,6 +17981,11 @@
                         userId, reason, exitInfoReason);
             }
         }
+
+        @Override
+        public void addCreatorToken(Intent intent) {
+            ActivityManagerService.this.addCreatorToken(intent);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
@@ -19114,6 +19127,7 @@
     private static final Map<IntentCreatorToken.Key, WeakReference<IntentCreatorToken>>
             sIntentCreatorTokenCache = new ConcurrentHashMap<>();
 
+    private static Handler sCreatorTokenCacheCleaner;
     /**
      * A binder token used to keep track of which app created the intent. This token can be used to
      * defend against intent redirect attacks. It stores uid of the intent creator and key fields of
@@ -19121,13 +19135,16 @@
      *
      * @hide
      */
+    @VisibleForTesting
     public static final class IntentCreatorToken extends Binder {
         @NonNull
         private final Key mKeyFields;
+        private final WeakReference<IntentCreatorToken> mRef;
 
         public IntentCreatorToken(int creatorUid, Intent intent) {
             super();
             this.mKeyFields = new Key(creatorUid, intent);
+            mRef = new WeakReference<>(this);
         }
 
         public int getCreatorUid() {
@@ -19145,6 +19162,26 @@
                     new Key(token.mKeyFields.mCreatorUid, intent));
         }
 
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                sCreatorTokenCacheCleaner.sendMessage(PooledLambda.obtainMessage(
+                        IntentCreatorToken::completeFinalize, this));
+            } finally {
+                super.finalize();
+            }
+        }
+
+        private void completeFinalize() {
+            synchronized (sIntentCreatorTokenCache) {
+                WeakReference<IntentCreatorToken> current = sIntentCreatorTokenCache.get(
+                        mKeyFields);
+                if (current == mRef) {
+                    sIntentCreatorTokenCache.remove(mKeyFields);
+                }
+            }
+        }
+
         private static class Key {
             private Key(int creatorUid, Intent intent) {
                 this.mCreatorUid = creatorUid;
@@ -19187,9 +19224,57 @@
             @Override
             public int hashCode() {
                 return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent,
-                        mFlags,
-                        mClipDataUris);
+                        mFlags, mClipDataUris);
             }
         }
     }
+
+    /**
+     * Add a creator token for all embedded intents (stored as extra) of the given intent.
+     *
+     * @param intent The given intent
+     * @hide
+     */
+    public void addCreatorToken(@Nullable Intent intent) {
+        if (!preventIntentRedirect()) return;
+
+        if (intent == null || intent.getExtraIntentKeys() == null) return;
+        for (String key : intent.getExtraIntentKeys()) {
+            try {
+                Intent extraIntent = intent.getParcelableExtra(key, Intent.class);
+                if (extraIntent == null) {
+                    Slog.w(TAG, "The key {" + key
+                            + "} does not correspond to an intent in the extra bundle.");
+                    continue;
+                }
+                Slog.wtf(TAG, "A creator token is added to an intent.");
+                IBinder creatorToken = createIntentCreatorToken(extraIntent);
+                if (creatorToken != null) {
+                    extraIntent.setCreatorToken(creatorToken);
+                }
+            } catch (Exception e) {
+                Slog.wtf(TAG,
+                        "Something went wrong when trying to add creator token for embedded "
+                                + "intents of intent: ."
+                                + intent, e);
+            }
+        }
+    }
+
+    private IBinder createIntentCreatorToken(Intent intent) {
+        if (IntentCreatorToken.isValid(intent)) return null;
+        int creatorUid = getCallingUid();
+        IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, intent);
+        IntentCreatorToken token;
+        synchronized (sIntentCreatorTokenCache) {
+            WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key);
+            if (ref == null || ref.get() == null) {
+                token = new IntentCreatorToken(creatorUid, intent);
+                sIntentCreatorTokenCache.put(key, token.mRef);
+            } else {
+                token = ref.get();
+            }
+        }
+        return token;
+    }
 }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index bae9a67..a815f72 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -138,6 +138,7 @@
     // The list is sorted.
     @VisibleForTesting
     static final String[] sDeviceConfigAconfigScopes = new String[] {
+        "aaos_sdv",
         "accessibility",
         "android_core_networking",
         "android_health_services",
@@ -150,6 +151,7 @@
         "art_performance",
         "attack_tools",
         "avic",
+        "desktop_firmware",
         "biometrics",
         "biometrics_framework",
         "biometrics_integration",
@@ -211,6 +213,7 @@
         "preload_safety",
         "printing",
         "privacy_infra_policy",
+        "ravenwood",
         "resource_manager",
         "responsible_apis",
         "rust",
@@ -243,8 +246,10 @@
         "wear_system_health",
         "wear_systems",
         "wear_sysui",
+        "wear_system_managed_surfaces",
         "window_surfaces",
         "windowing_frontend",
+        "xr",
     };
 
     public static final String NAMESPACE_REBOOT_STAGING = "staged";
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0475b94..60dbf3f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -389,7 +389,8 @@
      */
     private boolean shouldStartScoForUid(int uid) {
         return !(UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)
-                || UserHandle.isSameApp(uid, Process.PHONE_UID));
+                || UserHandle.isSameApp(uid, Process.PHONE_UID)
+                || UserHandle.isSameApp(uid, Process.SYSTEM_UID));
     }
 
     @GuardedBy("mDeviceStateLock")
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index bc58501..93b0e66 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -123,7 +123,8 @@
                 enrollSuccessful,
                 -1, /* sensorId */
                 ambientLightLux,
-                source);
+                source,
+                -1 /* templateId*/);
     }
 
     /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3dc531e..d71826f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2302,6 +2302,9 @@
         updateLogicalDisplayState(display);
 
         mExternalDisplayPolicy.handleLogicalDisplayAddedLocked(display);
+        if (mDisplayTopologyCoordinator != null) {
+            mDisplayTopologyCoordinator.onDisplayAdded(display.getDisplayInfoLocked());
+        }
     }
 
     private void handleLogicalDisplayChangedLocked(@NonNull LogicalDisplay display) {
@@ -2389,6 +2392,9 @@
         } else {
             releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
         }
+        if (mDisplayTopologyCoordinator != null) {
+            mDisplayTopologyCoordinator.onDisplayRemoved(display.getDisplayIdLocked());
+        }
 
         Slog.i(TAG, "Logical display removed: " + display.getDisplayIdLocked());
     }
diff --git a/services/core/java/com/android/server/display/DisplayTopology.java b/services/core/java/com/android/server/display/DisplayTopology.java
index 90038a0..b01d617 100644
--- a/services/core/java/com/android/server/display/DisplayTopology.java
+++ b/services/core/java/com/android/server/display/DisplayTopology.java
@@ -20,12 +20,15 @@
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Queue;
 
 /**
  * Represents the relative placement of extended displays.
@@ -45,35 +48,50 @@
      * This is not necessarily the same as the default display.
      */
     @VisibleForTesting
-    int mPrimaryDisplayId;
+    int mPrimaryDisplayId = Display.INVALID_DISPLAY;
 
     /**
      * Add a display to the topology.
      * If this is the second display in the topology, it will be placed above the first display.
      * Subsequent displays will be places to the left or right of the second display.
-     * @param displayId The ID of the display
+     * @param displayId The logical display ID
      * @param width The width of the display
      * @param height The height of the display
      */
     void addDisplay(int displayId, double width, double height) {
-        if (mRoot == null) {
-            mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0);
-            mPrimaryDisplayId = displayId;
-            Slog.i(TAG, "First display added: " + mRoot);
-        } else if (mRoot.mChildren.isEmpty()) {
-            // This is the 2nd display. Align the middles of the top and bottom edges.
-            double offset = mRoot.mWidth / 2 - width / 2;
-            TreeNode display = new TreeNode(displayId, width, height,
-                    TreeNode.Position.POSITION_TOP, offset);
-            mRoot.mChildren.add(display);
-            Slog.i(TAG, "Second display added: " + display + ", parent ID: " + mRoot.mDisplayId);
+        addDisplay(displayId, width, height, /* shouldLog= */ true);
+    }
+
+    /**
+     * Remove a display from the topology.
+     * The default topology is created from the remaining displays, as if they were reconnected
+     * one by one.
+     * @param displayId The logical display ID
+     */
+    void removeDisplay(int displayId) {
+        if (!isDisplayPresent(displayId, mRoot)) {
+            return;
+        }
+        Queue<TreeNode> queue = new LinkedList<>();
+        queue.add(mRoot);
+        mRoot = null;
+        while (!queue.isEmpty()) {
+            TreeNode node = queue.poll();
+            if (node.mDisplayId != displayId) {
+                addDisplay(node.mDisplayId, node.mWidth, node.mHeight, /* shouldLog= */ false);
+            }
+            queue.addAll(node.mChildren);
+        }
+        if (mPrimaryDisplayId == displayId) {
+            if (mRoot != null) {
+                mPrimaryDisplayId = mRoot.mDisplayId;
+            } else {
+                mPrimaryDisplayId = Display.INVALID_DISPLAY;
+            }
+            Slog.i(TAG,  "Primary display with ID " + displayId
+                    + " removed, new primary display: " + mPrimaryDisplayId);
         } else {
-            TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
-            TreeNode newDisplay = new TreeNode(displayId, width, height,
-                    TreeNode.Position.POSITION_RIGHT, /* offset= */ 0);
-            rightMostDisplay.mChildren.add(newDisplay);
-            Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: "
-                    + rightMostDisplay.mDisplayId);
+            Slog.i(TAG, "Display with ID " + displayId + " removed");
         }
     }
 
@@ -97,6 +115,35 @@
         }
     }
 
+    private void addDisplay(int displayId, double width, double height, boolean shouldLog) {
+        if (mRoot == null) {
+            mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0);
+            mPrimaryDisplayId = displayId;
+            if (shouldLog) {
+                Slog.i(TAG, "First display added: " + mRoot);
+            }
+        } else if (mRoot.mChildren.isEmpty()) {
+            // This is the 2nd display. Align the middles of the top and bottom edges.
+            double offset = mRoot.mWidth / 2 - width / 2;
+            TreeNode display = new TreeNode(displayId, width, height,
+                    TreeNode.Position.POSITION_TOP, offset);
+            mRoot.mChildren.add(display);
+            if (shouldLog) {
+                Slog.i(TAG, "Second display added: " + display + ", parent ID: "
+                        + mRoot.mDisplayId);
+            }
+        } else {
+            TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
+            TreeNode newDisplay = new TreeNode(displayId, width, height,
+                    TreeNode.Position.POSITION_RIGHT, /* offset= */ 0);
+            rightMostDisplay.mChildren.add(newDisplay);
+            if (shouldLog) {
+                Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: "
+                        + rightMostDisplay.mDisplayId);
+            }
+        }
+    }
+
     /**
      * @param display The display from which the search should start.
      * @param xPos The x position of the right edge of that display.
@@ -126,6 +173,21 @@
         return result;
     }
 
+    private boolean isDisplayPresent(int displayId, TreeNode node) {
+        if (node == null) {
+            return false;
+        }
+        if (node.mDisplayId == displayId) {
+            return true;
+        }
+        for (TreeNode child : node.mChildren) {
+            if (isDisplayPresent(displayId, child)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @VisibleForTesting
     static class TreeNode {
 
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index cbd224c..46358dfd 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -66,6 +66,16 @@
     }
 
     /**
+     * Remove a display from the topology.
+     * @param displayId The logical display ID
+     */
+    void onDisplayRemoved(int displayId) {
+        synchronized (mLock) {
+            mTopology.removeDisplay(displayId);
+        }
+    }
+
+    /**
      * Print the object's state and debug information into the given stream.
      * @param pw The stream to dump information to.
      */
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 03fc60c..cd0a2a7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -106,7 +106,8 @@
     protected final String TAG = getClass().getSimpleName().replace('$', '.');
     protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
+    protected static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
+    protected static final int ON_BINDING_DIED_REBIND_MSG = 1234;
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
     private static final String DB_VERSION_1 = "1";
     private static final String DB_VERSION_2 = "2";
@@ -856,7 +857,13 @@
             String approvedItem = getApprovedValue(pkgOrComponent);
 
             if (approvedItem != null) {
+                final ComponentName component = ComponentName.unflattenFromString(approvedItem);
                 if (enabled) {
+                    if (component != null && !isValidService(component, userId)) {
+                        Log.e(TAG, "Skip allowing " + mConfig.caption + " " + pkgOrComponent
+                                + " (userSet: " + userSet + ") for invalid service");
+                        return;
+                    }
                     approved.add(approvedItem);
                 } else {
                     approved.remove(approvedItem);
@@ -954,7 +961,7 @@
                 || isPackageOrComponentAllowed(component.getPackageName(), userId))) {
             return false;
         }
-        return componentHasBindPermission(component, userId);
+        return isValidService(component, userId);
     }
 
     private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1306,11 +1313,12 @@
                     if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
                         final ComponentName component = ComponentName.unflattenFromString(
                                 approvedPackageOrComponent);
-                        if (component != null && !componentHasBindPermission(component, userId)) {
+                        if (component != null && !isValidService(component, userId)) {
                             approved.removeAt(j);
                             if (DEBUG) {
                                 Slog.v(TAG, "Removing " + approvedPackageOrComponent
-                                        + " from approved list; no bind permission found "
+                                        + " from approved list; no bind permission or "
+                                        + "service interface filter found "
                                         + mConfig.bindPermission);
                             }
                         }
@@ -1329,6 +1337,11 @@
         }
     }
 
+    protected boolean isValidService(ComponentName component, int userId) {
+        return componentHasBindPermission(component, userId) && queryPackageForServices(
+                component.getPackageName(), userId).contains(component);
+    }
+
     protected boolean isValidEntry(String packageOrComponent, int userId) {
         return hasMatchingServices(packageOrComponent, userId);
     }
@@ -1486,23 +1499,25 @@
      * Called when user switched to unbind all services from other users.
      */
     @VisibleForTesting
-    void unbindOtherUserServices(int currentUser) {
+    void unbindOtherUserServices(int switchedToUser) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
-        t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
-        unbindServicesImpl(currentUser, true /* allExceptUser */);
+        t.traceBegin("ManagedServices.unbindOtherUserServices_current" + switchedToUser);
+        unbindServicesImpl(switchedToUser, true /* allExceptUser */);
         t.traceEnd();
     }
 
-    void unbindUserServices(int user) {
+    void unbindUserServices(int removedUser) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
-        t.traceBegin("ManagedServices.unbindUserServices" + user);
-        unbindServicesImpl(user, false /* allExceptUser */);
+        t.traceBegin("ManagedServices.unbindUserServices" + removedUser);
+        unbindServicesImpl(removedUser, false /* allExceptUser */);
         t.traceEnd();
     }
 
     void unbindServicesImpl(int user, boolean allExceptUser) {
         final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
         synchronized (mMutex) {
+            // Remove enqueued rebinds to avoid rebinding services for a switched user
+            mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
             final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
             for (ManagedServiceInfo info : removableBoundServices) {
                 if ((allExceptUser && (info.userid != user))
@@ -1697,6 +1712,7 @@
                             mServicesRebinding.add(servicesBindingTag);
                             mHandler.postDelayed(() ->
                                     reregisterService(name, userid),
+                                    ON_BINDING_DIED_REBIND_MSG,
                                     ON_BINDING_DIED_REBIND_DELAY_MS);
                         } else {
                             Slog.v(TAG, getCaption() + " not rebinding in user " + userid
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 03a34f2..b0d69e6 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -19,6 +19,8 @@
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.DEVICE_CONFIG_UPDATE_BUNDLE_KEY;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BROADCAST_INTENT;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BROADCAST_INTENT;
 import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
 
 import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
@@ -154,7 +156,7 @@
     @GuardedBy("mLock")
     private String[] mTemporaryBroadcastKeys;
     @GuardedBy("mLock")
-    private String mBroadcastPackageName;
+    private String mBroadcastPackageName = SYSTEM_PACKAGE;
     @GuardedBy("mLock")
     private String mTemporaryConfigNamespace;
 
@@ -921,10 +923,7 @@
             }
         }
 
-        return new String[]{mContext.getResources().getString(
-                R.string.config_onDeviceIntelligenceModelLoadedBroadcastKey),
-                mContext.getResources().getString(
-                        R.string.config_onDeviceIntelligenceModelUnloadedBroadcastKey)};
+        return new String[]{ MODEL_LOADED_BROADCAST_INTENT, MODEL_UNLOADED_BROADCAST_INTENT };
     }
 
     @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index c969eff..2c0ce25 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -1059,8 +1059,22 @@
                     throw new SecurityException(errMsg);
                 }
                 if (resetOnForkEnabled()){
-                    for (int tid : tids) {
-                        Process.setThreadScheduler(tid, Process.SCHED_RESET_ON_FORK, 0);
+                    try {
+                        for (int tid : tids) {
+                            int policy = Process.getThreadScheduler(tid);
+                            // If the thread is not using the default scheduling policy (SCHED_OTHER),
+                            // we don't change it.
+                            if (policy != Process.SCHED_OTHER) {
+                                continue;
+                            }
+                            // set the SCHED_RESET_ON_FORK flag.
+                            int prio = Process.getThreadPriority(tid);
+                            Process.setThreadScheduler(tid, Process.SCHED_OTHER | Process.SCHED_RESET_ON_FORK, 0);
+                            Process.setThreadPriority(tid, prio);
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Failed to set SCHED_RESET_ON_FORK for tids "
+                                + Arrays.toString(tids), e);
                     }
                 }
 
@@ -1454,8 +1468,22 @@
                             throw new SecurityException(errMsg);
                         }
                         if (resetOnForkEnabled()){
-                            for (int tid : tids) {
-                                Process.setThreadScheduler(tid, Process.SCHED_RESET_ON_FORK, 0);
+                            try {
+                                for (int tid : tids) {
+                                    int policy = Process.getThreadScheduler(tid);
+                                    // If the thread is not using the default scheduling policy (SCHED_OTHER),
+                                    // we don't change it.
+                                    if (policy != Process.SCHED_OTHER) {
+                                        continue;
+                                    }
+                                    // set the SCHED_RESET_ON_FORK flag.
+                                    int prio = Process.getThreadPriority(tid);
+                                    Process.setThreadScheduler(tid, Process.SCHED_OTHER | Process.SCHED_RESET_ON_FORK, 0);
+                                    Process.setThreadPriority(tid, prio);
+                                }
+                            } catch (Exception e) {
+                                Slog.e(TAG, "Failed to set SCHED_RESET_ON_FORK for tids "
+                                        + Arrays.toString(tids), e);
                             }
                         }
                         if (powerhintThreadCleanup()) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 517f631..ae30fcd 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -442,6 +442,8 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        mService.mAmInternal.addCreatorToken(resultData);
+
         final ActivityRecord r;
         synchronized (mGlobalLock) {
             r = ActivityRecord.isInRootTaskLocked(token);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index dc960a0..3dfc8f4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1228,6 +1228,7 @@
             String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions) {
+        mAmInternal.addCreatorToken(intent);
         return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                 resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                 UserHandle.getCallingUserId());
@@ -1240,6 +1241,11 @@
         assertPackageMatchesCallingUid(callingPackage);
         final String reason = "startActivities";
         enforceNotIsolatedCaller(reason);
+        if (intents != null) {
+            for (Intent intent : intents) {
+                mAmInternal.addCreatorToken(intent);
+            }
+        }
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
         // TODO: Switch to user app stacks here.
         return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
@@ -1269,6 +1275,7 @@
             @Nullable String callingFeatureId, Intent intent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
             ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
+        mAmInternal.addCreatorToken(intent);
         final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
 
         assertPackageMatchesCallingUid(callingPackage);
@@ -1323,6 +1330,7 @@
             }
             // Remove existing mismatch flag so it can be properly updated later
             fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+            mAmInternal.addCreatorToken(fillInIntent);
         }
 
         if (!(target instanceof PendingIntentRecord)) {
@@ -1352,6 +1360,9 @@
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
+
+        mAmInternal.addCreatorToken(intent);
+
         SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
 
         synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index bd01351..c84711d 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
@@ -106,6 +107,16 @@
         return isChangeEnabled(mActivityRecord, OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
     }
 
+    boolean shouldRespectRequestedOrientationDueToOverride() {
+        // Checking TaskFragment rather than ActivityRecord to ensure that transition
+        // between fullscreen and PiP would work well. Checking TaskFragment rather than
+        // Task to ensure that Activity Embedding is excluded.
+        return mActivityRecord.isVisibleRequested() && mActivityRecord.getTaskFragment() != null
+                && mActivityRecord.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                && mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+                    .isOverrideRespectRequestedOrientationEnabled();
+    }
+
     /**
      * Whether an app is calling {@link android.app.Activity#setRequestedOrientation}
      * in a loop and orientation request should be ignored.
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index d699af8..4d17ed2 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -160,6 +160,7 @@
             Intent intent, String resolvedType, Bundle bOptions) {
         checkCallerOrSystemOrRoot();
         mService.assertPackageMatchesCallingUid(callingPackage);
+        mService.mAmInternal.addCreatorToken(intent);
 
         int callingUser = UserHandle.getCallingUserId();
         Task task;
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 34b5f6a..1a8f5fc 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -30,7 +30,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
-import android.app.TaskInfo;
 import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.pm.ActivityInfo.WindowLayout;
 import android.graphics.Rect;
@@ -98,7 +97,6 @@
     private static Rect calculateInitialBounds(@NonNull Task task,
             @NonNull ActivityRecord activity, @NonNull Rect stableBounds
     ) {
-        final TaskInfo taskInfo = task.getTaskInfo();
         // Display bounds not taking into account insets.
         final TaskDisplayArea displayArea = task.getDisplayArea();
         final Rect screenBounds = displayArea.getBounds();
@@ -118,14 +116,15 @@
         float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio(task);
         final float tdaWidth = stableBounds.width();
         final float tdaHeight = stableBounds.height();
+        final int taskConfigOrientation = task.getConfiguration().orientation;
         final int activityOrientation = getActivityOrientation(activity, task);
-        final Size initialSize = switch (taskInfo.configuration.orientation) {
+        final Size initialSize = switch (taskConfigOrientation) {
             case ORIENTATION_LANDSCAPE -> {
                 // Device in landscape orientation.
                 if (appAspectRatio == 0) {
                     appAspectRatio = 1;
                 }
-                if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, taskInfo, task)) {
+                if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, task)) {
                     if (isFixedOrientationPortrait(activityOrientation)) {
                         // For portrait resizeable activities, respect apps fullscreen width but
                         // apply ideal size height.
@@ -143,7 +142,7 @@
                 // Device in portrait orientation.
                 final int customPortraitWidthForLandscapeApp = screenBounds.width()
                         - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
-                if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, taskInfo, task)) {
+                if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, task)) {
                     if (isFixedOrientationLandscape(activityOrientation)) {
                         if (appAspectRatio == 0) {
                             appAspectRatio = tdaWidth / (tdaWidth - 1);
@@ -182,8 +181,8 @@
      */
     private static boolean canChangeAspectRatio(
             @NonNull DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy,
-            @NonNull TaskInfo taskInfo, @NonNull Task task) {
-        return taskInfo.isResizeable
+            @NonNull Task task) {
+        return task.isResizeable()
                 && !desktopAppCompatAspectRatioPolicy.hasMinAspectRatioOverride(task);
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 0416f74..29ffda7 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -260,15 +259,14 @@
         if (mDisplayContent == null) {
             return false;
         }
-        ActivityRecord activity = mDisplayContent.topRunningActivity(
-                /* considerKeyguardState= */ true);
-        return activity != null && activity.getTaskFragment() != null
-                // Checking TaskFragment rather than ActivityRecord to ensure that transition
-                // between fullscreen and PiP would work well. Checking TaskFragment rather than
-                // Task to ensure that Activity Embedding is excluded.
-                && activity.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                && activity.mAppCompatController.getAppCompatOrientationOverrides()
-                    .isOverrideRespectRequestedOrientationEnabled();
+
+        // Top running activity can be freeform and ignore orientation request from bottom activity
+        // that should be respected, Check all activities in display to make sure any eligible
+        // activity should be respected.
+        final ActivityRecord activity = mDisplayContent.getActivity((r) ->
+                r.mAppCompatController.getAppCompatOrientationOverrides()
+                    .shouldRespectRequestedOrientationDueToOverride());
+        return activity != null;
     }
 
     boolean getIgnoreOrientationRequest() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c062f5a..04a625b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -79,6 +79,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.window.flags.Flags.enableFullyImmersiveInDesktop;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -2515,10 +2516,16 @@
                 defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
                         && task.getTopLeafTask().getAdjacentTask() != null)
                         != null;
-        final boolean freeformRootTaskVisible =
-                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
+        final Task topFreeformTask = defaultTaskDisplayArea
+                .getTopRootTaskInWindowingMode(WINDOWING_MODE_FREEFORM);
+        final boolean freeformRootTaskVisible = topFreeformTask != null
+                && topFreeformTask.isVisible();
+        final boolean inNonFullscreenFreeformMode = freeformRootTaskVisible
+                && !topFreeformTask.getBounds().equals(mDisplayContent.getBounds());
 
-        getInsetsPolicy().updateSystemBars(win, adjacentTasksVisible, freeformRootTaskVisible);
+        getInsetsPolicy().updateSystemBars(win, adjacentTasksVisible,
+                enableFullyImmersiveInDesktop()
+                        ? inNonFullscreenFreeformMode : freeformRootTaskVisible);
 
         final boolean topAppHidesStatusBar = topAppHidesSystemBar(Type.statusBars());
         if (getStatusBar() != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 61b13a8..24a6f118 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -628,8 +628,9 @@
         return (mForcedShowingTypes & types) == types;
     }
 
-    void updateSystemBars(WindowState win, boolean inSplitScreenMode, boolean inFreeformMode) {
-        mForcedShowingTypes = (inSplitScreenMode || inFreeformMode)
+    void updateSystemBars(WindowState win, boolean inSplitScreenMode,
+            boolean inNonFullscreenFreeformMode) {
+        mForcedShowingTypes = (inSplitScreenMode || inNonFullscreenFreeformMode)
                 ? (Type.statusBars() | Type.navigationBars())
                 : forceShowingNavigationBars(win)
                         ? Type.navigationBars()
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index b6e4c11..9de96f14 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -551,6 +551,12 @@
         long currentElapsedTime = SystemClock.elapsedRealtime();
         for (int i = 0; i < tasks.size(); i++) {
             Task task = tasks.get(i);
+            // Remove the task restored from xml if any existing tasks match.
+            if (findRemoveIndexForAddTask(task) >= 0) {
+                tasks.remove(i);
+                i--;
+                continue;
+            }
             task.lastActiveTime = currentElapsedTime - i;
         }
 
@@ -561,6 +567,7 @@
         if (existedTaskIds.size() > 0) {
             syncPersistentTaskIdsLocked();
         }
+        mTaskNotificationController.notifyTaskListUpdated();
     }
 
     private boolean isRecentTasksLoaded(int userId) {
@@ -679,27 +686,35 @@
         if (isRecentTasksLoaded(userId)) {
             Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
             mUsersWithRecentsLoaded.delete(userId);
-            removeTasksForUserLocked(userId);
+            removeTasksForUserFromMemoryLocked(userId);
         }
         mPersistedTaskIds.delete(userId);
         mTaskPersister.unloadUserDataFromMemory(userId);
     }
 
     /** Remove recent tasks for a user. */
-    private void removeTasksForUserLocked(int userId) {
+    private void removeTasksForUserFromMemoryLocked(int userId) {
         if (userId <= 0) {
             Slog.i(TAG, "Can't remove recent task on user " + userId);
             return;
         }
 
+        boolean notifyTaskUpdated = false;
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             Task task = mTasks.get(i);
             if (task.mUserId == userId) {
                 ProtoLog.i(WM_DEBUG_TASKS, "remove RecentTask %s when finishing user "
                         + "%d", task, userId);
-                remove(task);
+                mTasks.remove(task);
+                mService.mWindowManager.mSnapshotController.mTaskSnapshotController
+                        .removeSnapshotCache(task.mTaskId);
+                // Only notify if list has changed.
+                notifyTaskUpdated = true;
             }
         }
+        if (notifyTaskUpdated) {
+            mTaskNotificationController.notifyTaskListUpdated();
+        }
     }
 
     void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
index 1fad14b..f3a8d841 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.display
 
+import android.view.Display
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
@@ -23,7 +24,7 @@
     private val topology = DisplayTopology()
 
     @Test
-    fun oneDisplay() {
+    fun addOneDisplay() {
         val displayId = 1
         val width = 800.0
         val height = 600.0
@@ -40,7 +41,7 @@
     }
 
     @Test
-    fun twoDisplays() {
+    fun addTwoDisplays() {
         val displayId1 = 1
         val width1 = 800.0
         val height1 = 600.0
@@ -71,7 +72,7 @@
     }
 
     @Test
-    fun manyDisplays() {
+    fun addManyDisplays() {
         val displayId1 = 1
         val width1 = 800.0
         val height1 = 600.0
@@ -118,4 +119,130 @@
             assertThat(display.mOffset).isEqualTo(0)
         }
     }
+
+    @Test
+    fun removeDisplays() {
+        val displayId1 = 1
+        val width1 = 800.0
+        val height1 = 600.0
+
+        val displayId2 = 2
+        val width2 = 1000.0
+        val height2 = 1500.0
+
+        topology.addDisplay(displayId1, width1, height1)
+        topology.addDisplay(displayId2, width2, height2)
+
+        val noOfDisplays = 30
+        for (i in 3..noOfDisplays) {
+            topology.addDisplay(/* displayId= */ i, width1, height1)
+        }
+
+        var removedDisplays = arrayOf(20)
+        topology.removeDisplay(20)
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
+
+        var display1 = topology.mRoot!!
+        assertThat(display1.mDisplayId).isEqualTo(displayId1)
+        assertThat(display1.mWidth).isEqualTo(width1)
+        assertThat(display1.mHeight).isEqualTo(height1)
+        assertThat(display1.mChildren).hasSize(1)
+
+        var display2 = display1.mChildren[0]
+        assertThat(display2.mDisplayId).isEqualTo(displayId2)
+        assertThat(display2.mWidth).isEqualTo(width2)
+        assertThat(display2.mHeight).isEqualTo(height2)
+        assertThat(display2.mChildren).hasSize(1)
+        assertThat(display2.mPosition).isEqualTo(
+            DisplayTopology.TreeNode.Position.POSITION_TOP)
+        assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
+
+        var display = display2
+        for (i in 3..noOfDisplays) {
+            if (i in removedDisplays) {
+                continue
+            }
+            display = display.mChildren[0]
+            assertThat(display.mDisplayId).isEqualTo(i)
+            assertThat(display.mWidth).isEqualTo(width1)
+            assertThat(display.mHeight).isEqualTo(height1)
+            // The last display should have no children
+            assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
+            assertThat(display.mPosition).isEqualTo(
+                DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+            assertThat(display.mOffset).isEqualTo(0)
+        }
+
+        topology.removeDisplay(22)
+        removedDisplays += 22
+        topology.removeDisplay(23)
+        removedDisplays += 23
+        topology.removeDisplay(25)
+        removedDisplays += 25
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
+
+        display1 = topology.mRoot!!
+        assertThat(display1.mDisplayId).isEqualTo(displayId1)
+        assertThat(display1.mWidth).isEqualTo(width1)
+        assertThat(display1.mHeight).isEqualTo(height1)
+        assertThat(display1.mChildren).hasSize(1)
+
+        display2 = display1.mChildren[0]
+        assertThat(display2.mDisplayId).isEqualTo(displayId2)
+        assertThat(display2.mWidth).isEqualTo(width2)
+        assertThat(display2.mHeight).isEqualTo(height2)
+        assertThat(display2.mChildren).hasSize(1)
+        assertThat(display2.mPosition).isEqualTo(
+            DisplayTopology.TreeNode.Position.POSITION_TOP)
+        assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
+
+        display = display2
+        for (i in 3..noOfDisplays) {
+            if (i in removedDisplays) {
+                continue
+            }
+            display = display.mChildren[0]
+            assertThat(display.mDisplayId).isEqualTo(i)
+            assertThat(display.mWidth).isEqualTo(width1)
+            assertThat(display.mHeight).isEqualTo(height1)
+            // The last display should have no children
+            assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
+            assertThat(display.mPosition).isEqualTo(
+                DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+            assertThat(display.mOffset).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun removeAllDisplays() {
+        val displayId = 1
+        val width = 800.0
+        val height = 600.0
+
+        topology.addDisplay(displayId, width, height)
+        topology.removeDisplay(displayId)
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
+        assertThat(topology.mRoot).isNull()
+    }
+
+    @Test
+    fun removeDisplayThatDoesNotExist() {
+        val displayId = 1
+        val width = 800.0
+        val height = 600.0
+
+        topology.addDisplay(displayId, width, height)
+        topology.removeDisplay(3)
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId)
+
+        val display = topology.mRoot!!
+        assertThat(display.mDisplayId).isEqualTo(displayId)
+        assertThat(display.mWidth).isEqualTo(width)
+        assertThat(display.mHeight).isEqualTo(height)
+        assertThat(display.mChildren).isEmpty()
+    }
 }
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 809e13c..6ccc037 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -29,6 +29,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
 import static android.content.ContentResolver.SCHEME_CONTENT;
+import static android.content.Intent.FILL_IN_ACTION;
 import static android.os.PowerExemptionManager.REASON_DENIED;
 import static android.os.UserHandle.USER_ALL;
 import static android.util.DebugUtils.valueToString;
@@ -1300,6 +1301,45 @@
                 .containsExactly(new Pair<>(USER_ID, 42));
     }
 
+    @Test
+    @RequiresFlagsEnabled(android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT)
+    public void testAddCreatorToken() {
+        Intent intent = new Intent();
+        Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
+        intent.putExtra("EXTRA_INTENT0", extraIntent);
+
+        intent.collectExtraIntentKeys();
+        mAms.addCreatorToken(intent);
+
+        ActivityManagerService.IntentCreatorToken token =
+                (ActivityManagerService.IntentCreatorToken) extraIntent.getCreatorToken();
+        assertThat(token).isNotNull();
+        assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT)
+    public void testAddCreatorTokenForFillingIntent() {
+        Intent intent = new Intent();
+        Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
+        intent.putExtra("EXTRA_INTENT0", extraIntent);
+        Intent fillinIntent = new Intent();
+        Intent fillinExtraIntent = new Intent("FILLIN_EXTRA_INTENT_ACTION");
+        fillinIntent.putExtra("FILLIN_EXTRA_INTENT0", fillinExtraIntent);
+
+        fillinIntent.collectExtraIntentKeys();
+        intent.fillIn(fillinIntent, FILL_IN_ACTION);
+
+        mAms.addCreatorToken(fillinIntent);
+
+        fillinExtraIntent = intent.getParcelableExtra("FILLIN_EXTRA_INTENT0", Intent.class);
+
+        ActivityManagerService.IntentCreatorToken token =
+                (ActivityManagerService.IntentCreatorToken) fillinExtraIntent.getCreatorToken();
+        assertThat(token).isNotNull();
+        assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+    }
+
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
             long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
index 5181af1..aa22790 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
@@ -2,3 +2,4 @@
 
 per-file BackgroundDexOptServiceUnitTest.java = file:/services/core/java/com/android/server/pm/dex/OWNERS
 per-file StagingManagerTest.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
+per-file ApexManagerTest.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index e16bc05..98b1191 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -150,6 +150,7 @@
     private static final String NONBLOCKED_APP_PACKAGE_NAME = "com.someapp";
     private static final String PERMISSION_CONTROLLER_PACKAGE_NAME =
             "com.android.permissioncontroller";
+    private static final String VIRTUAL_DEVICE_OWNER_PACKAGE = "com.android.virtualdevice.test";
     private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
     private static final String VENDING_PACKAGE_NAME = "com.android.vending";
     private static final String GOOGLE_DIALER_PACKAGE_NAME = "com.google.android.dialer";
@@ -294,11 +295,10 @@
     private Intent createRestrictedActivityBlockedIntent(Set<String> displayCategories,
             String targetDisplayCategory) {
         when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(),
-                eq(NONBLOCKED_APP_PACKAGE_NAME))).thenReturn(DISPLAY_ID_1);
+                eq(VIRTUAL_DEVICE_OWNER_PACKAGE))).thenReturn(DISPLAY_ID_1);
         VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("display", 640, 480,
                 420).setDisplayCategories(displayCategories).build();
-        mDeviceImpl.createVirtualDisplay(config, mVirtualDisplayCallback,
-                NONBLOCKED_APP_PACKAGE_NAME);
+        mDeviceImpl.createVirtualDisplay(config, mVirtualDisplayCallback);
         GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1949,7 +1949,7 @@
                         mVirtualDeviceLog,
                         new Binder(),
                         new AttributionSource(
-                                ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
+                                ownerUid, VIRTUAL_DEVICE_OWNER_PACKAGE, "virtualdevice"),
                         virtualDeviceId,
                         mInputController,
                         mCameraAccessController,
@@ -1970,8 +1970,7 @@
     private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId) {
         when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback),
                 eq(virtualDevice), any(), any())).thenReturn(displayId);
-        virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback,
-                NONBLOCKED_APP_PACKAGE_NAME);
+        virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback);
         final String uniqueId = UNIQUE_ID + displayId;
         doAnswer(inv -> {
             final DisplayInfo displayInfo = new DisplayInfo();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 48bc9d7..3bbc6b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -63,11 +63,13 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
+import android.testing.TestableLooper;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -82,6 +84,7 @@
 
 import com.google.android.collect.Lists;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -103,6 +106,7 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
+
 public class ManagedServicesTest extends UiServiceTestCase {
 
     @Mock
@@ -115,6 +119,7 @@
     private ManagedServices.UserProfiles mUserProfiles;
     @Mock private DevicePolicyManager mDpm;
     Object mLock = new Object();
+    private TestableLooper mTestableLooper;
 
     UserInfo mZero = new UserInfo(0, "zero", 0);
     UserInfo mTen = new UserInfo(10, "ten", 0);
@@ -142,6 +147,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mTestableLooper = new TestableLooper(Looper.getMainLooper());
 
         mContext.setMockPackageManager(mPm);
         mContext.addMockSystemService(Context.USER_SERVICE, mUm);
@@ -199,6 +205,11 @@
                 mIpm, APPROVAL_BY_COMPONENT);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        mTestableLooper.destroy();
+    }
+
     @Test
     public void testBackupAndRestore_migration() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -888,7 +899,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -919,7 +930,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -950,7 +961,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a/a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -981,7 +992,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a/a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -1053,6 +1064,77 @@
     }
 
     @Test
+    public void registerService_bindingDied_rebindIsClearedOnUserSwitch() throws Exception {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mPkg);
+        when(context.getUserId()).thenReturn(mUser.getIdentifier());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_PACKAGE);
+        service = spy(service);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        // Trigger onBindingDied for component when registering
+        //  => will schedule a rebind in 10 seconds
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onBindingDied(cn);
+            return true;
+        });
+        service.registerService(cn, 0);
+        assertThat(service.isBound(cn, 0)).isFalse();
+
+        // Switch to user 10
+        service.onUserSwitched(10);
+
+        // Check that the scheduled rebind for user 0 was cleared
+        mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
+        mTestableLooper.processAllMessages();
+        verify(service, never()).reregisterService(any(), anyInt());
+    }
+
+    @Test
+    public void registerService_bindingDied_rebindIsExecutedAfterTimeout() throws Exception {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mPkg);
+        when(context.getUserId()).thenReturn(mUser.getIdentifier());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_PACKAGE);
+        service = spy(service);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        // Trigger onBindingDied for component when registering
+        //  => will schedule a rebind in 10 seconds
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onBindingDied(cn);
+            return true;
+        });
+        service.registerService(cn, 0);
+        assertThat(service.isBound(cn, 0)).isFalse();
+
+        // Check that the scheduled rebind is run
+        mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
+        mTestableLooper.processAllMessages();
+        verify(service, times(1)).reregisterService(eq(cn), eq(0));
+    }
+
+    @Test
     public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1211,6 +1293,64 @@
     }
 
     @Test
+    public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
+        Context context = spy(getContext());
+        doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        List<String> packages = new ArrayList<>();
+        packages.add("package");
+        addExpectedServices(service, packages, 0);
+
+        final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+        final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+        // Both components are approved initially
+        mExpectedPrimaryComponentNames.clear();
+        mExpectedPrimaryPackages.clear();
+        mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+        mExpectedSecondaryComponentNames.clear();
+        mExpectedSecondaryPackages.clear();
+
+        loadXml(service);
+
+        //Component package/C1 loses serviceInterface intent filter
+        ManagedServices.Config config = service.getConfig();
+        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+                .thenAnswer(new Answer<List<ResolveInfo>>() {
+                    @Override
+                    public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+                            throws Throwable {
+                        Object[] args = invocationOnMock.getArguments();
+                        Intent invocationIntent = (Intent) args[0];
+                        if (invocationIntent != null) {
+                            if (invocationIntent.getAction().equals(config.serviceInterface)
+                                    && packages.contains(invocationIntent.getPackage())) {
+                                List<ResolveInfo> dummyServices = new ArrayList<>();
+                                ResolveInfo resolveInfo = new ResolveInfo();
+                                ServiceInfo serviceInfo = new ServiceInfo();
+                                serviceInfo.packageName = invocationIntent.getPackage();
+                                serviceInfo.name = approvedComponent.getClassName();
+                                serviceInfo.permission = service.getConfig().bindPermission;
+                                resolveInfo.serviceInfo = serviceInfo;
+                                dummyServices.add(resolveInfo);
+                                return dummyServices;
+                            }
+                        }
+                        return new ArrayList<>();
+                    }
+                });
+
+        // Trigger package update
+        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+        assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
+        assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
+    }
+
+    @Test
     public void testSetPackageOrComponentEnabled() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1223,6 +1363,21 @@
                             "user10package1/K", "user10.3/Component", "user10package2/L",
                             "user10.4/Component"}));
 
+            // mock permissions for services
+            PackageManager pm = mock(PackageManager.class);
+            when(getContext().getPackageManager()).thenReturn(pm);
+            List<ComponentName> enabledComponents = List.of(
+                    ComponentName.unflattenFromString("package/Comp"),
+                    ComponentName.unflattenFromString("package/C2"),
+                    ComponentName.unflattenFromString("again/M4"),
+                    ComponentName.unflattenFromString("user10package/B"),
+                    ComponentName.unflattenFromString("user10/Component"),
+                    ComponentName.unflattenFromString("user10package1/K"),
+                    ComponentName.unflattenFromString("user10.3/Component"),
+                    ComponentName.unflattenFromString("user10package2/L"),
+                    ComponentName.unflattenFromString("user10.4/Component"));
+            mockServiceInfoWithMetaData(enabledComponents, service, pm, new ArrayMap<>());
+
             for (int userId : expectedEnabled.keySet()) {
                 ArrayList<String> expectedForUser = expectedEnabled.get(userId);
                 for (int i = 0; i < expectedForUser.size(); i++) {
@@ -1944,7 +2099,7 @@
         metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
         metaDatas.put(cn_allowed, metaDataAutobindAllow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_allowed.flattenToString(), 0, true);
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -1989,7 +2144,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2028,7 +2183,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2099,8 +2254,8 @@
     }
 
     private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
-            ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
-            throws RemoteException {
+            ManagedServices service, PackageManager packageManager,
+            ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
         when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
                 (Answer<ServiceInfo>) invocation -> {
                     ComponentName invocationCn = invocation.getArgument(0);
@@ -2115,6 +2270,39 @@
                     return null;
                 }
         );
+
+        // add components to queryIntentServicesAsUser response
+        final List<String> packages = new ArrayList<>();
+        for (ComponentName cn: componentNames) {
+            packages.add(cn.getPackageName());
+        }
+        ManagedServices.Config config = service.getConfig();
+        when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
+                thenAnswer(new Answer<List<ResolveInfo>>() {
+                @Override
+                public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+                    throws Throwable {
+                    Object[] args = invocationOnMock.getArguments();
+                    Intent invocationIntent = (Intent) args[0];
+                    if (invocationIntent != null) {
+                        if (invocationIntent.getAction().equals(config.serviceInterface)
+                            && packages.contains(invocationIntent.getPackage())) {
+                            List<ResolveInfo> dummyServices = new ArrayList<>();
+                            for (ComponentName cn: componentNames) {
+                                ResolveInfo resolveInfo = new ResolveInfo();
+                                ServiceInfo serviceInfo = new ServiceInfo();
+                                serviceInfo.packageName = invocationIntent.getPackage();
+                                serviceInfo.name = cn.getClassName();
+                                serviceInfo.permission = service.getConfig().bindPermission;
+                                resolveInfo.serviceInfo = serviceInfo;
+                                dummyServices.add(resolveInfo);
+                            }
+                            return dummyServices;
+                        }
+                    }
+                    return new ArrayList<>();
+                }
+            });
     }
 
     private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 797b95b5..7e4ae67 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -25,6 +25,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertNull;
+
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.any;
@@ -193,6 +194,8 @@
     public void testWriteXml_userTurnedOffNAS() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
+
         mAssistants.loadDefaultsFromConfig(true);
 
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
@@ -398,6 +401,10 @@
     public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
         ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
         ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
+
+        doReturn(true).when(mAssistants).isValidService(eq(component1), eq(mZero.id));
+        doReturn(true).when(mAssistants).isValidService(eq(component2), eq(mZero.id));
+
         mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
                 true, true);
         verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal(
@@ -543,6 +550,7 @@
     public void testSetAdjustmentTypeSupportedState() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                 true, true);
@@ -566,6 +574,7 @@
     public void testSetAdjustmentTypeSupportedState_readWriteXml_entries() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                 true, true);
@@ -589,6 +598,7 @@
     public void testSetAdjustmentTypeSupportedState_readWriteXml_empty() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                 true, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 03cad24..592eec5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2291,10 +2291,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         NotificationRecord r = getBeepyNotification();
 
@@ -2338,10 +2335,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         NotificationRecord r = getBeepyNotification();
 
@@ -2379,10 +2373,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         NotificationRecord r = getBeepyNotification();
 
@@ -2428,10 +2419,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         NotificationRecord r = getBeepyNotification();
         r.getNotification().category = Notification.CATEGORY_EVENT;
@@ -2504,10 +2492,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         // Regular notification: should beep at 0% volume
         NotificationRecord r = getBeepyNotification();
@@ -2574,10 +2559,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         NotificationRecord r = getBeepyNotification();
 
@@ -2602,10 +2584,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         // CATEGORY_ALARM is exempted
         NotificationRecord r = getBeepyNotification();
@@ -2646,10 +2625,7 @@
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
 
-        // Trigger avalanche trigger intent
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", false);
-        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        triggerAvalancheEvent();
 
         // Create a conversation group with GROUP_ALERT_SUMMARY behavior
         // Where the summary is not MessagingStyle
@@ -2693,6 +2669,16 @@
         assertEquals(-1, summary.getLastAudiblyAlertedMs());
     }
 
+    private void triggerAvalancheEvent() throws Exception {
+        // Trigger avalanche trigger intent
+        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", false);
+        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+        // Wait after avalanche trigger before posting notifications
+        // so that notification#getWhen() is not the same value
+        Thread.sleep(100);
+    }
+
     @Test
     public void testBeepVolume_politeNotif_exemptEmergency() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
index 9b9040b..cb5afd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
@@ -18,7 +18,6 @@
 
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_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 com.android.server.wm.AppCompatCameraPolicy.isTreatmentEnabledForActivity;
 import static com.android.server.wm.AppCompatCameraPolicy.shouldOverrideMinAspectRatioForCamera;
@@ -26,7 +25,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
 
 import android.compat.testing.PlatformCompatChangeRule;
 import android.platform.test.annotations.DisableFlags;
@@ -90,7 +88,7 @@
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_presentWhenEnabledAndDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(/* isAllowed= */ true);
+            robot.dw().allowEnterDesktopMode(/* isAllowed= */ true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
         });
@@ -100,7 +98,7 @@
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(/* isAllowed= */ false);
+            robot.dw().allowEnterDesktopMode(/* isAllowed= */ false);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
         });
@@ -110,7 +108,7 @@
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoFlag() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(/* isAllowed= */ true);
+            robot.dw().allowEnterDesktopMode(/* isAllowed= */ true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
         });
@@ -120,7 +118,7 @@
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoFlagAndNoDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(/* isAllowed= */ false);
+            robot.dw().allowEnterDesktopMode(/* isAllowed= */ false);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
         });
@@ -130,7 +128,7 @@
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_startedWhenEnabledAndDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(/* isAllowed= */ true);
+            robot.dw().allowEnterDesktopMode(/* isAllowed= */ true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
             robot.checkTopActivityCameraCompatFreeformPolicyIsRunning();
@@ -141,7 +139,7 @@
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraStateManager_existsWhenCameraCompatFreeformExists() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(true);
+            robot.dw().allowEnterDesktopMode(true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
             robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
@@ -152,7 +150,7 @@
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraStateManager_startedWhenCameraCompatFreeformExists() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(true);
+            robot.dw().allowEnterDesktopMode(true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
             robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
             robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
@@ -224,7 +222,7 @@
     public void testShouldOverrideMinAspectRatioForCamera_whenCameraIsNotRunning() {
         runTestScenario((robot) -> {
             robot.applyOnActivity((a)-> {
-                robot.allowEnterDesktopMode(true);
+                robot.dw().allowEnterDesktopMode(true);
                 robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
                 a.createActivityWithComponentInNewTaskAndDisplay();
                 a.setIsCameraRunningAndWindowingModeEligibleFullscreen(/* enabled */ false);
@@ -239,7 +237,7 @@
     public void testShouldOverrideMinAspectRatioForCamera_whenCameraIsRunning_overrideDisabled() {
         runTestScenario((robot) -> {
             robot.applyOnActivity((a)-> {
-                robot.allowEnterDesktopMode(true);
+                robot.dw().allowEnterDesktopMode(true);
                 robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
                 a.createActivityWithComponentInNewTaskAndDisplay();
                 a.setIsCameraRunningAndWindowingModeEligibleFullscreen(/* active */ true);
@@ -270,7 +268,7 @@
     public void testShouldOverrideMinAspectRatioForCameraFreeform_cameraRunning_overrideEnabled() {
         runTestScenario((robot) -> {
             robot.applyOnActivity((a)-> {
-                robot.allowEnterDesktopMode(true);
+                robot.dw().allowEnterDesktopMode(true);
                 a.createActivityWithComponentInNewTaskAndDisplay();
                 a.setIsCameraRunningAndWindowingModeEligibleFreeform(/* active */ true);
             });
@@ -346,11 +344,5 @@
         void checkShouldOverrideMinAspectRatioForCamera(boolean expected) {
             assertEquals(expected, shouldOverrideMinAspectRatioForCamera(activity().top()));
         }
-
-        // TODO(b/350460645): Create Desktop Windowing Robot to reuse common functionalities.
-        void allowEnterDesktopMode(boolean isAllowed) {
-            doReturn(isAllowed).when(() ->
-                    DesktopModeHelper.canEnterDesktopMode(any()));
-        }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
index d9b5f37..8747cfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -17,11 +17,13 @@
 
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
 import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
@@ -31,6 +33,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
@@ -228,6 +231,25 @@
         });
     }
 
+    @Test
+    public void testOverrideRespectRequestedOrientationIsEnabled_bottomOrientationIsRespected() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setIgnoreOrientationRequest(true);
+                a.createActivityWithComponentInNewTask();
+                robot.setOverrideRespectRequestedOrientationEnabled(true);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_LANDSCAPE);
+                robot.checkDisplayShouldIgnoreOrientationRequest(SCREEN_ORIENTATION_LANDSCAPE,
+                        /* expected */ false);
+
+                a.createActivityWithComponentInNewTask();
+                a.setTopActivityInFreeformWindowingMode(true);
+            });
+            robot.checkDisplayShouldIgnoreOrientationRequest(SCREEN_ORIENTATION_LANDSCAPE,
+                    /* expected */ false);
+        });
+    }
+
     /**
      * Runs a test scenario providing a Robot.
      */
@@ -291,6 +313,22 @@
             }
         }
 
+        void setOverrideRespectRequestedOrientationEnabled(boolean override) {
+            spyOn(getTopOrientationOverrides());
+            doReturn(override).when(getTopOrientationOverrides())
+                    .isOverrideRespectRequestedOrientationEnabled();
+        }
+
+        void checkDisplayShouldIgnoreOrientationRequest(@ScreenOrientation int candidate,
+                boolean expected) {
+            assertEquals(expected, activity().displayContent()
+                    .shouldIgnoreOrientationRequest(candidate));
+        }
+
+        void checkExpectedDisplayOrientation(@ScreenOrientation int expected) {
+            assertEquals(expected, activity().displayContent().getOrientation());
+        }
+
         void checkShouldUseDisplayLandscapeNaturalOrientation(boolean expected) {
             assertEquals(expected,
                     getTopOrientationOverrides().shouldUseDisplayLandscapeNaturalOrientation());
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 76101d5..09ed9ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -37,12 +37,10 @@
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
-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_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 
 import android.compat.testing.PlatformCompatChangeRule;
@@ -338,7 +336,7 @@
     public void testOverrideOrientationIfNeeded_fullscrOverrideFreeform_cameraActivity_unchanged() {
         runTestScenario((robot) -> {
             robot.applyOnActivity((a) -> {
-                robot.allowEnterDesktopMode(true);
+                robot.dw().allowEnterDesktopMode(true);
                 a.createActivityWithComponentInNewTaskAndDisplay();
                 a.setIsCameraRunningAndWindowingModeEligibleFreeform(false);
             });
@@ -610,11 +608,5 @@
         private AppCompatOrientationPolicy getTopAppCompatOrientationPolicy() {
             return activity().top().mAppCompatController.getOrientationPolicy();
         }
-
-        // TODO(b/350460645): Create Desktop Windowing Robot to reuse common functionalities.
-        void allowEnterDesktopMode(boolean isAllowed) {
-            doReturn(isAllowed).when(() ->
-                    DesktopModeHelper.canEnterDesktopMode(any()));
-        }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
index 5f2a63a..0d929ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -39,6 +39,8 @@
     private final AppCompatComponentPropRobot mOptPropRobot;
     @NonNull
     private final AppCompatResourcesRobot mResourcesRobot;
+    @NonNull
+    private final DesktopWindowingRobot mDesktopWindowingRobot;
 
     AppCompatRobotBase(@NonNull WindowManagerService wm,
             @NonNull ActivityTaskManagerService atm,
@@ -51,6 +53,7 @@
                 new AppCompatConfigurationRobot(wm.mAppCompatConfiguration);
         mOptPropRobot = new AppCompatComponentPropRobot(wm);
         mResourcesRobot = new AppCompatResourcesRobot(wm.mContext.getResources());
+        mDesktopWindowingRobot = new DesktopWindowingRobot();
     }
 
     AppCompatRobotBase(@NonNull WindowManagerService wm,
@@ -111,6 +114,11 @@
         return mResourcesRobot;
     }
 
+    @NonNull
+    DesktopWindowingRobot dw() {
+        return mDesktopWindowingRobot;
+    }
+
     void applyOnResources(@NonNull Consumer<AppCompatResourcesRobot> consumer) {
         consumer.accept(mResourcesRobot);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopWindowingRobot.java b/services/tests/wmtests/src/com/android/server/wm/DesktopWindowingRobot.java
new file mode 100644
index 0000000..285a5e2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopWindowingRobot.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static org.mockito.ArgumentMatchers.any;
+
+/** Robot for changing desktop windowing properties. */
+class DesktopWindowingRobot {
+    void allowEnterDesktopMode(boolean isAllowed) {
+        doReturn(isAllowed).when(() -> DesktopModeHelper.canEnterDesktopMode(any()));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index b26c267..d2cf03d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -41,7 +41,10 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.StatusBarManager;
+import android.graphics.Rect;
 import android.os.Binder;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
@@ -52,6 +55,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -95,6 +99,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     public void testControlsForDispatch_freeformTaskVisible() {
         addStatusBar();
         addNavigationBar();
@@ -108,6 +113,37 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void testControlsForDispatch_fullscreenFreeformTaskVisible() {
+        addStatusBar();
+        addNavigationBar();
+
+        final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+        win.setBounds(new Rect());
+        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+        // The freeform (w/fullscreen bounds) app window can control both system bars.
+        assertNotNull(controls);
+        assertEquals(2, controls.length);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void testControlsForDispatch_nonFullscreenFreeformTaskVisible() {
+        addStatusBar();
+        addNavigationBar();
+
+        final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+        win.getTask().setBounds(new Rect(1, 1, 10, 10));
+        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+        // The freeform (but not fullscreen bounds) app window must not control any system bars.
+        assertNull(controls);
+    }
+
+    @Test
     public void testControlsForDispatch_forceStatusBarVisible() {
         addStatusBar().mAttrs.forciblyShownTypes |= statusBars();
         addNavigationBar();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ff966ae..f01cfc1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2741,7 +2741,7 @@
 
     /**
      * Returns a constant indicating the device phone type.  This
-     * indicates the type of radio used to transmit voice calls.
+     * indicates the type of radio used to transmit voice/data calls.
      *
      * @see #PHONE_TYPE_NONE
      * @see #PHONE_TYPE_GSM
@@ -2753,7 +2753,7 @@
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getPhoneType() {
-        if (!isVoiceCapable()) {
+        if (!isVoiceCapable() && !isDataCapable()) {
             return PHONE_TYPE_NONE;
         }
         return getCurrentPhoneType();
diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp
index c681ce9..529f84a 100644
--- a/tests/FlickerTests/ActivityEmbedding/Android.bp
+++ b/tests/FlickerTests/ActivityEmbedding/Android.bp
@@ -24,71 +24,6 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Begin to cleanup after CL merges
-
-filegroup {
-    name: "FlickerTestsOtherCommon-src",
-    srcs: ["src/**/ActivityEmbeddingTestBase.kt"],
-}
-
-filegroup {
-    name: "FlickerTestsOtherOpen-src",
-    srcs: ["src/**/open/*"],
-}
-
-filegroup {
-    name: "FlickerTestsOtherRotation-src",
-    srcs: ["src/**/rotation/*"],
-}
-
-java_library {
-    name: "FlickerTestsOtherCommon",
-    defaults: ["FlickerTestsDefault"],
-    srcs: [":FlickerTestsOtherCommon-src"],
-    static_libs: ["FlickerTestsBase"],
-}
-
-java_defaults {
-    name: "FlickerTestsOtherDefaults",
-    defaults: ["FlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.server.wm.flicker",
-    instrumentation_target_package: "com.android.server.wm.flicker",
-    test_config_template: "AndroidTestTemplate.xml",
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsOtherCommon",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "FlickerTestsOtherOpen",
-    defaults: ["FlickerTestsOtherDefaults"],
-    srcs: [":FlickerTestsOtherOpen-src"],
-}
-
-android_test {
-    name: "FlickerTestsOtherRotation",
-    defaults: ["FlickerTestsOtherDefaults"],
-    srcs: [":FlickerTestsOtherRotation-src"],
-}
-
-android_test {
-    name: "FlickerTestsOther",
-    defaults: ["FlickerTestsOtherDefaults"],
-    srcs: ["src/**/*"],
-    exclude_srcs: [
-        ":FlickerTestsOtherOpen-src",
-        ":FlickerTestsOtherRotation-src",
-        ":FlickerTestsOtherCommon-src",
-    ],
-}
-
-// End to cleanup after CL merges
-////////////////////////////////////////////////////////////////////////////////
-
 android_test {
     name: "FlickerTestsActivityEmbedding",
     defaults: ["FlickerTestsDefault"],
@@ -97,10 +32,7 @@
     instrumentation_target_package: "com.android.server.wm.flicker",
     test_config_template: "AndroidTestTemplate.xml",
     srcs: ["src/**/*"],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsOtherCommon",
-    ],
+    static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
 }
 
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/OWNERS
similarity index 100%
rename from tests/FlickerTests/ActivityEmbedding/OWNERS
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
index 519b429..f44e282 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
@@ -38,7 +38,7 @@
  * Setup: Launch A|B in split with B being the secondary activity. Transitions: Finish B and expect
  * A to become fullscreen.
  *
- * To run this test: `atest FlickerTestsOther:CloseSecondaryActivityInSplitTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:CloseSecondaryActivityInSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
index 4cd6d15b..7a76dd9 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
@@ -39,7 +39,7 @@
  * windows are equal in size. B is on the top and A is on the bottom. Transitions: Change the split
  * ratio to A:B=0.7:0.3, expect bounds change for both A and B.
  *
- * To run this test: `atest FlickerTestsOther:HorizontalSplitChangeRatioTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:HorizontalSplitChangeRatioTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 5df8b572..08b5f38 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -39,7 +39,7 @@
  * Setup: Launch A|B in split with B being the secondary activity. Transitions: A start C with
  * alwaysExpand=true, expect C to launch in fullscreen and cover split A|B.
  *
- * To run this test: `atest FlickerTestsOther:MainActivityStartsSecondaryWithAlwaysExpandTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:MainActivityStartsSecondaryWithAlwaysExpandTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
index 5009c7c..1f002a0 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -34,7 +34,7 @@
  * Test opening an activity that will launch another activity as ActivityEmbedding placeholder in
  * split.
  *
- * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingPlaceholderSplitTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:OpenActivityEmbeddingPlaceholderSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
index 6327d92..b78c3ec 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
@@ -34,7 +34,7 @@
 /**
  * Test opening a secondary activity that will split with the main activity.
  *
- * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingSecondaryToSplitTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:OpenActivityEmbeddingSecondaryToSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
index 78004cc..10167d71 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -39,7 +39,7 @@
  *
  * Transitions: Let B start C, expect C to cover B and end up in split A|C.
  *
- * To run this test: `atest FlickerTestsOther:OpenThirdActivityOverSplitTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:OpenThirdActivityOverSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index eed9225..a0b910b 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -41,7 +41,7 @@
  * Setup: Start from a split A|B. Transition: B enters PIP, observe the window first goes fullscreen
  * then shrink to the bottom right corner on screen.
  *
- * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:SecondaryActivityEnterPipTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
index f5e6c78..ea13f5f 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -36,7 +36,7 @@
  * Setup: Launch A|B in split with B being the secondary activity. Transitions: Rotate display, and
  * expect A and B to split evenly in new rotation.
  *
- * To run this test: `atest FlickerTestsOther:RotateSplitNoChangeTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:RotateSplitNoChangeTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/OWNERS
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
index 65a23e8..2a177d5 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
@@ -37,7 +37,7 @@
  * PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. Expect split
  * PlaceholderSecondary|PlaceholderPrimary covering split B|A.
  *
- * To run this test: `atest FlickerTestsOther:RTLStartSecondaryWithPlaceholderTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:RTLStartSecondaryWithPlaceholderTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index c3e1a1f..0ca8f37 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -47,7 +47,7 @@
  * Setup: Launch A|B in split and secondaryApp, return to home. Transitions: Let AE Split A|B enter
  * splitscreen with secondaryApp. Resulting in A|B|secondaryApp.
  *
- * To run this test: `atest FlickerTestsOther:EnterSystemSplitTest`
+ * To run this test: `atest FlickerTestsActivityEmbedding:EnterSystemSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/OWNERS
similarity index 100%
copy from tests/FlickerTests/ActivityEmbedding/OWNERS
copy to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/OWNERS
diff --git a/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index e19e1ce..56b718a 100644
--- a/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test app closes by pressing back button
  *
- * To run this test: `atest FlickerTests:CloseAppBackButtonTest`
+ * To run this test: `atest FlickerTestsAppClose:CloseAppBackButtonTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 47ed642..5deacaf 100644
--- a/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test app closes by pressing home button
  *
- * To run this test: `atest FlickerTests:CloseAppHomeButtonTest`
+ * To run this test: `atest FlickerTestsAppClose:CloseAppHomeButtonTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp
index b61739f..17d0f96 100644
--- a/tests/FlickerTests/AppLaunch/Android.bp
+++ b/tests/FlickerTests/AppLaunch/Android.bp
@@ -24,69 +24,13 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Begin to cleanup after CL merges
-
-filegroup {
-    name: "FlickerTestsAppLaunchCommon-src",
-    srcs: ["src/**/common/*"],
-}
-
-filegroup {
-    name: "FlickerTestsAppLaunch1-src",
-    srcs: ["src/**/OpenAppFrom*"],
-}
-
-java_library {
-    name: "FlickerTestsAppLaunchCommon",
-    defaults: ["FlickerTestsDefault"],
-    srcs: [":FlickerTestsAppLaunchCommon-src"],
-    static_libs: ["FlickerTestsBase"],
-}
-
-android_test {
-    name: "FlickerTestsAppLaunch1",
-    defaults: ["FlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [":FlickerTestsAppLaunch1-src"],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsAppLaunchCommon",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "FlickerTestsAppLaunch2",
-    defaults: ["FlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: ["src/**/*"],
-    exclude_srcs: [
-        ":FlickerTestsAppLaunchCommon-src",
-        ":FlickerTestsAppLaunch1-src",
-    ],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsAppLaunchCommon",
-    ],
-    data: ["trace_config/*"],
-}
-
-// End to cleanup after CL merges
-////////////////////////////////////////////////////////////////////////////////
-
 android_test {
     name: "FlickerTestsAppLaunch",
     defaults: ["FlickerTestsDefault"],
     manifest: "AndroidManifest.xml",
     test_config_template: "AndroidTestTemplate.xml",
     srcs: ["src/**/*"],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsAppLaunchCommon",
-    ],
+    static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
 }
 
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
index ffa90a3..01cdbb8 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
@@ -35,7 +35,7 @@
 /**
  * Test the back and forward transition between 2 activities.
  *
- * To run this test: `atest FlickerTests:ActivitiesTransitionTest`
+ * To run this test: `atest FlickerTestsAppLaunch:ActivitiesTransitionTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index 8c285bd..3d9321c 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -30,7 +30,7 @@
 /**
  * Test cold launching an app from launcher
  *
- * To run this test: `atest FlickerTests:OpenAppColdFromIcon`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenAppColdFromIcon`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index 57da05f..9207530 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -30,7 +30,7 @@
 /**
  * Test launching an app after cold opening camera
  *
- * To run this test: `atest FlickerTests:OpenAppAfterCameraTest`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenAppAfterCameraTest`
  *
  * Notes: Some default assertions are inherited [OpenAppTransition]
  */
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 267f282..cbe7c32 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -35,7 +35,7 @@
 /**
  * Test cold launching an app from launcher
  *
- * To run this test: `atest FlickerTests:OpenAppColdTest`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenAppColdTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 83065de..b2941e7 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -34,7 +34,7 @@
 /**
  * Test warm launching an app from launcher
  *
- * To run this test: `atest FlickerTests:OpenAppWarmTest`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenAppWarmTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 6e6a327..4048e0c 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -41,7 +41,7 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenAppNonResizeableTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 6d3eaeb..064c76f 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -35,7 +35,7 @@
 /**
  * Test launching an app from the recents app view (the overview)
  *
- * To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenAppFromOverviewTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index bec02d0..41423fd 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -41,7 +41,7 @@
 /**
  * Test cold launching camera from launcher by double pressing power button
  *
- * To run this test: `atest FlickerTests:OpenCameraOnDoubleClickPowerButton`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenCameraOnDoubleClickPowerButton`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
index e0aef8d..9d7a9c6 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -34,7 +34,7 @@
 /**
  * Test cold launching an app from launcher
  *
- * To run this test: `atest FlickerTests:OpenTransferSplashscreenAppFromLauncherTransition`
+ * To run this test: `atest FlickerTestsAppLaunch:OpenTransferSplashscreenAppFromLauncherTransition`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index f114499..7e2d472 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -42,7 +42,7 @@
 /**
  * Test the [android.app.ActivityOptions.makeCustomTaskAnimation].
  *
- * To run this test: `atest FlickerTests:OverrideTaskTransitionTest`
+ * To run this test: `atest FlickerTestsAppLaunch:OverrideTaskTransitionTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index a71599d..95e8126 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -49,7 +49,7 @@
 /**
  * Test the back and forward transition between 2 activities.
  *
- * To run this test: `atest FlickerTests:TaskTransitionTest`
+ * To run this test: `atest FlickerTestsAppLaunch:TaskTransitionTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
index f80e6b4..cba3d09 100644
--- a/tests/FlickerTests/IME/Android.bp
+++ b/tests/FlickerTests/IME/Android.bp
@@ -24,27 +24,6 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Begin to cleanup after CL merges
-
-filegroup {
-    name: "FlickerTestsImeCommon-src",
-    srcs: ["src/**/common/*"],
-}
-
-filegroup {
-    name: "FlickerTestsIme1-src",
-    srcs: ["src/**/Close*"],
-}
-
-filegroup {
-    name: "FlickerTestsIme2-src",
-    srcs: ["src/**/ShowImeOnAppStart*"],
-}
-
-// End to cleanup after CL merges
-////////////////////////////////////////////////////////////////////////////////
-
 android_test {
     name: "FlickerTestsIme",
     defaults: ["FlickerTestsDefault"],
@@ -60,67 +39,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Begin to cleanup after CL merges
-
-java_library {
-    name: "FlickerTestsImeCommon",
-    defaults: ["FlickerTestsDefault"],
-    srcs: [":FlickerTestsImeCommon-src"],
-    static_libs: ["FlickerTestsBase"],
-}
-
-android_test {
-    name: "FlickerTestsIme1",
-    defaults: ["FlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    test_config_template: "AndroidTestTemplate.xml",
-    test_suites: [
-        "device-tests",
-        "device-platinum-tests",
-    ],
-    srcs: [":FlickerTestsIme1-src"],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsImeCommon",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "FlickerTestsIme2",
-    defaults: ["FlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [":FlickerTestsIme2-src"],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsImeCommon",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "FlickerTestsIme3",
-    defaults: ["FlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: ["src/**/*"],
-    exclude_srcs: [
-        ":FlickerTestsIme1-src",
-        ":FlickerTestsIme2-src",
-        ":FlickerTestsImeCommon-src",
-    ],
-    static_libs: [
-        "FlickerTestsBase",
-        "FlickerTestsImeCommon",
-    ],
-    data: ["trace_config/*"],
-}
-
-// End to cleanup after CL merges
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
 // Begin breakdowns for FlickerTestsIme module
 
 test_module_config {
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 2b6ddcb..48ca36f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -33,7 +33,7 @@
 import org.junit.runners.Parameterized
 
 /**
- * To run this test: `atest FlickerTestsIme1:CloseImeOnDismissPopupDialogTest`
+ * To run this test: `atest FlickerTestsIme:CloseImeOnDismissPopupDialogTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 0344197..e3f3aca 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -34,7 +34,7 @@
 
 /**
  * Test IME window closing to home transitions.
- * To run this test: `atest FlickerTestsIme1:CloseImeOnGoHomeTest`
+ * To run this test: `atest FlickerTestsIme:CloseImeOnGoHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index fde1373..3509e5b 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -42,7 +42,7 @@
  *
  * More details on b/190352379
  *
- * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartOnGoHomeTest`
+ * To run this test: `atest FlickerTestsIme:CloseImeShownOnAppStartOnGoHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index ed6e8df..53d7a3f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -43,7 +43,7 @@
  *
  * More details on b/190352379
  *
- * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartToAppOnPressBackTest`
+ * To run this test: `atest FlickerTestsIme:CloseImeShownOnAppStartToAppOnPressBackTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 522c68b..4bc2705 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -35,7 +35,7 @@
 
 /**
  * Test IME window closing back to app window transitions.
- * To run this test: `atest FlickerTestsIme1:CloseImeToAppOnPressBackTest`
+ * To run this test: `atest FlickerTestsIme:CloseImeToAppOnPressBackTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 05771e8..6117bb0 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -40,7 +40,7 @@
  * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify
  * there is no flickering when back to the simple activity without requesting IME to show.
  *
- * To run this test: `atest FlickerTestsIme1:CloseImeToHomeOnFinishActivityTest`
+ * To run this test: `atest FlickerTestsIme:CloseImeToHomeOnFinishActivityTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 336fe6f..9b8d86d 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -37,7 +37,7 @@
 
 /**
  * Test IME window shown on the app with fixing portrait orientation.
- * To run this test: `atest FlickerTestsIme2:OpenImeWindowToFixedPortraitAppTest`
+ * To run this test: `atest FlickerTestsIme:OpenImeWindowToFixedPortraitAppTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index 34a7085..f806fae 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -34,7 +34,7 @@
 
 /**
  * Test IME window opening transitions.
- * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 7c72c31..cc19f62 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -36,7 +36,7 @@
 
 /**
  * Test IME windows switching with 2-Buttons or gestural navigation.
- * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index fe5320c..4a4d372 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -36,7 +36,7 @@
 /**
  * Launch an app that automatically displays the IME
  *
- * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeOnAppStartWhenLaunchingAppTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
index 82e53c8..d47e7ad 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -36,7 +36,7 @@
 
 /**
  * Test IME window closing on lock and opening on screen unlock.
- * To run this test: `atest FlickerTestsIme2:ShowImeOnUnlockScreenTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeOnUnlockScreenTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 9eaf998..47bf324 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -33,7 +33,7 @@
 
 /**
  * Test IME window opening transitions.
- * To run this test: `atest FlickerTestsIme2:ShowImeWhenFocusingOnInputFieldTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeWhenFocusingOnInputFieldTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 7186a2c..e3118b4 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -41,7 +41,7 @@
 
 /**
  * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
- * To run this test: `atest FlickerTestsIme2:ShowImeWhileDismissingThemedPopupDialogTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeWhileDismissingThemedPopupDialogTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index eb63e49..064c07e 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -39,7 +39,7 @@
 
 /**
  * Test IME window layer will be associated with the app task when going to the overview screen.
- * To run this test: `atest FlickerTestsIme2:ShowImeWhileEnteringOverviewTest`
+ * To run this test: `atest FlickerTestsIme:ShowImeWhileEnteringOverviewTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 9bb62e1..1a32f20 100644
--- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -38,7 +38,7 @@
 /**
  * Test quick switching back to previous app from last opened app
  *
- * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ * To run this test: `atest FlickerTestsQuickswitch:QuickSwitchBetweenTwoAppsBackTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 491b994..d82dddd 100644
--- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -37,7 +37,7 @@
 /**
  * Test quick switching back to previous app from last opened app
  *
- * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest`
+ * To run this test: `atest FlickerTestsQuickswitch:QuickSwitchBetweenTwoAppsForwardTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index de54c95..ab36628 100644
--- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -38,7 +38,7 @@
 /**
  * Test quick switching to last opened app from launcher
  *
- * To run this test: `atest FlickerTests:QuickSwitchFromLauncherTest`
+ * To run this test: `atest FlickerTestsQuickswitch:QuickSwitchFromLauncherTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 05ab364..49e2553 100644
--- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -48,7 +48,7 @@
  *     Stop tracing
  * ```
  *
- * To run this test: `atest FlickerTests:ChangeAppRotationTest`
+ * To run this test: `atest FlickerTestsRotation:ChangeAppRotationTest`
  *
  * To run only the presubmit assertions add: `--
  *
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index a413628..d7f91e0 100644
--- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -55,7 +55,7 @@
  *     Stop tracing
  * ```
  *
- * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
+ * To run this test: `atest FlickerTestsRotation:SeamlessAppRotationTest`
  *
  * To run only the presubmit assertions add: `--
  *