Merge "add namespace to native mapping" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index cbb6566..b5f398b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -58,7 +58,6 @@
     ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
     ":com.android.net.flags-aconfig-java{.generated_srcjars}",
     ":device_policy_aconfig_flags_lib{.generated_srcjars}",
-    ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
     ":surfaceflinger_flags_java_lib{.generated_srcjars}",
     ":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
     ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 6c83add..8b55e07 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,10 +13,6 @@
     name: "service-jobscheduler",
     installable: true,
 
-    defaults: [
-        "service-jobscheduler-aconfig-libraries",
-    ],
-
     srcs: [
         "java/**/*.java",
         ":framework-jobscheduler-shared-srcs",
@@ -32,6 +28,7 @@
 
     static_libs: [
         "modules-utils-fastxmlserializer",
+        "service-jobscheduler-job.flags-aconfig-java",
     ],
 
     // Rename classes shared with the framework
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 7d8a363..3ba7aa2 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -10,7 +10,6 @@
 java_aconfig_library {
     name: "service-jobscheduler-deviceidle.flags-aconfig-java",
     aconfig_declarations: "service-deviceidle.flags-aconfig",
-    defaults: ["framework-minus-apex-aconfig-java-defaults"],
     visibility: ["//frameworks/base:__subpackages__"],
 }
 
@@ -26,21 +25,5 @@
 java_aconfig_library {
     name: "service-jobscheduler-job.flags-aconfig-java",
     aconfig_declarations: "service-job.flags-aconfig",
-    defaults: ["framework-minus-apex-aconfig-java-defaults"],
-    visibility: ["//frameworks/base:__subpackages__"],
-}
-
-service_jobscheduler_aconfig_srcjars = [
-    ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
-    ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
-]
-
-// Aconfig declarations and libraries for the core framework
-java_defaults {
-    name: "service-jobscheduler-aconfig-libraries",
-    // Add java_aconfig_libraries to here to add them to the core framework
-    srcs: service_jobscheduler_aconfig_srcjars,
-    // Add aconfig-annotations-lib as a dependency for the optimization
-    libs: ["aconfig-annotations-lib"],
     visibility: ["//frameworks/base:__subpackages__"],
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a7ea753..afea9b5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4598,6 +4598,14 @@
     field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
   }
 
+  public final class VirtualDisplayConfig implements android.os.Parcelable {
+    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+  }
+
+  public static final class VirtualDisplayConfig.Builder {
+    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+  }
+
 }
 
 package android.hardware.hdmi {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 26f1c4b..9c279c3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -2563,11 +2563,14 @@
         public static final int TYPE_LOCKSCREEN = 3;
         /** Launched from recents gesture handler. */
         public static final int TYPE_RECENTS_ANIMATION = 4;
+        /** Launched from desktop's transition handler. */
+        public static final int TYPE_DESKTOP_ANIMATION = 5;
 
         @IntDef(prefix = { "TYPE_" }, value = {
                 TYPE_LAUNCHER,
                 TYPE_NOTIFICATION,
                 TYPE_LOCKSCREEN,
+                TYPE_DESKTOP_ANIMATION
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SourceType {}
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 0b8d1df..6b558d0 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -184,6 +184,14 @@
     public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
 
     /**
+     * Flag for {@link RestoreSet#backupTransportFlags} to indicate if restore should be skipped
+     * for apps that have already been launched.
+     *
+     * @hide
+     */
+    public static final int FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS = 1 << 2;
+
+    /**
      * Flag for {@link BackupDataOutput#getTransportFlags()} and
      * {@link FullBackupDataOutput#getTransportFlags()} only.
      *
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 97a7aa4..0d73e44 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -37,6 +37,7 @@
 import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
 import android.content.ComponentName;
 import android.content.Context;
+import android.hardware.display.VirtualDisplayConfig;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SharedMemory;
@@ -326,8 +327,8 @@
      * support home activities.
      *
      * @see Builder#setHomeComponent
+     * @see VirtualDisplayConfig#isHomeSupported()
      */
-    // TODO(b/297168328): Link to the relevant API for creating displays with home support.
     @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
     @Nullable
     public ComponentName getHomeComponent() {
@@ -737,8 +738,9 @@
          *
          * @param homeComponent The component name to be used as home. If unset, then the system-
          *   default secondary home activity will be used.
+         *
+         * @see VirtualDisplayConfig#isHomeSupported()
          */
-        // TODO(b/297168328): Link to the relevant API for creating displays with home support.
         @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
         @NonNull
         public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 22e3938..7388b5b 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -18,10 +18,12 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.hardware.display.DisplayManager.VirtualDisplayFlag;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
@@ -55,6 +57,7 @@
     private final boolean mWindowManagerMirroringEnabled;
     private ArraySet<String> mDisplayCategories = null;
     private final float mRequestedRefreshRate;
+    private final boolean mIsHomeSupported;
 
     private VirtualDisplayConfig(
             @NonNull String name,
@@ -67,7 +70,8 @@
             int displayIdToMirror,
             boolean windowManagerMirroringEnabled,
             @NonNull ArraySet<String> displayCategories,
-            float requestedRefreshRate) {
+            float requestedRefreshRate,
+            boolean isHomeSupported) {
         mName = name;
         mWidth = width;
         mHeight = height;
@@ -79,6 +83,7 @@
         mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
         mDisplayCategories = displayCategories;
         mRequestedRefreshRate = requestedRefreshRate;
+        mIsHomeSupported = isHomeSupported;
     }
 
     /**
@@ -157,6 +162,18 @@
     }
 
     /**
+     * Whether this virtual display supports showing home activity and wallpaper.
+     *
+     * @see Builder#setHomeSupported
+     * @hide
+     */
+    @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
+    @SystemApi
+    public boolean isHomeSupported() {
+        return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported;
+    }
+
+    /**
      * Returns the display categories.
      *
      * @see Builder#setDisplayCategories
@@ -189,6 +206,7 @@
         dest.writeBoolean(mWindowManagerMirroringEnabled);
         dest.writeArraySet(mDisplayCategories);
         dest.writeFloat(mRequestedRefreshRate);
+        dest.writeBoolean(mIsHomeSupported);
     }
 
     @Override
@@ -213,7 +231,8 @@
                 && mDisplayIdToMirror == that.mDisplayIdToMirror
                 && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
                 && Objects.equals(mDisplayCategories, that.mDisplayCategories)
-                && mRequestedRefreshRate == that.mRequestedRefreshRate;
+                && mRequestedRefreshRate == that.mRequestedRefreshRate
+                && mIsHomeSupported == that.mIsHomeSupported;
     }
 
     @Override
@@ -221,7 +240,7 @@
         int hashCode = Objects.hash(
                 mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
                 mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
-                mRequestedRefreshRate);
+                mRequestedRefreshRate, mIsHomeSupported);
         return hashCode;
     }
 
@@ -240,6 +259,7 @@
                 + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
                 + " mDisplayCategories=" + mDisplayCategories
                 + " mRequestedRefreshRate=" + mRequestedRefreshRate
+                + " mIsHomeSupported=" + mIsHomeSupported
                 + ")";
     }
 
@@ -255,6 +275,7 @@
         mWindowManagerMirroringEnabled = in.readBoolean();
         mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
         mRequestedRefreshRate = in.readFloat();
+        mIsHomeSupported = in.readBoolean();
     }
 
     @NonNull
@@ -286,6 +307,7 @@
         private boolean mWindowManagerMirroringEnabled = false;
         private ArraySet<String> mDisplayCategories = new ArraySet<>();
         private float mRequestedRefreshRate = 0.0f;
+        private boolean mIsHomeSupported = false;
 
         /**
          * Creates a new Builder.
@@ -422,6 +444,27 @@
         }
 
         /**
+         * Sets whether this display supports showing home activities and wallpaper.
+         *
+         * <p>If set to {@code true}, then the home activity relevant to this display will be
+         * automatically launched upon the display creation.</p>
+         *
+         * <p>Note: setting to {@code true} requires the display to be trusted. If the display is
+         * not trusted, this property is ignored.</p>
+         *
+         * @param isHomeSupported whether home activities are supported on the display
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+         * @hide
+         */
+        @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
+        @SystemApi
+        @NonNull
+        public Builder setHomeSupported(boolean isHomeSupported) {
+            mIsHomeSupported = isHomeSupported;
+            return this;
+        }
+
+        /**
          * Builds the {@link VirtualDisplayConfig} instance.
          */
         @NonNull
@@ -437,7 +480,8 @@
                     mDisplayIdToMirror,
                     mWindowManagerMirroringEnabled,
                     mDisplayCategories,
-                    mRequestedRefreshRate);
+                    mRequestedRefreshRate,
+                    mIsHomeSupported);
         }
     }
 }
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 7e388d4..59ec605 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -157,6 +157,11 @@
     public Matrix transform;
 
     /**
+     * The alpha value returned from SurfaceFlinger. This will be ignored if passed as input data.
+     */
+    public float alpha;
+
+    /**
      * The input token for the window to which focus should be transferred when this input window
      * can be successfully focused. If null, this input window will not transfer its focus to
      * any other window.
@@ -199,6 +204,7 @@
         }
         focusTransferTarget = other.focusTransferTarget;
         contentSize = new Size(other.contentSize.getWidth(), other.contentSize.getHeight());
+        alpha = other.alpha;
     }
 
     @Override
@@ -212,6 +218,7 @@
                 .append(", displayId=").append(displayId)
                 .append(", isClone=").append((inputConfig & InputConfig.CLONE) != 0)
                 .append(", contentSize=").append(contentSize)
+                .append(", alpha=").append(alpha)
                 .toString();
 
     }
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index c3d21a4..ae23942 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -74,6 +74,7 @@
     jfieldID transform;
     jfieldID windowToken;
     jfieldID focusTransferTarget;
+    jfieldID alpha;
 } gInputWindowHandleClassInfo;
 
 static struct {
@@ -325,6 +326,8 @@
     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken,
                         javaObjectForIBinder(env, windowInfo.windowToken));
 
+    env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha);
+
     return inputWindowHandle;
 }
 
@@ -446,6 +449,8 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl, clazz,
             "touchableRegionSurfaceControl", "Ljava/lang/ref/WeakReference;");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F");
+
     jclass surfaceControlClazz;
     FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl");
     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b065611..f19acbe 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -811,6 +811,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1325565952": {
+      "message": "Attempted to get home support flag of a display that does not exist: %d",
+      "level": "WARN",
+      "group": "WM_ERROR",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-1323783276": {
       "message": "performEnableScreen: bootFinished() failed.",
       "level": "WARN",
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 29bdd5c..c366ccd 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -35,3 +35,10 @@
     description: "Enables taskbar / navbar unification"
     bug: "309671494"
 }
+
+flag {
+    name: "enable_pip_ui_state_on_entering"
+    namespace: "multitasking"
+    description: "Enables PiP UI state callback on entering"
+    bug: "303718131"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index ab61a48..5143d41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -115,6 +115,19 @@
         b.setParent(sc);
     }
 
+    /**
+     * Re-parents the provided surface to the leash of the provided display.
+     *
+     * @param displayId the display area to reparent to.
+     * @param sc the surface to be reparented.
+     * @param t a {@link SurfaceControl.Transaction} in which to reparent.
+     */
+    public void reparentToDisplayArea(int displayId, SurfaceControl sc,
+                                      SurfaceControl.Transaction t) {
+        final SurfaceControl displayAreaLeash = mLeashes.get(displayId);
+        t.reparent(sc, displayAreaLeash);
+    }
+
     public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) {
         final SurfaceControl sc = mLeashes.get(displayId);
         if (sc == null) {
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 47769a8..71bf487 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
@@ -59,6 +59,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
 import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
 import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
@@ -498,6 +499,7 @@
             EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
             ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
+            DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
             @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
             LaunchAdjacentController launchAdjacentController,
             RecentsTransitionHandler recentsTransitionHandler,
@@ -506,8 +508,19 @@
         return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
                 displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
                 transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
-                toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
-                launchAdjacentController, recentsTransitionHandler, mainExecutor);
+                toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler,
+                desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler,
+                mainExecutor);
+    }
+
+    @WMSingleton
+    @Provides
+    static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
+            Context context,
+            Transitions transitions,
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+        return new DragToDesktopTransitionHandler(context, transitions,
+                rootTaskDisplayAreaOrganizer);
     }
 
     @WMSingleton
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 8e12991..4a9ea6f 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
@@ -59,6 +59,7 @@
 import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
@@ -92,6 +93,7 @@
         private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
         private val toggleResizeDesktopTaskTransitionHandler:
         ToggleResizeDesktopTaskTransitionHandler,
+        private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
         private val desktopModeTaskRepository: DesktopModeTaskRepository,
         private val launchAdjacentController: LaunchAdjacentController,
         private val recentsTransitionHandler: RecentsTransitionHandler,
@@ -110,6 +112,20 @@
             launchAdjacentController.launchAdjacentEnabled = !hasVisibleFreeformTasks
         }
     }
+    private val dragToDesktopStateListener = object : DragToDesktopStateListener {
+        override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) {
+            removeVisualIndicator(tx)
+        }
+
+        override fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) {
+            removeVisualIndicator(tx)
+        }
+
+        private fun removeVisualIndicator(tx: SurfaceControl.Transaction) {
+            visualIndicator?.releaseVisualIndicator(tx)
+            visualIndicator = null
+        }
+    }
 
     private val transitionAreaHeight
         get() = context.resources.getDimensionPixelSize(
@@ -122,9 +138,7 @@
         )
 
     private var recentsAnimationRunning = false
-
-    // This is public to avoid cyclic dependency; it is set by SplitScreenController
-    lateinit var splitScreenController: SplitScreenController
+    private lateinit var splitScreenController: SplitScreenController
 
     init {
         desktopMode = DesktopModeImpl()
@@ -143,7 +157,7 @@
         )
         transitions.addHandler(this)
         desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
-
+        dragToDesktopTransitionHandler.setDragToDesktopStateListener(dragToDesktopStateListener)
         recentsTransitionHandler.addTransitionStateListener(
             object : RecentsTransitionStateListener {
                 override fun onAnimationStateChanged(running: Boolean) {
@@ -158,6 +172,12 @@
         )
     }
 
+    /** Setter needed to avoid cyclic dependency. */
+    fun setSplitScreenController(controller: SplitScreenController) {
+        splitScreenController = controller
+        dragToDesktopTransitionHandler.setSplitScreenController(controller)
+    }
+
     /** Show all tasks, that are part of the desktop, on top of launcher */
     fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
         KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
@@ -248,56 +268,43 @@
     }
 
     /**
-     * The first part of the animated move to desktop transition. Applies the changes to move task
-     * to desktop mode and sets the taskBounds to the passed in bounds, startBounds. This is
-     * followed with a call to {@link finishMoveToDesktop} or {@link cancelMoveToDesktop}.
+     * The first part of the animated drag to desktop transition. This is
+     * followed with a call to [finalizeDragToDesktop] or [cancelDragToDesktop].
      */
-    fun startMoveToDesktop(
+    fun startDragToDesktop(
             taskInfo: RunningTaskInfo,
-            startBounds: Rect,
-            dragToDesktopValueAnimator: MoveToDesktopAnimator
+            dragToDesktopValueAnimator: MoveToDesktopAnimator,
+            windowDecor: DesktopModeWindowDecoration
     ) {
         KtProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: startMoveToDesktop taskId=%d",
-            taskInfo.taskId
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopTasksController: startDragToDesktop taskId=%d",
+                taskInfo.taskId
+        )
+        dragToDesktopTransitionHandler.startDragToDesktopTransition(
+                taskInfo.taskId,
+                dragToDesktopValueAnimator,
+                windowDecor
+        )
+    }
+
+    /**
+     * The second part of the animated drag to desktop transition, called after
+     * [startDragToDesktop].
+     */
+    private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
+        KtProtoLog.v(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopTasksController: finalizeDragToDesktop taskId=%d",
+                taskInfo.taskId
         )
         val wct = WindowContainerTransaction()
         exitSplitIfApplicable(wct, taskInfo)
         moveHomeTaskToFront(wct)
-        addMoveToDesktopChanges(wct, taskInfo)
-        wct.setBounds(taskInfo.token, startBounds)
-
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            enterDesktopTaskTransitionHandler.startMoveToDesktop(wct, dragToDesktopValueAnimator,
-                    mOnAnimationFinishedCallback)
-        } else {
-            shellTaskOrganizer.applyTransaction(wct)
-        }
-    }
-
-    /**
-     * The second part of the animated move to desktop transition, called after
-     * {@link startMoveToDesktop}. Brings apps to front and sets freeform task bounds.
-     */
-    private fun finalizeMoveToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
-        KtProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: finalizeMoveToDesktop taskId=%d",
-            taskInfo.taskId
-        )
-        val wct = WindowContainerTransaction()
         bringDesktopAppsToFront(taskInfo.displayId, wct)
         addMoveToDesktopChanges(wct, taskInfo)
         wct.setBounds(taskInfo.token, freeformBounds)
-
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            enterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct,
-                    mOnAnimationFinishedCallback)
-        } else {
-            shellTaskOrganizer.applyTransaction(wct)
-            releaseVisualIndicator()
-        }
+        dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
     }
 
     /**
@@ -353,40 +360,40 @@
     }
 
     private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
-        if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
-            splitScreenController.prepareExitSplitScreen(wct,
-                splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+        if (splitScreenController.isTaskInSplitScreen(taskInfo.taskId)) {
+            splitScreenController.prepareExitSplitScreen(
+                    wct,
+                    splitScreenController.getStageOfTask(taskInfo.taskId),
+                    EXIT_REASON_ENTER_DESKTOP
+            )
+            getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
+                wct.removeTask(otherTaskInfo.token)
+            }
         }
     }
 
+    private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? {
+        val remainingTaskPosition: Int =
+                if (splitScreenController.getSplitPosition(taskId)
+                        == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+                    SPLIT_POSITION_TOP_OR_LEFT
+                } else {
+                    SPLIT_POSITION_BOTTOM_OR_RIGHT
+                }
+        return splitScreenController.getTaskInfo(remainingTaskPosition)
+    }
+
     /**
-     * The second part of the animated move to desktop transition, called after
-     * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
-     * and released back into status bar area.
+     * The second part of the animated drag to desktop transition, called after
+     * [startDragToDesktop].
      */
-    fun cancelMoveToDesktop(task: RunningTaskInfo, moveToDesktopAnimator: MoveToDesktopAnimator) {
+    fun cancelDragToDesktop(task: RunningTaskInfo) {
         KtProtoLog.v(
             WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: cancelMoveToDesktop taskId=%d",
+            "DesktopTasksController: cancelDragToDesktop taskId=%d",
             task.taskId
         )
-        val wct = WindowContainerTransaction()
-        wct.setBounds(task.token, Rect())
-
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct,
-                    moveToDesktopAnimator) { t ->
-                val callbackWCT = WindowContainerTransaction()
-                visualIndicator?.releaseVisualIndicator(t)
-                visualIndicator = null
-                addMoveToFullscreenChanges(callbackWCT, task)
-                transitions.startTransition(TRANSIT_CHANGE, callbackWCT, null /* handler */)
-            }
-        } else {
-            addMoveToFullscreenChanges(wct, task)
-            shellTaskOrganizer.applyTransaction(wct)
-            releaseVisualIndicator()
-        }
+        dragToDesktopTransitionHandler.cancelDragToDesktopTransition()
     }
 
     private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) {
@@ -966,6 +973,11 @@
             visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
                     displayController, context, taskSurface, shellTaskOrganizer,
                     rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR)
+            // TODO(b/301106941): don't show the indicator until the drag-to-desktop animation has
+            // started, or it'll be visible too early on top of the task surface, especially in
+            // the cancel-early case. Also because it shouldn't even be shown in the cancel-early
+            // case since its dismissal is tied to the cancel animation end, which doesn't even run
+            // in cancel-early.
             visualIndicator?.createIndicatorWithAnimatedBounds()
         }
         val indicator = visualIndicator ?: return
@@ -988,7 +1000,7 @@
             taskInfo: RunningTaskInfo,
             freeformBounds: Rect
     ) {
-        finalizeMoveToDesktop(taskInfo, freeformBounds)
+        finalizeDragToDesktop(taskInfo, freeformBounds)
     }
 
     private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
new file mode 100644
index 0000000..75d27d9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -0,0 +1,543 @@
+package com.android.wm.shell.desktopmode
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.RectEvaluator
+import android.animation.ValueAnimator
+import android.app.ActivityOptions
+import android.app.ActivityOptions.SourceInfo
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
+import android.app.PendingIntent.FLAG_MUTABLE
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.FILL_IN_COMPONENT
+import android.graphics.Rect
+import android.os.IBinder
+import android.os.SystemClock
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TransitionHandler
+import com.android.wm.shell.util.TransitionUtil
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator.Companion.DRAG_FREEFORM_SCALE
+import java.util.function.Supplier
+
+/**
+ * Handles the transition to enter desktop from fullscreen by dragging on the handle bar. It also
+ * handles the cancellation case where the task is dragged back to the status bar area in the same
+ * gesture.
+ */
+class DragToDesktopTransitionHandler(
+        private val context: Context,
+        private val transitions: Transitions,
+        private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+        private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+) : TransitionHandler {
+
+    constructor(
+            context: Context,
+            transitions: Transitions,
+            rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    ) : this(
+            context,
+            transitions,
+            rootTaskDisplayAreaOrganizer,
+            Supplier { SurfaceControl.Transaction() }
+    )
+
+    private val rectEvaluator = RectEvaluator(Rect())
+    private val launchHomeIntent = Intent(Intent.ACTION_MAIN)
+            .addCategory(Intent.CATEGORY_HOME)
+
+    private var dragToDesktopStateListener: DragToDesktopStateListener? = null
+    private var splitScreenController: SplitScreenController? = null
+    private var transitionState: TransitionState? = null
+
+    /** Sets a listener to receive callback about events during the transition animation. */
+    fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
+        dragToDesktopStateListener = listener
+    }
+
+    /** Setter needed to avoid cyclic dependency. */
+    fun setSplitScreenController(controller: SplitScreenController) {
+        splitScreenController = controller
+    }
+
+    /**
+     * Starts a transition that performs a transient launch of Home so that Home is brought to the
+     * front while still keeping the currently focused task that is being dragged resumed. This
+     * allows the animation handler to reorder the task to the front and to scale it with the
+     * gesture into the desktop area with the Home and wallpaper behind it.
+     *
+     * Note that the transition handler for this transition doesn't call the finish callback until
+     * after one of the "end" or "cancel" transitions is merged into this transition.
+     */
+    fun startDragToDesktopTransition(
+            taskId: Int,
+            dragToDesktopAnimator: MoveToDesktopAnimator,
+            windowDecoration: DesktopModeWindowDecoration
+    ) {
+        if (transitionState != null) {
+            error("A drag to desktop is already in progress")
+        }
+
+        val options = ActivityOptions.makeBasic().apply {
+            setTransientLaunch()
+            setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
+        }
+        val pendingIntent = PendingIntent.getActivity(
+                context,
+                0 /* requestCode */,
+                launchHomeIntent,
+                FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT
+        )
+        val wct = WindowContainerTransaction()
+        wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
+        val startTransitionToken = transitions
+                .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
+
+        transitionState = if (isSplitTask(taskId)) {
+            TransitionState.FromSplit(
+                    draggedTaskId = taskId,
+                    dragAnimator = dragToDesktopAnimator,
+                    windowDecoration = windowDecoration,
+                    startTransitionToken = startTransitionToken
+            )
+        } else {
+            TransitionState.FromFullscreen(
+                    draggedTaskId = taskId,
+                    dragAnimator = dragToDesktopAnimator,
+                    windowDecoration = windowDecoration,
+                    startTransitionToken = startTransitionToken
+            )
+        }
+    }
+
+    /**
+     * Starts a transition that "finishes" the drag to desktop gesture. This transition is intended
+     * to merge into the "start" transition and is the one that actually applies the bounds and
+     * windowing mode changes to the dragged task. This is called when the dragged task is released
+     * inside the desktop drop zone.
+     */
+    fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
+        transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
+    }
+
+    /**
+     * Starts a transition that "cancels" the drag to desktop gesture. This transition is intended
+     * to merge into the "start" transition and it restores the transient state that was used to
+     * launch the Home task over the dragged task. This is called when the dragged task is released
+     * outside the desktop drop zone and is instead dropped back into the status bar region that
+     * means the user wants to remain in their current windowing mode.
+     */
+    fun cancelDragToDesktopTransition() {
+        val state = requireTransitionState()
+        state.cancelled = true
+        if (state.draggedTaskChange != null) {
+            // Regular case, transient launch of Home happened as is waiting for the cancel
+            // transient to start and merge. Animate the cancellation (scale back to original
+            // bounds) first before actually starting the cancel transition so that the wallpaper
+            // is visible behind the animating task.
+            startCancelAnimation()
+        } else {
+            // There's no dragged task, this can happen when the "cancel" happened too quickly
+            // before the "start" transition is even ready (like on a fling gesture). The
+            // "shrink" animation didn't even start, so there's no need to animate the "cancel".
+            // We also don't want to start the cancel transition yet since we don't have
+            // enough info to restore the order. We'll check for the cancelled state flag when
+            // the "start" animation is ready and cancel from #startAnimation instead.
+        }
+    }
+
+    override fun startAnimation(
+            transition: IBinder,
+            info: TransitionInfo,
+            startTransaction: SurfaceControl.Transaction,
+            finishTransaction: SurfaceControl.Transaction,
+            finishCallback: Transitions.TransitionFinishCallback
+    ): Boolean {
+        val state = requireTransitionState()
+
+        val isStartDragToDesktop = info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP &&
+                transition == state.startTransitionToken
+        if (!isStartDragToDesktop) {
+            return false
+        }
+
+        // Layering: non-wallpaper, non-home tasks excluding the dragged task go at the bottom,
+        // then Home on top of that, wallpaper on top of that and finally the dragged task on top
+        // of everything.
+        val appLayers = info.changes.size
+        val homeLayers = info.changes.size * 2
+        val wallpaperLayers = info.changes.size * 3
+        val dragLayer = wallpaperLayers
+        val leafTaskFilter = TransitionUtil.LeafTaskFilter()
+        info.changes.withIndex().forEach { (i, change) ->
+            if (TransitionUtil.isWallpaper(change)) {
+                val layer = wallpaperLayers - i
+                startTransaction.apply {
+                    setLayer(change.leash, layer)
+                    show(change.leash)
+                }
+            } else if (isHomeChange(change)) {
+                state.homeToken = change.container
+                val layer = homeLayers - i
+                startTransaction.apply {
+                    setLayer(change.leash, layer)
+                    show(change.leash)
+                }
+            } else if (TransitionInfo.isIndependent(change, info)) {
+                // Root.
+                when (state) {
+                    is TransitionState.FromSplit -> {
+                        state.splitRootChange = change
+                        val layer = if (!state.cancelled) {
+                            // Normal case, split root goes to the bottom behind everything else.
+                            appLayers - i
+                        } else {
+                            // Cancel-early case, pretend nothing happened so split root stays top.
+                            dragLayer
+                        }
+                        startTransaction.apply {
+                            setLayer(change.leash, layer)
+                            show(change.leash)
+                        }
+                    }
+                    is TransitionState.FromFullscreen -> {
+                        if (change.taskInfo?.taskId == state.draggedTaskId) {
+                            state.draggedTaskChange = change
+                            val bounds = change.endAbsBounds
+                            startTransaction.apply {
+                                setLayer(change.leash, dragLayer)
+                                setWindowCrop(change.leash, bounds.width(), bounds.height())
+                                show(change.leash)
+                            }
+                        } else {
+                            throw IllegalStateException("Expected root to be dragged task")
+                        }
+                    }
+                }
+            } else if (leafTaskFilter.test(change)) {
+                // When dragging one of the split tasks, the dragged leaf needs to be re-parented
+                // so that it can be layered separately from the rest of the split root/stages.
+                // The split root including the other split side was layered behind the wallpaper
+                // and home while the dragged split needs to be layered in front of them.
+                // Do not do this in the cancel-early case though, since in that case nothing should
+                // happen on screen so the layering will remain the same as if no transition
+                // occurred.
+                if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) {
+                    state.draggedTaskChange = change
+                    taskDisplayAreaOrganizer.reparentToDisplayArea(
+                            change.endDisplayId, change.leash, startTransaction)
+                    val bounds = change.endAbsBounds
+                    startTransaction.apply {
+                        setLayer(change.leash, dragLayer)
+                        setWindowCrop(change.leash, bounds.width(), bounds.height())
+                        show(change.leash)
+                    }
+                }
+            }
+        }
+        state.startTransitionFinishCb = finishCallback
+        state.startTransitionFinishTransaction = finishTransaction
+        startTransaction.apply()
+
+        if (!state.cancelled) {
+            // Normal case, start animation to scale down the dragged task. It'll also be moved to
+            // follow the finger and when released we'll start the next phase/transition.
+            state.dragAnimator.startAnimation()
+        } else {
+            // Cancel-early case, the state was flagged was cancelled already, which means the
+            // gesture ended in the cancel region. This can happen even before the start transition
+            // is ready/animate here when cancelling quickly like with a fling. There's no point
+            // in starting the scale down animation that we would scale up anyway, so just jump
+            // directly into starting the cancel transition to restore WM order. Surfaces should
+            // not move as if no transition happened.
+            startCancelDragToDesktopTransition()
+        }
+        return true
+    }
+
+    override fun mergeAnimation(
+            transition: IBinder,
+            info: TransitionInfo,
+            t: SurfaceControl.Transaction,
+            mergeTarget: IBinder,
+            finishCallback: Transitions.TransitionFinishCallback
+    ) {
+        val state = requireTransitionState()
+        val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
+                transition == state.cancelTransitionToken &&
+                mergeTarget == state.startTransitionToken
+        val isEndTransition = info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP &&
+                mergeTarget == state.startTransitionToken
+
+        val startTransactionFinishT = state.startTransitionFinishTransaction
+                ?: error("Start transition expected to be waiting for merge but wasn't")
+        val startTransitionFinishCb = state.startTransitionFinishCb
+                ?: error("Start transition expected to be waiting for merge but wasn't")
+        if (isEndTransition) {
+            info.changes.withIndex().forEach { (i, change) ->
+                if (change.mode == TRANSIT_CLOSE) {
+                    t.hide(change.leash)
+                    startTransactionFinishT.hide(change.leash)
+                } else if (change.taskInfo?.taskId == state.draggedTaskId) {
+                    t.show(change.leash)
+                    startTransactionFinishT.show(change.leash)
+                    state.draggedTaskChange = change
+                } else if (change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM) {
+                    // Other freeform tasks that are being restored go behind the dragged task.
+                    val draggedTaskLeash = state.draggedTaskChange?.leash
+                            ?: error("Expected dragged leash to be non-null")
+                    t.setRelativeLayer(change.leash, draggedTaskLeash, -i)
+                    startTransactionFinishT.setRelativeLayer(change.leash, draggedTaskLeash, -i)
+                }
+            }
+
+            val draggedTaskChange = state.draggedTaskChange
+                    ?: throw IllegalStateException("Expected non-null change of dragged task")
+            val draggedTaskLeash = draggedTaskChange.leash
+            val startBounds = draggedTaskChange.startAbsBounds
+            val endBounds = draggedTaskChange.endAbsBounds
+
+            // TODO(b/301106941): Instead of forcing-finishing the animation that scales the
+            //  surface down and then starting another that scales it back up to the final size,
+            //  blend the two animations.
+            state.dragAnimator.endAnimator()
+            // Using [DRAG_FREEFORM_SCALE] to calculate animated width/height is possible because
+            // it is known that the animation scale is finished because the animation was
+            // force-ended above. This won't be true when the two animations are blended.
+            val animStartWidth = (startBounds.width() * DRAG_FREEFORM_SCALE).toInt()
+            val animStartHeight = (startBounds.height() * DRAG_FREEFORM_SCALE).toInt()
+            // Using end bounds here to find the left/top also assumes the center animation has
+            // finished and the surface is placed exactly in the center of the screen which matches
+            // the end/default bounds of the now freeform task.
+            val animStartLeft = endBounds.centerX() - (animStartWidth / 2)
+            val animStartTop = endBounds.centerY() - (animStartHeight / 2)
+            val animStartBounds = Rect(
+                    animStartLeft,
+                    animStartTop,
+                    animStartLeft + animStartWidth,
+                    animStartTop + animStartHeight
+            )
+
+
+            dragToDesktopStateListener?.onCommitToDesktopAnimationStart(t)
+            t.apply {
+                setScale(draggedTaskLeash, 1f, 1f)
+                setPosition(
+                        draggedTaskLeash,
+                        animStartBounds.left.toFloat(),
+                        animStartBounds.top.toFloat()
+                )
+                setWindowCrop(
+                        draggedTaskLeash,
+                        animStartBounds.width(),
+                        animStartBounds.height()
+                )
+            }
+            // Accept the merge by applying the merging transaction (applied by #showResizeVeil)
+            // and finish callback. Show the veil and position the task at the first frame before
+            // starting the final animation.
+            state.windowDecoration.showResizeVeil(t, animStartBounds)
+            finishCallback.onTransitionFinished(null /* wct */)
+
+            // Because the task surface was scaled down during the drag, we must use the animated
+            // bounds instead of the [startAbsBounds].
+            val tx: SurfaceControl.Transaction = transactionSupplier.get()
+            ValueAnimator.ofObject(rectEvaluator, animStartBounds, endBounds)
+                    .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+                    .apply {
+                        addUpdateListener { animator ->
+                            val animBounds = animator.animatedValue as Rect
+                            tx.apply {
+                                setScale(draggedTaskLeash, 1f, 1f)
+                                 setPosition(
+                                         draggedTaskLeash,
+                                         animBounds.left.toFloat(),
+                                         animBounds.top.toFloat()
+                                 )
+                                setWindowCrop(
+                                        draggedTaskLeash,
+                                        animBounds.width(),
+                                        animBounds.height()
+                                )
+                            }
+                            state.windowDecoration.updateResizeVeil(tx, animBounds)
+                        }
+                        addListener(object : AnimatorListenerAdapter() {
+                            override fun onAnimationEnd(animation: Animator) {
+                                state.windowDecoration.hideResizeVeil()
+                                startTransitionFinishCb.onTransitionFinished(null /* null */)
+                                clearState()
+                            }
+                        })
+                        start()
+                    }
+        } else if (isCancelTransition) {
+            info.changes.forEach { change ->
+                t.show(change.leash)
+                startTransactionFinishT.show(change.leash)
+            }
+            t.apply()
+            finishCallback.onTransitionFinished(null /* wct */)
+            startTransitionFinishCb.onTransitionFinished(null /* wct */)
+            clearState()
+        }
+    }
+
+    override fun handleRequest(
+            transition: IBinder,
+            request: TransitionRequestInfo
+    ): WindowContainerTransaction? {
+        // Only handle transitions started from shell.
+        return null
+    }
+
+    private fun isHomeChange(change: Change): Boolean {
+        return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME
+    }
+
+    private fun startCancelAnimation() {
+        val state = requireTransitionState()
+        val dragToDesktopAnimator = state.dragAnimator
+
+        val draggedTaskChange = state.draggedTaskChange
+                ?: throw IllegalStateException("Expected non-null task change")
+        val sc = draggedTaskChange.leash
+        // TODO(b/301106941): Don't end the animation and start one to scale it back, merge them
+        //  instead.
+        // End the animation that shrinks the window when task is first dragged from fullscreen
+        dragToDesktopAnimator.endAnimator()
+        // Then animate the scaled window back to its original bounds.
+        val x: Float = dragToDesktopAnimator.position.x
+        val y: Float = dragToDesktopAnimator.position.y
+        val targetX = draggedTaskChange.endAbsBounds.left
+        val targetY = draggedTaskChange.endAbsBounds.top
+        val dx = targetX - x
+        val dy = targetY - y
+        val tx: SurfaceControl.Transaction = transactionSupplier.get()
+        ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f)
+                .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+                .apply {
+                    addUpdateListener { animator ->
+                        val scale = animator.animatedValue as Float
+                        val fraction = animator.animatedFraction
+                        val animX = x + (dx * fraction)
+                        val animY = y + (dy * fraction)
+                        tx.apply {
+                            setPosition(sc, animX, animY)
+                            setScale(sc, scale, scale)
+                            show(sc)
+                            apply()
+                        }
+                    }
+                    addListener(object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator) {
+                            dragToDesktopStateListener?.onCancelToDesktopAnimationEnd(tx)
+                            // Start the cancel transition to restore order.
+                            startCancelDragToDesktopTransition()
+                        }
+                    })
+                    start()
+                }
+    }
+
+    private fun startCancelDragToDesktopTransition() {
+        val state = requireTransitionState()
+        val wct = WindowContainerTransaction()
+        when (state) {
+            is TransitionState.FromFullscreen -> {
+                val wc = state.draggedTaskChange?.container
+                        ?: error("Dragged task should be non-null before cancelling")
+                wct.reorder(wc, true /* toTop */)
+            }
+            is TransitionState.FromSplit -> {
+                val wc = state.splitRootChange?.container
+                        ?: error("Split root should be non-null before cancelling")
+                wct.reorder(wc, true /* toTop */)
+            }
+        }
+        val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling")
+        wct.restoreTransientOrder(homeWc)
+
+        state.cancelTransitionToken = transitions.startTransition(
+                TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
+    }
+
+    private fun clearState() {
+        transitionState = null
+    }
+
+    private fun isSplitTask(taskId: Int): Boolean {
+        return splitScreenController?.isTaskInSplitScreen(taskId) ?: false
+    }
+
+    private fun requireTransitionState(): TransitionState {
+        return transitionState ?: error("Expected non-null transition state")
+    }
+
+    interface DragToDesktopStateListener {
+        fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction)
+        fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction)
+    }
+
+    sealed class TransitionState {
+        abstract val draggedTaskId: Int
+        abstract val dragAnimator: MoveToDesktopAnimator
+        abstract val windowDecoration: DesktopModeWindowDecoration
+        abstract val startTransitionToken: IBinder
+        abstract var startTransitionFinishCb: Transitions.TransitionFinishCallback?
+        abstract var startTransitionFinishTransaction: SurfaceControl.Transaction?
+        abstract var cancelTransitionToken: IBinder?
+        abstract var homeToken: WindowContainerToken?
+        abstract var draggedTaskChange: Change?
+        abstract var cancelled: Boolean
+
+        data class FromFullscreen(
+                override val draggedTaskId: Int,
+                override val dragAnimator: MoveToDesktopAnimator,
+                override val windowDecoration: DesktopModeWindowDecoration,
+                override val startTransitionToken: IBinder,
+                override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+                override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+                override var cancelTransitionToken: IBinder? = null,
+                override var homeToken: WindowContainerToken? = null,
+                override var draggedTaskChange: Change? = null,
+                override var cancelled: Boolean = false,
+        ) : TransitionState()
+        data class FromSplit(
+                override val draggedTaskId: Int,
+                override val dragAnimator: MoveToDesktopAnimator,
+                override val windowDecoration: DesktopModeWindowDecoration,
+                override val startTransitionToken: IBinder,
+                override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+                override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+                override var cancelTransitionToken: IBinder? = null,
+                override var homeToken: WindowContainerToken? = null,
+                override var draggedTaskChange: Change? = null,
+                override var cancelled: Boolean = false,
+                var splitRootChange: Change? = null,
+        ) : TransitionState()
+    }
+
+    companion object {
+        /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
+        private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 024465b..605600f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -18,12 +18,13 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
+import static com.android.wm.shell.transition.Transitions.TRANSIT_MOVE_TO_DESKTOP;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.util.Slog;
@@ -38,11 +39,9 @@
 
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration;
-import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -60,8 +59,6 @@
     public static final int FREEFORM_ANIMATION_DURATION = 336;
 
     private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
-    private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
-    private MoveToDesktopAnimator mMoveToDesktopAnimator;
     private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
 
     public EnterDesktopTaskTransitionHandler(
@@ -77,61 +74,6 @@
     }
 
     /**
-     * Starts Transition of a given type
-     * @param type Transition type
-     * @param wct WindowContainerTransaction for transition
-     * @param onAnimationEndCallback to be called after animation
-     */
-    private void startTransition(@WindowManager.TransitionType int type,
-            @NonNull WindowContainerTransaction wct,
-            Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
-        mOnAnimationFinishedCallback = onAnimationEndCallback;
-        final IBinder token = mTransitions.startTransition(type, wct, this);
-        mPendingTransitionTokens.add(token);
-    }
-
-    /**
-     * Starts Transition of type TRANSIT_START_DRAG_TO_DESKTOP_MODE
-     * @param wct WindowContainerTransaction for transition
-     * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
-     *                              to desktop animation
-     * @param onAnimationEndCallback to be called after animation
-     */
-    public void startMoveToDesktop(@NonNull WindowContainerTransaction wct,
-            @NonNull MoveToDesktopAnimator moveToDesktopAnimator,
-            Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
-        mMoveToDesktopAnimator = moveToDesktopAnimator;
-        startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
-                onAnimationEndCallback);
-    }
-
-    /**
-     * Starts Transition of type TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
-     * @param wct WindowContainerTransaction for transition
-     * @param onAnimationEndCallback to be called after animation
-     */
-    public void finalizeMoveToDesktop(@NonNull WindowContainerTransaction wct,
-            Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
-        startTransition(Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, wct,
-                onAnimationEndCallback);
-    }
-
-    /**
-     * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
-     * @param wct WindowContainerTransaction for transition
-     * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
-     *                              to desktop animation
-     * @param onAnimationEndCallback to be called after animation
-     */
-    public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct,
-            MoveToDesktopAnimator moveToDesktopAnimator,
-            Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
-        mMoveToDesktopAnimator = moveToDesktopAnimator;
-        startTransition(Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE, wct,
-                onAnimationEndCallback);
-    }
-
-    /**
      * Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
      * @param wct WindowContainerTransaction for transition
      * @param decor {@link DesktopModeWindowDecoration} of task being animated
@@ -139,8 +81,8 @@
     public void moveToDesktop(@NonNull WindowContainerTransaction wct,
             DesktopModeWindowDecoration decor) {
         mDesktopModeWindowDecoration = decor;
-        startTransition(Transitions.TRANSIT_MOVE_TO_DESKTOP, wct,
-                null /* onAnimationEndCallback */);
+        final IBinder token = mTransitions.startTransition(TRANSIT_MOVE_TO_DESKTOP, wct, this);
+        mPendingTransitionTokens.add(token);
     }
 
     @Override
@@ -182,30 +124,11 @@
         }
 
         final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-        if (type == Transitions.TRANSIT_MOVE_TO_DESKTOP
+        if (type == TRANSIT_MOVE_TO_DESKTOP
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             return animateMoveToDesktop(change, startT, finishCallback);
         }
 
-        if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
-                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-            return animateStartDragToDesktopMode(change, startT, finishT, finishCallback);
-        }
-
-        final Rect endBounds = change.getEndAbsBounds();
-        if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
-                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
-                && !endBounds.isEmpty()) {
-            return animateFinalizeDragToDesktopMode(change, startT, finishT, finishCallback,
-                    endBounds);
-        }
-
-        if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
-                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-            return animateCancelDragToDesktopMode(change, startT, finishT, finishCallback,
-                    endBounds);
-        }
-
         return false;
     }
 
@@ -248,142 +171,6 @@
         return true;
     }
 
-    private boolean animateStartDragToDesktopMode(
-            @NonNull TransitionInfo.Change change,
-            @NonNull SurfaceControl.Transaction startT,
-            @NonNull SurfaceControl.Transaction finishT,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        // Transitioning to freeform but keeping fullscreen bounds, so the crop is set
-        // to null and we don't require an animation
-        final SurfaceControl sc = change.getLeash();
-        startT.setWindowCrop(sc, null);
-
-        if (mMoveToDesktopAnimator == null
-                || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
-            Slog.e(TAG, "No animator available for this transition");
-            return false;
-        }
-
-        // Calculate and set position of the task
-        final PointF position = mMoveToDesktopAnimator.getPosition();
-        startT.setPosition(sc, position.x, position.y);
-        finishT.setPosition(sc, position.x, position.y);
-
-        startT.apply();
-
-        mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null));
-
-        return true;
-    }
-
-    private boolean animateFinalizeDragToDesktopMode(
-            @NonNull TransitionInfo.Change change,
-            @NonNull SurfaceControl.Transaction startT,
-            @NonNull SurfaceControl.Transaction finishT,
-            @NonNull Transitions.TransitionFinishCallback finishCallback,
-            @NonNull Rect endBounds) {
-        // This Transition animates a task to freeform bounds after being dragged into freeform
-        // mode and brings the remaining freeform tasks to front
-        final SurfaceControl sc = change.getLeash();
-        startT.setWindowCrop(sc, endBounds.width(),
-                endBounds.height());
-        startT.apply();
-
-        // End the animation that shrinks the window when task is first dragged from fullscreen
-        if (mMoveToDesktopAnimator != null) {
-            mMoveToDesktopAnimator.endAnimator();
-        }
-
-        // We want to find the scale of the current bounds relative to the end bounds. The
-        // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be
-        // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to
-        // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds
-        final ValueAnimator animator =
-                ValueAnimator.ofFloat(
-                        MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
-        animator.setDuration(FREEFORM_ANIMATION_DURATION);
-        final SurfaceControl.Transaction t = mTransactionSupplier.get();
-        animator.addUpdateListener(animation -> {
-            final float animationValue = (float) animation.getAnimatedValue();
-            t.setScale(sc, animationValue, animationValue);
-
-            final float animationWidth = endBounds.width() * animationValue;
-            final float animationHeight = endBounds.height() * animationValue;
-            final int animationX = endBounds.centerX() - (int) (animationWidth / 2);
-            final int animationY = endBounds.centerY() - (int) (animationHeight / 2);
-
-            t.setPosition(sc, animationX, animationY);
-            t.apply();
-        });
-
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (mOnAnimationFinishedCallback != null) {
-                    mOnAnimationFinishedCallback.accept(finishT);
-                }
-                mTransitions.getMainExecutor().execute(
-                        () -> finishCallback.onTransitionFinished(null));
-            }
-        });
-
-        animator.start();
-        return true;
-    }
-    private boolean animateCancelDragToDesktopMode(
-            @NonNull TransitionInfo.Change change,
-            @NonNull SurfaceControl.Transaction startT,
-            @NonNull SurfaceControl.Transaction finishT,
-            @NonNull Transitions.TransitionFinishCallback finishCallback,
-            @NonNull Rect endBounds) {
-        // This Transition animates a task to fullscreen after being dragged from the status
-        // bar and then released back into the status bar area
-        final SurfaceControl sc = change.getLeash();
-        // Hide the first (fullscreen) frame because the animation will start from the smaller
-        // scale size.
-        startT.hide(sc)
-                .setWindowCrop(sc, endBounds.width(), endBounds.height())
-                .apply();
-
-        if (mMoveToDesktopAnimator == null
-                || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
-            Slog.e(TAG, "No animator available for this transition");
-            return false;
-        }
-
-        // End the animation that shrinks the window when task is first dragged from fullscreen
-        mMoveToDesktopAnimator.endAnimator();
-
-        final ValueAnimator animator = new ValueAnimator();
-        animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f);
-        animator.setDuration(FREEFORM_ANIMATION_DURATION);
-        final SurfaceControl.Transaction t = mTransactionSupplier.get();
-
-        // Get position of the task
-        final float x = mMoveToDesktopAnimator.getPosition().x;
-        final float y = mMoveToDesktopAnimator.getPosition().y;
-
-        animator.addUpdateListener(animation -> {
-            final float scale = (float) animation.getAnimatedValue();
-            t.setPosition(sc, x * (1 - scale), y * (1 - scale))
-                    .setScale(sc, scale, scale)
-                    .show(sc)
-                    .apply();
-        });
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (mOnAnimationFinishedCallback != null) {
-                    mOnAnimationFinishedCallback.accept(finishT);
-                }
-                mTransitions.getMainExecutor().execute(
-                        () -> finishCallback.onTransitionFinished(null));
-            }
-        });
-        animator.start();
-        return true;
-    }
-
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index ab5c063..41ec33c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -150,19 +150,19 @@
     /** Transition type for maximize to freeform transition. */
     public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
 
-    /** Transition type for starting the move to desktop mode. */
-    public static final int TRANSIT_START_DRAG_TO_DESKTOP_MODE =
+    /** Transition type for starting the drag to desktop mode. */
+    public static final int TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP =
             WindowManager.TRANSIT_FIRST_CUSTOM + 10;
 
-    /** Transition type for finalizing the move to desktop mode. */
-    public static final int TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE =
+    /** Transition type for finalizing the drag to desktop mode. */
+    public static final int TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP =
             WindowManager.TRANSIT_FIRST_CUSTOM + 11;
 
     /** Transition type to fullscreen from desktop mode. */
     public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
 
-    /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */
-    public static final int TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE =
+    /** Transition type to cancel the drag to desktop mode. */
+    public static final int TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP =
             WindowManager.TRANSIT_FIRST_CUSTOM + 13;
 
     /** Transition type to animate the toggle resize between the max and default desktop sizes. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index e206039..3add6f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -268,13 +268,19 @@
             @NonNull TransitionInfo info,
             @NonNull TransitionInfo.Change change) {
         if (change.getMode() == WindowManager.TRANSIT_CHANGE
-                && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
-                || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
-                || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+                && (info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
                 || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
                 || info.getType() == Transitions.TRANSIT_MOVE_TO_DESKTOP)) {
             mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
                     .addTransitionPausingRelayout(transition);
+        } else if (change.getMode() == WindowManager.TRANSIT_TO_BACK
+                && info.getType() == Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+                && change.getTaskInfo() != null) {
+            final DesktopModeWindowDecoration decor =
+                    mWindowDecorByTaskId.get(change.getTaskInfo().taskId);
+            if (decor != null) {
+                decor.addTransitionPausingRelayout(transition);
+            }
         }
     }
 
@@ -765,10 +771,8 @@
                         mMoveToDesktopAnimator = null;
                         return;
                     } else if (mMoveToDesktopAnimator != null) {
-                        relevantDecor.incrementRelayoutBlock();
                         mDesktopTasksController.ifPresent(
-                                c -> c.cancelMoveToDesktop(relevantDecor.mTaskInfo,
-                                        mMoveToDesktopAnimator));
+                                c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo));
                         mMoveToDesktopAnimator = null;
                         return;
                     }
@@ -790,15 +794,24 @@
                             relevantDecor.mTaskInfo.displayId);
                     if (ev.getY() > statusBarHeight) {
                         if (mMoveToDesktopAnimator == null) {
-                            closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
                             mMoveToDesktopAnimator = new MoveToDesktopAnimator(
-                                    mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
-                                    relevantDecor.mTaskSurface);
+                                    mContext, mDragToDesktopAnimationStartBounds,
+                                    relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
                             mDesktopTasksController.ifPresent(
-                                    c -> c.startMoveToDesktop(relevantDecor.mTaskInfo,
-                                            mDragToDesktopAnimationStartBounds,
-                                            mMoveToDesktopAnimator));
-                            mMoveToDesktopAnimator.startAnimation();
+                                    c -> {
+                                        final int taskId = relevantDecor.mTaskInfo.taskId;
+                                        relevantDecor.incrementRelayoutBlock();
+                                        if (isTaskInSplitScreen(taskId)) {
+                                            final DesktopModeWindowDecoration otherDecor =
+                                                    mWindowDecorByTaskId.get(
+                                                            getOtherSplitTask(taskId).taskId);
+                                            if (otherDecor != null) {
+                                                otherDecor.incrementRelayoutBlock();
+                                            }
+                                        }
+                                        c.startDragToDesktop(relevantDecor.mTaskInfo,
+                                                mMoveToDesktopAnimator, relevantDecor);
+                                    });
                         }
                     }
                     if (mMoveToDesktopAnimator != null) {
@@ -837,7 +850,6 @@
      */
     private void animateToDesktop(DesktopModeWindowDecoration relevantDecor,
             MotionEvent ev) {
-        relevantDecor.incrementRelayoutBlock();
         centerAndMoveToDesktopWithAnimation(relevantDecor, ev);
     }
 
@@ -853,15 +865,15 @@
         final SurfaceControl sc = relevantDecor.mTaskSurface;
         final Rect endBounds = calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE);
         final Transaction t = mTransactionFactory.get();
-        final float diffX = endBounds.centerX() - ev.getX();
-        final float diffY = endBounds.top - ev.getY();
-        final float startingX = ev.getX() - DRAG_FREEFORM_SCALE
+        final float diffX = endBounds.centerX() - ev.getRawX();
+        final float diffY = endBounds.top - ev.getRawY();
+        final float startingX = ev.getRawX() - DRAG_FREEFORM_SCALE
                 * mDragToDesktopAnimationStartBounds.width() / 2;
 
         animator.addUpdateListener(animation -> {
             final float animatorValue = (float) animation.getAnimatedValue();
             final float x = startingX + diffX * animatorValue;
-            final float y = ev.getY() + diffY * animatorValue;
+            final float y = ev.getRawY() + diffY * animatorValue;
             t.setPosition(sc, x, y);
             t.apply();
         });
@@ -869,9 +881,11 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mDesktopTasksController.ifPresent(
-                        c -> c.onDragPositioningEndThroughStatusBar(
-                                relevantDecor.mTaskInfo,
-                                calculateFreeformBounds(ev.getDisplayId(), FINAL_FREEFORM_SCALE)));
+                        c -> {
+                            c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo,
+                                    calculateFreeformBounds(ev.getDisplayId(),
+                                            FINAL_FREEFORM_SCALE));
+                        });
             }
         });
         animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index b2267dd..af05523 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -2,10 +2,12 @@
 
 import android.animation.ValueAnimator
 import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
 import android.graphics.PointF
 import android.graphics.Rect
 import android.view.MotionEvent
 import android.view.SurfaceControl
+import com.android.internal.policy.ScreenDecorationsUtils
 
 /**
  * Creates an animator to shrink and position task after a user drags a fullscreen task from
@@ -14,6 +16,7 @@
  * accessed by the EnterDesktopTaskTransitionHandler.
  */
 class MoveToDesktopAnimator @JvmOverloads constructor(
+        private val context: Context,
         private val startBounds: Rect,
         private val taskInfo: RunningTaskInfo,
         private val taskSurface: SurfaceControl,
@@ -33,9 +36,11 @@
             .setDuration(ANIMATION_DURATION.toLong())
             .apply {
                 val t = SurfaceControl.Transaction()
+                val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
                 addUpdateListener { animation ->
                     val animatorValue = animation.animatedValue as Float
                     t.setScale(taskSurface, animatorValue, animatorValue)
+                            .setCornerRadius(taskSurface, cornerRadius)
                             .apply()
                 }
             }
@@ -44,19 +49,40 @@
     val position: PointF = PointF(0.0f, 0.0f)
 
     /**
+     * Whether motion events from the drag gesture should affect the dragged surface or not. Used
+     * to disallow moving the surface's position prematurely since it should not start moving at
+     * all until the drag-to-desktop transition is ready to animate and the wallpaper/home are
+     * ready to be revealed behind the dragged/scaled task.
+     */
+    private var allowSurfaceChangesOnMove = false
+
+    /**
      * Starts the animation that scales the task down.
      */
     fun startAnimation() {
+        allowSurfaceChangesOnMove = true
         dragToDesktopAnimator.start()
     }
 
     /**
-     * Uses the position of the motion event and the current scale of the task as defined by the
-     * ValueAnimator to update the local position variable and set the task surface's position
+     * Uses the position of the motion event of the drag-to-desktop gesture to update the dragged
+     * task's position on screen to follow the touch point. Note that the position change won't
+     * be applied immediately always, such as near the beginning where it waits until the wallpaper
+     * or home are visible behind it. Once they're visible the surface will catch-up to the most
+     * recent touch position.
      */
     fun updatePosition(ev: MotionEvent) {
-        position.x = ev.x - animatedTaskWidth / 2
-        position.y = ev.y
+        // Using rawX/Y because when dragging a task in split, the local X/Y is relative to the
+        // split stages, but the split task surface is re-parented to the task display area to
+        // allow dragging beyond its stage across any region of the display. Because of that, the
+        // rawX/Y are more true to where the gesture is on screen and where the surface should be
+        // positioned.
+        position.x = ev.rawX - animatedTaskWidth / 2
+        position.y = ev.rawY
+
+        if (!allowSurfaceChangesOnMove) {
+            return
+        }
 
         val t = transactionFactory()
         t.setPosition(taskSurface, position.x, position.y)
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 ebcb640..fde6acb 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
@@ -100,6 +100,7 @@
     @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
     @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
             ToggleResizeDesktopTaskTransitionHandler
+    @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
     @Mock lateinit var launchAdjacentController: LaunchAdjacentController
     @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
     @Mock lateinit var splitScreenController: SplitScreenController
@@ -127,7 +128,7 @@
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
 
         controller = createController()
-        controller.splitScreenController = splitScreenController
+        controller.setSplitScreenController(splitScreenController)
 
         shellInit.init()
 
@@ -150,6 +151,7 @@
             enterDesktopTransitionHandler,
             exitDesktopTransitionHandler,
             mToggleResizeDesktopTaskTransitionHandler,
+            dragToDesktopTransitionHandler,
             desktopModeTaskRepository,
             launchAdjacentController,
             recentsTransitionHandler,
@@ -757,6 +759,7 @@
 
     private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createSplitScreenTask(displayId)
+        whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         runningTasks.add(task)
         return task
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
new file mode 100644
index 0000000..a5629c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -0,0 +1,211 @@
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WindowingMode
+import android.graphics.PointF
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.TransitionInfo.FLAG_IS_WALLPAPER
+import androidx.test.filters.SmallTest
+import com.android.server.testutils.any
+import com.android.server.testutils.mock
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import java.util.function.Supplier
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/** Tests of [DragToDesktopTransitionHandler]. */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DragToDesktopTransitionHandlerTest : ShellTestCase() {
+
+    @Mock private lateinit var transitions: Transitions
+    @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    @Mock private lateinit var splitScreenController: SplitScreenController
+
+    private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+
+    private lateinit var handler: DragToDesktopTransitionHandler
+
+    @Before
+    fun setUp() {
+        handler =
+            DragToDesktopTransitionHandler(
+                    context,
+                    transitions,
+                    taskDisplayAreaOrganizer,
+                    transactionSupplier
+                )
+                .apply { setSplitScreenController(splitScreenController) }
+    }
+
+    @Test
+    fun startDragToDesktop_animateDragWhenReady() {
+        val task = createTask()
+        val dragAnimator = mock<MoveToDesktopAnimator>()
+        // Simulate transition is started.
+        val transition = startDragToDesktopTransition(task, dragAnimator)
+
+        // Now it's ready to animate.
+        handler.startAnimation(
+            transition = transition,
+            info =
+                createTransitionInfo(
+                    type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+                    draggedTask = task
+                ),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+            finishCallback = {}
+        )
+
+        verify(dragAnimator).startAnimation()
+    }
+
+    @Test
+    fun startDragToDesktop_cancelledBeforeReady_startCancelTransition() {
+        val task = createTask()
+        val dragAnimator = mock<MoveToDesktopAnimator>()
+        // Simulate transition is started and is ready to animate.
+        val transition = startDragToDesktopTransition(task, dragAnimator)
+
+        handler.cancelDragToDesktopTransition()
+
+        handler.startAnimation(
+            transition = transition,
+            info =
+                createTransitionInfo(
+                    type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+                    draggedTask = task
+                ),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+            finishCallback = {}
+        )
+
+        // Don't even animate the "drag" since it was already cancelled.
+        verify(dragAnimator, never()).startAnimation()
+        // Instead, start the cancel transition.
+        verify(transitions)
+            .startTransition(eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), any(), eq(handler))
+    }
+
+    @Test
+    fun cancelDragToDesktop_startWasReady_cancel() {
+        val task = createTask()
+        val dragAnimator = mock<MoveToDesktopAnimator>()
+        whenever(dragAnimator.position).thenReturn(PointF())
+        // Simulate transition is started and is ready to animate.
+        val transition = startDragToDesktopTransition(task, dragAnimator)
+        handler.startAnimation(
+            transition = transition,
+            info =
+                createTransitionInfo(
+                    type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+                    draggedTask = task
+                ),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+            finishCallback = {}
+        )
+
+        // Then user cancelled after it had already started.
+        handler.cancelDragToDesktopTransition()
+
+        // Cancel animation should run since it had already started.
+        verify(dragAnimator).endAnimator()
+    }
+
+    @Test
+    fun cancelDragToDesktop_startWasNotReady_animateCancel() {
+        val task = createTask()
+        val dragAnimator = mock<MoveToDesktopAnimator>()
+        // Simulate transition is started and is ready to animate.
+        startDragToDesktopTransition(task, dragAnimator)
+
+        // Then user cancelled before the transition was ready and animated.
+        handler.cancelDragToDesktopTransition()
+
+        // No need to animate the cancel since the start animation couldn't even start.
+        verifyZeroInteractions(dragAnimator)
+    }
+
+    private fun startDragToDesktopTransition(
+        task: RunningTaskInfo,
+        dragAnimator: MoveToDesktopAnimator
+    ): IBinder {
+        val token = mock<IBinder>()
+        whenever(
+                transitions.startTransition(
+                    eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
+                    any(),
+                    eq(handler)
+                )
+            )
+            .thenReturn(token)
+        handler.startDragToDesktopTransition(task.taskId, dragAnimator, mock())
+        return token
+    }
+
+    private fun createTask(
+        @WindowingMode windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+        isHome: Boolean = false,
+    ): RunningTaskInfo {
+        return TestRunningTaskInfoBuilder()
+            .setActivityType(if (isHome) ACTIVITY_TYPE_HOME else ACTIVITY_TYPE_STANDARD)
+            .setWindowingMode(windowingMode)
+            .build()
+            .also {
+                whenever(splitScreenController.isTaskInSplitScreen(it.taskId))
+                    .thenReturn(windowingMode == WINDOWING_MODE_MULTI_WINDOW)
+            }
+    }
+
+    private fun createTransitionInfo(type: Int, draggedTask: RunningTaskInfo): TransitionInfo {
+        return TransitionInfo(type, 0 /* flags */).apply {
+            addChange( // Home.
+                TransitionInfo.Change(mock(), mock()).apply {
+                    parent = null
+                    taskInfo =
+                        TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+                    flags = flags or FLAG_IS_WALLPAPER
+                }
+            )
+            addChange( // Dragged Task.
+                TransitionInfo.Change(mock(), mock()).apply {
+                    parent = null
+                    taskInfo = draggedTask
+                }
+            )
+            addChange( // Wallpaper.
+                TransitionInfo.Change(mock(), mock()).apply {
+                    parent = null
+                    taskInfo = null
+                    flags = flags or FLAG_IS_WALLPAPER
+                }
+            )
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
deleted file mode 100644
index 772d97d..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.desktopmode;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerToken;
-import android.window.TransitionInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
-
-import junit.framework.AssertionFailedError;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.Supplier;
-
-/** Tests of {@link com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler} */
-@SmallTest
-public class EnterDesktopTaskTransitionHandlerTest {
-
-    @Mock
-    private Transitions mTransitions;
-    @Mock
-    IBinder mToken;
-    @Mock
-    Supplier<SurfaceControl.Transaction> mTransactionFactory;
-    @Mock
-    SurfaceControl.Transaction mStartT;
-    @Mock
-    SurfaceControl.Transaction mFinishT;
-    @Mock
-    SurfaceControl.Transaction mAnimationT;
-    @Mock
-    Transitions.TransitionFinishCallback mTransitionFinishCallback;
-    @Mock
-    ShellExecutor mExecutor;
-    @Mock
-    SurfaceControl mSurfaceControl;
-    @Mock
-    MoveToDesktopAnimator mMoveToDesktopAnimator;
-    @Mock
-    PointF mPosition;
-
-    private EnterDesktopTaskTransitionHandler mEnterDesktopTaskTransitionHandler;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        doReturn(mExecutor).when(mTransitions).getMainExecutor();
-        doReturn(mAnimationT).when(mTransactionFactory).get();
-        doReturn(mPosition).when(mMoveToDesktopAnimator).getPosition();
-
-        mEnterDesktopTaskTransitionHandler = new EnterDesktopTaskTransitionHandler(mTransitions,
-                mTransactionFactory);
-    }
-
-    @Test
-    public void testEnterFreeformAnimation() {
-        final int taskId = 1;
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        doReturn(mToken).when(mTransitions)
-                .startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
-                        mEnterDesktopTaskTransitionHandler);
-        doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId();
-
-        mEnterDesktopTaskTransitionHandler.startMoveToDesktop(wct,
-                mMoveToDesktopAnimator, null);
-
-        TransitionInfo.Change change =
-                createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
-        TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE,
-                change);
-
-
-        assertTrue(mEnterDesktopTaskTransitionHandler
-                .startAnimation(mToken, info, mStartT, mFinishT, mTransitionFinishCallback));
-
-        verify(mStartT).setWindowCrop(mSurfaceControl, null);
-        verify(mStartT).apply();
-    }
-
-    @Test
-    public void testTransitEnterDesktopModeAnimation() throws Throwable {
-        final int transitionType = Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE;
-        final int taskId = 1;
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        doReturn(mToken).when(mTransitions)
-                .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler);
-        mEnterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct, null);
-
-        TransitionInfo.Change change =
-                createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
-        change.setEndAbsBounds(new Rect(0, 0, 1, 1));
-        TransitionInfo info = createTransitionInfo(
-                Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, change);
-
-        runOnUiThread(() -> {
-            try {
-                assertTrue(mEnterDesktopTaskTransitionHandler
-                                .startAnimation(mToken, info, mStartT, mFinishT,
-                                        mTransitionFinishCallback));
-            } catch (Exception e) {
-                throw new AssertionFailedError(e.getMessage());
-            }
-        });
-
-        verify(mStartT).setWindowCrop(mSurfaceControl, change.getEndAbsBounds().width(),
-                change.getEndAbsBounds().height());
-        verify(mStartT).apply();
-    }
-
-    private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
-            @WindowConfiguration.WindowingMode int windowingMode) {
-        final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
-        taskInfo.taskId = taskId;
-        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
-        final TransitionInfo.Change change = new TransitionInfo.Change(
-                new WindowContainerToken(mock(IWindowContainerToken.class)), mSurfaceControl);
-        change.setMode(type);
-        change.setTaskInfo(taskInfo);
-        return change;
-    }
-
-    private static TransitionInfo createTransitionInfo(
-            @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) {
-        TransitionInfo info = new TransitionInfo(type, 0);
-        info.addChange(change);
-        return info;
-    }
-
-}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index cf51e21..f1ffa66 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -294,6 +294,8 @@
         "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
         // Keyguard helper
@@ -616,6 +618,9 @@
 
     instrumentation_for: "SystemUIRobo-stub",
     java_resource_dirs: ["tests/robolectric/config"],
+    plugins: [
+        "dagger2-compiler",
+    ],
 }
 
 // Opt-out config for optimizing the SystemUI target using R8.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5881631..e218308 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -982,6 +982,24 @@
             android:excludeFromRecents="true"
             android:visibleToInstantApps="true"/>
 
+        <activity android:name="com.android.systemui.communal.widgets.WidgetPickerActivity"
+            android:theme="@style/Theme.EditWidgetsActivity"
+            android:excludeFromRecents="true"
+            android:autoRemoveFromRecents="true"
+            android:showOnLockScreen="true"
+            android:launchMode="singleTop"
+            android:exported="false">
+        </activity>
+
+        <activity android:name="com.android.systemui.communal.widgets.EditWidgetsActivity"
+            android:theme="@style/Theme.EditWidgetsActivity"
+            android:excludeFromRecents="true"
+            android:autoRemoveFromRecents="true"
+            android:showOnLockScreen="true"
+            android:launchMode="singleTop"
+            android:exported="false">
+        </activity>
+
         <!-- Doze with notifications, run in main sysui process for every user  -->
         <service
             android:name=".doze.DozeService"
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 6e18cb9..c80902e 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
@@ -45,6 +45,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
@@ -89,11 +90,8 @@
                 )
             }
         }
-        IconButton(onClick = viewModel::onOpenWidgetPicker) {
-            Icon(
-                Icons.Default.Add,
-                LocalContext.current.getString(R.string.button_to_open_widget_picker)
-            )
+        IconButton(onClick = viewModel::onOpenWidgetEditor) {
+            Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor))
         }
 
         // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
diff --git a/packages/SystemUI/res/layout/edit_widgets.xml b/packages/SystemUI/res/layout/edit_widgets.xml
new file mode 100644
index 0000000..182e651
--- /dev/null
+++ b/packages/SystemUI/res/layout/edit_widgets.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/edit_widgets"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        style="@android:Widget.DeviceDefault.Button.Colored"
+        android:id="@+id/add_widget"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:textSize="28sp"
+        android:text="@string/hub_mode_add_widget_button_text"/>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/widget_picker.xml b/packages/SystemUI/res/layout/widget_picker.xml
new file mode 100644
index 0000000..827bd5d
--- /dev/null
+++ b/packages/SystemUI/res/layout/widget_picker.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widgets_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="64dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal">
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3163533..daf6cb3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1056,10 +1056,12 @@
     <!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
     <string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
 
-    <!-- Description for the button that opens the widget picker on click. [CHAR LIMIT=50] -->
-    <string name="button_to_open_widget_picker">Open the widget picker</string>
+    <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] -->
+    <string name="button_to_open_widget_editor">Open the widget editor</string>
     <!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] -->
     <string name="button_to_remove_widget">Remove a widget</string>
+    <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] -->
+    <string name="hub_mode_add_widget_button_text">Add Widget</string>
 
     <!-- Related to user switcher --><skip/>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7ce530f..2117714 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -943,6 +943,11 @@
         <item name="android:windowLightStatusBar">true</item>
     </style>
 
+    <style name="Theme.EditWidgetsActivity"
+        parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+        <item name="android:windowBackground">@android:color/white</item>
+    </style>
+
     <style name="TextAppearance.Control">
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 8f31a2d..a5bd89a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -48,9 +48,9 @@
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -237,7 +237,7 @@
                 )
             }
             REASON_AUTH_KEYGUARD -> {
-                if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+                if (DeviceEntryUdfpsRefactor.isEnabled) {
                     // note: empty controller, currently shows no visual affordance
                     // instead SysUI will show the fingerprint icon in its DeviceEntryIconView
                     UdfpsBpViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 2c4ed58..35c3ded 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -184,6 +184,7 @@
     }
 
     init {
+        com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode()
         view.repeatWhenAttached {
             // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
             // can make the view not visible; and we still want to listen for events
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 273adcf..847b98e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -16,12 +16,17 @@
 
 package com.android.systemui.communal.dagger
 
+import android.content.Context
 import com.android.systemui.communal.data.db.CommunalDatabaseModule
 import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
+import com.android.systemui.dagger.qualifiers.Application
 import dagger.Module
+import dagger.Provides
 
 @Module(
     includes =
@@ -33,4 +38,11 @@
             CommunalDatabaseModule::class,
         ]
 )
-class CommunalModule
+class CommunalModule {
+    @Provides
+    fun provideEditWidgetsActivityStarter(
+        @Application context: Context
+    ): EditWidgetsActivityStarter {
+        return EditWidgetsActivityStarterImpl(context)
+    }
+}
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 771dfbc..7391a5e 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
@@ -25,6 +25,7 @@
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
 import javax.inject.Inject
@@ -48,6 +49,7 @@
     smartspaceRepository: SmartspaceRepository,
     tutorialInteractor: CommunalTutorialInteractor,
     private val appWidgetHost: AppWidgetHost,
+    private val editWidgetsActivityStarter: EditWidgetsActivityStarter
 ) {
 
     /** Whether communal features are enabled. */
@@ -72,6 +74,11 @@
         communalRepository.setDesiredScene(newScene)
     }
 
+    /** Show the widget editor Activity. */
+    fun showWidgetEditor() {
+        editWidgetsActivityStarter.startActivity()
+    }
+
     /** Add a widget at the specified position. */
     fun addWidget(componentName: ComponentName, priority: Int) =
         widgetRepository.addWidget(componentName, priority)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 5efe6ce..14edc8e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
-import android.content.ComponentName
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalSceneKey
@@ -46,19 +45,6 @@
     /** Delete a widget by id. */
     fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
 
-    /** Open the widget picker */
-    fun onOpenWidgetPicker() {
-        // STOPSHIP(b/306500486): refactor this when integrating with the widget picker.
-        // Eventually clicking on this button will bring up the widget picker and inside
-        // the widget picker, addWidget will be called to add the user selected widget.
-        // For now, a stopwatch widget will be added to the end of the grid.
-        communalInteractor.addWidget(
-            componentName =
-                ComponentName(
-                    "com.google.android.deskclock",
-                    "com.android.alarmclock.StopwatchAppWidgetProvider"
-                ),
-            priority = 0
-        )
-    }
+    /** Open the widget editor */
+    fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
new file mode 100644
index 0000000..78e85db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import androidx.activity.ComponentActivity
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** An Activity for editing the widgets that appear in hub mode. */
+class EditWidgetsActivity @Inject constructor(private val communalInteractor: CommunalInteractor) :
+    ComponentActivity() {
+    companion object {
+        /**
+         * Intent extra name for the {@link AppWidgetProviderInfo} of a widget to add to hub mode.
+         */
+        const val ADD_WIDGET_INFO = "add_widget_info"
+        private const val TAG = "EditWidgetsActivity"
+    }
+
+    private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
+        registerForActivityResult(StartActivityForResult()) { result ->
+            when (result.resultCode) {
+                RESULT_OK -> {
+                    result.data
+                        ?.let {
+                            it.getParcelableExtra(
+                                ADD_WIDGET_INFO,
+                                AppWidgetProviderInfo::class.java
+                            )
+                        }
+                        ?.let { communalInteractor.addWidget(it.provider, 0) }
+                        ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+                }
+                else ->
+                    Log.w(
+                        TAG,
+                        "Failed to receive result from widget picker, code=${result.resultCode}"
+                    )
+            }
+            this@EditWidgetsActivity.finish()
+        }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setShowWhenLocked(true)
+        setContentView(R.layout.edit_widgets)
+
+        val addWidgetsButton = findViewById<View>(R.id.add_widget)
+        addWidgetsButton?.setOnClickListener({
+            addWidgetActivityLauncher.launch(
+                Intent(applicationContext, WidgetPickerActivity::class.java)
+            )
+        })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
new file mode 100644
index 0000000..846e300
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.dagger.qualifiers.Application
+
+interface EditWidgetsActivityStarter {
+    fun startActivity()
+}
+
+class EditWidgetsActivityStarterImpl(@Application private val applicationContext: Context) :
+    EditWidgetsActivityStarter {
+    override fun startActivity() {
+        applicationContext.startActivity(
+            Intent(applicationContext, EditWidgetsActivity::class.java)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
new file mode 100644
index 0000000..3e6dbd5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Intent
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.activity.ComponentActivity
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * An Activity responsible for displaying a list of widgets to add to the hub mode grid. This is
+ * essentially a placeholder until Launcher's widget picker can be used.
+ */
+class WidgetPickerActivity
+@Inject
+constructor(
+    private val appWidgetManager: AppWidgetManager,
+) : ComponentActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(R.layout.widget_picker)
+        setShowWhenLocked(true)
+
+        loadWidgets()
+    }
+
+    private fun loadWidgets() {
+        val containerView: ViewGroup? = findViewById(R.id.widgets_container)
+        containerView?.apply {
+            try {
+                appWidgetManager
+                    .getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
+                    ?.stream()
+                    ?.limit(5)
+                    ?.forEach { widgetInfo ->
+                        val activity = this@WidgetPickerActivity
+                        val widgetPreview =
+                            widgetInfo.loadPreviewImage(activity, DisplayMetrics.DENSITY_HIGH)
+                        val widgetView = ImageView(activity)
+                        val lp = LinearLayout.LayoutParams(WIDGET_PREVIEW_SIZE, WIDGET_PREVIEW_SIZE)
+                        widgetView.setLayoutParams(lp)
+                        widgetView.setImageDrawable(widgetPreview)
+                        widgetView.setOnClickListener({
+                            setResult(
+                                RESULT_OK,
+                                Intent().putExtra(EditWidgetsActivity.ADD_WIDGET_INFO, widgetInfo)
+                            )
+                            finish()
+                        })
+
+                        addView(widgetView)
+                    }
+            } catch (e: RuntimeException) {
+                Log.e(TAG, "Exception fetching widget providers", e)
+            }
+        }
+    }
+
+    companion object {
+        private const val WIDGET_PREVIEW_SIZE = 400
+        private const val TAG = "WidgetPickerActivity"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 32e40c9..4b27af1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,6 +19,8 @@
 import android.app.Activity;
 
 import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.communal.widgets.EditWidgetsActivity;
+import com.android.systemui.communal.widgets.WidgetPickerActivity;
 import com.android.systemui.contrast.ContrastDialogActivity;
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.people.PeopleSpaceActivity;
@@ -150,7 +152,17 @@
     @ClassKey(SensorUseStartedActivity.class)
     public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
 
+    /** Inject into EditWidgetsActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(EditWidgetsActivity.class)
+    public abstract Activity bindEditWidgetsActivity(EditWidgetsActivity activity);
 
+    /** Inject into WidgetPickerActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(WidgetPickerActivity.class)
+    public abstract Activity bindWidgetPickerActivity(WidgetPickerActivity activity);
 
     /** Inject into SwitchToManagedProfileForCallActivity. */
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
new file mode 100644
index 0000000..b5d5803
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the device entry udfps refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object DeviceEntryUdfpsRefactor {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.deviceEntryUdfpsRefactor()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b5fe4c5..b2c4136 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -224,11 +224,6 @@
     val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
             unreleasedFlag("wallpaper_picker_grid_apply_button")
 
-    /** Whether to run the new udfps keyguard refactor code. */
-    // TODO(b/279440316): Tracking bug.
-    @JvmField
-    val REFACTOR_UDFPS_KEYGUARD_VIEWS = unreleasedFlag("refactor_udfps_keyguard_views")
-
     /** Provide new auth messages on the bouncer. */
     // TODO(b/277961132): Tracking bug.
     @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
@@ -285,11 +280,6 @@
             R.bool.flag_stop_pulsing_face_scanning_animation,
             "stop_pulsing_face_scanning_animation")
 
-    /** Flag to use a separate view for the alternate bouncer. */
-    // TODO(b/300440924): Tracking bug
-    @JvmField
-    val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
-
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
@@ -713,12 +703,6 @@
     @JvmField
     val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing")
 
-    // 3100 - Haptic interactions
-
-    // TODO(b/290213663): Tracking Bug
-    @JvmField
-    val ONE_WAY_HAPTICS_API_MIGRATION = releasedFlag("oneway_haptics_api_migration")
-
     /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
     @JvmField
     val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 1037b0e..017dac2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
@@ -134,7 +134,7 @@
         val indicationArea = KeyguardIndicationArea(context, null)
         keyguardIndicationController.setIndicationArea(indicationArea)
 
-        if (!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+        if (!DeviceEntryUdfpsRefactor.isEnabled) {
             lockIconViewController.get().setLockIconView(LockIconView(context, null))
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 41af9e8..cb5813e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -23,18 +23,17 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.scrim.ScrimView
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.statusbar.NotificationShadeWindowController
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
-import javax.inject.Inject
 
 @ExperimentalCoroutinesApi
 @SysUISingleton
@@ -42,13 +41,12 @@
 @Inject
 constructor(
     private val notificationShadeWindowView: NotificationShadeWindowView,
-    private val featureFlags: FeatureFlagsClassic,
     private val alternateBouncerViewModel: AlternateBouncerViewModel,
     @Application private val scope: CoroutineScope,
     private val notificationShadeWindowController: NotificationShadeWindowController,
 ) : CoreStartable {
     override fun start() {
-        if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+        if (!DeviceEntryUdfpsRefactor.isEnabled) {
             return
         }
 
@@ -79,6 +77,7 @@
         scope: CoroutineScope,
         notificationShadeWindowController: NotificationShadeWindowController,
     ) {
+        DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
         scope.launch {
             // forcePluginOpen is necessary to show over occluded apps.
             // This cannot be tied to the view's lifecycle because setting this allows the view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index a8b28bc..4b4a19e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -23,6 +23,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
@@ -51,6 +52,7 @@
         bgViewModel: DeviceEntryBackgroundViewModel,
         falsingManager: FalsingManager,
     ) {
+        DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
         val longPressHandlingView = view.longPressHandlingView
         val fgIconView = view.iconView
         val bgView = view.bgView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index b7a165c..55df466 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -26,14 +26,13 @@
 import androidx.constraintlayout.widget.ConstraintSet.RIGHT
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.android.systemui.statusbar.VibratorHelper
 import javax.inject.Inject
@@ -48,7 +47,6 @@
     private val falsingManager: FalsingManager,
     private val indicationController: KeyguardIndicationController,
     private val vibratorHelper: VibratorHelper,
-    private val featureFlags: FeatureFlagsClassic,
 ) : BaseShortcutSection() {
     override fun addViews(constraintLayout: ConstraintLayout) {
         if (keyguardBottomAreaRefactor()) {
@@ -86,11 +84,12 @@
         val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
         val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
 
-        val lockIconViewId = if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
-            R.id.device_entry_icon_view
-        } else {
-            R.id.lock_icon_view
-        }
+        val lockIconViewId =
+            if (DeviceEntryUdfpsRefactor.isEnabled) {
+                R.id.device_entry_icon_view
+            } else {
+                R.id.lock_icon_view
+            }
 
         constraintSet.apply {
             constrainWidth(R.id.start_button, width)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index 13ea8ff..790ddd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -31,6 +31,7 @@
 import com.android.keyguard.LockIconViewController
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -65,10 +66,7 @@
     private val deviceEntryIconViewId = R.id.device_entry_icon_view
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (
-            !keyguardBottomAreaRefactor() &&
-                !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
-        ) {
+        if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
             return
         }
 
@@ -77,7 +75,7 @@
         }
 
         val view =
-            if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+            if (DeviceEntryUdfpsRefactor.isEnabled) {
                 DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
             } else {
                 // keyguardBottomAreaRefactor()
@@ -87,7 +85,7 @@
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+        if (DeviceEntryUdfpsRefactor.isEnabled) {
             constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
                 DeviceEntryIconViewBinder.bind(
                     it,
@@ -140,7 +138,7 @@
     }
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
-        if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+        if (DeviceEntryUdfpsRefactor.isEnabled) {
             constraintLayout.removeView(deviceEntryIconViewId)
         } else {
             constraintLayout.removeView(R.id.lock_icon_view)
@@ -160,7 +158,7 @@
             }
 
         val iconId =
-            if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+            if (DeviceEntryUdfpsRefactor.isEnabled) {
                 deviceEntryIconViewId
             } else {
                 R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 165ee36..7512e51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,6 +24,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
@@ -81,7 +82,7 @@
             connect(R.id.nssl_placeholder, END, PARENT_ID, END)
 
             val lockId =
-                if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+                if (DeviceEntryUdfpsRefactor.isEnabled) {
                     R.id.device_entry_icon_view
                 } else {
                     R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 2c45da6..f2559ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -24,6 +24,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
@@ -81,7 +82,7 @@
             connect(R.id.nssl_placeholder, END, PARENT_ID, END)
 
             val lockId =
-                if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+                if (DeviceEntryUdfpsRefactor.isEnabled) {
                     R.id.device_entry_icon_view
                 } else {
                     R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index d0f2784..cf1dfdc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -46,6 +46,7 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
 import com.android.systemui.compose.ComposeFacade;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
@@ -448,7 +449,7 @@
                 }
 
                 boolean bouncerShowing;
-                if (mFeatureFlagsClassic.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+                if (DeviceEntryUdfpsRefactor.isEnabled()) {
                     bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
                             || mAlternateBouncerInteractor.isVisibleState();
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index cbe9d4b..4e77801 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -49,7 +49,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
@@ -186,8 +185,6 @@
     private long mLastFpFailureUptimeMillis;
     private int mNumConsecutiveFpFailures;
 
-    private final FeatureFlags mFeatureFlags;
-
     private static final class PendingAuthenticated {
         public final int userId;
         public final BiometricSourceType biometricSourceType;
@@ -291,7 +288,6 @@
             ScreenOffAnimationController screenOffAnimationController,
             VibratorHelper vibrator,
             SystemClock systemClock,
-            FeatureFlags featureFlags,
             DeviceEntryHapticsInteractor hapticsInteractor,
             Lazy<SelectedUserInteractor> selectedUserInteractor,
             BiometricUnlockInteractor biometricUnlockInteractor
@@ -322,7 +318,6 @@
         mVibratorHelper = vibrator;
         mLogger = biometricUnlockLogger;
         mSystemClock = systemClock;
-        mFeatureFlags = featureFlags;
         mOrderUnlockAndWake = resources.getBoolean(
                 com.android.internal.R.bool.config_orderUnlockAndWake);
         mHapticsInteractor = hapticsInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index cd7a9ea..46675c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -129,6 +129,7 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.emergency.EmergencyGesture;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -2777,7 +2778,7 @@
         mScrimController.setExpansionAffectsAlpha(!unlocking);
 
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+            if (!DeviceEntryUdfpsRefactor.isEnabled()) {
                 if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
                         && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
                         || mTransitionToFullShadeProgress > 0f)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 267b563..274b50f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -62,6 +62,7 @@
 import com.android.systemui.bouncer.ui.BouncerView;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.flags.FeatureFlags;
@@ -1573,7 +1574,7 @@
      * notification shade's child views.
      */
     public boolean shouldInterceptTouchEvent(MotionEvent event) {
-        if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+        if (DeviceEntryUdfpsRefactor.isEnabled()) {
             return false;
         }
         return mAlternateBouncerInteractor.isVisibleState();
@@ -1584,7 +1585,7 @@
      * showing.
      */
     public boolean onTouch(MotionEvent event) {
-        if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+        if (DeviceEntryUdfpsRefactor.isEnabled()) {
             return false;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2d95b09..f4122d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -50,8 +50,6 @@
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
 import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.events.ANIMATING_OUT
@@ -87,8 +85,6 @@
     @JvmField @Rule
     var mockitoRule = MockitoJUnit.rule()
 
-    private val featureFlags = FakeFeatureFlags()
-
     @Mock
     lateinit var callback: AuthDialogCallback
     @Mock
@@ -135,7 +131,6 @@
     @Before
     fun setup() {
         displayRepository = FakeDisplayRepository()
-        featureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false)
 
         displayStateInteractor =
             DisplayStateInteractorImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index d2b81e0..00ea78f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -17,14 +17,14 @@
 package com.android.systemui.biometrics
 
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.user.domain.UserDomainLayerModule
 import dagger.BindsInstance
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index af4bf36..e0567a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -45,6 +46,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -59,6 +61,7 @@
     private lateinit var widgetRepository: FakeCommunalWidgetRepository
     private lateinit var smartspaceRepository: FakeSmartspaceRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
 
     private lateinit var underTest: CommunalInteractor
 
@@ -76,6 +79,7 @@
         widgetRepository = withDeps.widgetRepository
         smartspaceRepository = withDeps.smartspaceRepository
         keyguardRepository = withDeps.keyguardRepository
+        editWidgetsActivityStarter = withDeps.editWidgetsActivityStarter
 
         underTest = withDeps.communalInteractor
     }
@@ -322,4 +326,11 @@
             runCurrent()
             assertThat(isCommunalShowing()).isEqualTo(true)
         }
+
+    @Test
+    fun testShowWidgetEditorStartsActivity() =
+        testScope.runTest {
+            underTest.showWidgetEditor()
+            verify(editWidgetsActivityStarter).startActivity()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index b439fcf..722c11d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -18,9 +18,9 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index bc4c237..49f7565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -18,9 +18,9 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 2dfc132..16d072e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -80,11 +80,7 @@
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
         configRepository = FakeConfigurationRepository()
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
-                set(Flags.FACE_AUTH_REFACTOR, false)
-            }
+        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
         KeyguardInteractorFactory.create(featureFlags = featureFlags).let {
             keyguardInteractor = it.keyguardInteractor
             keyguardRepository = it.repository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index 570dfb3..e9399cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 75bdcdd..a010ea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -67,10 +67,7 @@
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
 
         featureFlags =
-            FakeFeatureFlagsClassic().apply {
-                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
-                set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
-            }
+            FakeFeatureFlagsClassic().apply { set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) }
         underTest =
             DefaultDeviceEntryIconSection(
                 keyguardUpdateMonitor,
@@ -98,7 +95,7 @@
     @Test
     fun addViewsConditionally_migrateAndRefactorFlagsOn() {
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -107,7 +104,7 @@
     @Test
     fun addViewsConditionally_migrateFlagOff() {
         mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isEqualTo(0)
@@ -115,7 +112,7 @@
 
     @Test
     fun applyConstraints_udfps_refactor_off() {
-        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val cs = ConstraintSet()
         underTest.applyConstraints(cs)
 
@@ -127,7 +124,7 @@
 
     @Test
     fun applyConstraints_udfps_refactor_on() {
-        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val cs = ConstraintSet()
         underTest.applyConstraints(cs)
 
@@ -139,7 +136,7 @@
 
     @Test
     fun testCenterIcon_udfps_refactor_off() {
-        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val cs = ConstraintSet()
         underTest.centerIcon(Point(5, 6), 1F, cs)
 
@@ -155,7 +152,7 @@
 
     @Test
     fun testCenterIcon_udfps_refactor_on() {
-        featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val cs = ConstraintSet()
         underTest.centerIcon(Point(5, 6), 1F, cs)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 259c74f..d3019f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -21,18 +21,16 @@
 
 import android.view.View
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
@@ -46,6 +44,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.plugins.ClockController
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -64,7 +64,6 @@
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -109,10 +108,7 @@
 
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
 
-        val featureFlags =
-            FakeFeatureFlagsClassic().apply {
-                set(Flags.FACE_AUTH_REFACTOR, true)
-            }
+        val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
 
         val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
         keyguardInteractor = withDeps.keyguardInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index 4074851..c50be04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -18,15 +18,13 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -39,6 +37,8 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.collect.Range
@@ -77,15 +77,15 @@
                 mocks: TestMocksModule,
             ): TestComponent
         }
+    }
 
-        fun shadeExpanded(expanded: Boolean) {
-            if (expanded) {
-                shadeRepository.setQsExpansion(1f)
-            } else {
-                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-                shadeRepository.setQsExpansion(0f)
-                shadeRepository.setLockscreenShadeExpansion(0f)
-            }
+    private fun TestComponent.shadeExpanded(expanded: Boolean) {
+        if (expanded) {
+            shadeRepository.setQsExpansion(1f)
+        } else {
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeRepository.setQsExpansion(0f)
+            shadeRepository.setLockscreenShadeExpansion(0f)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 5c85357..26704da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,14 +18,12 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
@@ -35,13 +33,14 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import dagger.BindsInstance
 import dagger.Component
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -69,15 +68,15 @@
                 mocks: TestMocksModule,
             ): TestComponent
         }
+    }
 
-        fun shadeExpanded(expanded: Boolean) {
-            if (expanded) {
-                shadeRepository.setQsExpansion(1f)
-            } else {
-                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-                shadeRepository.setQsExpansion(0f)
-                shadeRepository.setLockscreenShadeExpansion(0f)
-            }
+    private fun TestComponent.shadeExpanded(expanded: Boolean) {
+        if (expanded) {
+            shadeRepository.setQsExpansion(1f)
+        } else {
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeRepository.setQsExpansion(0f)
+            shadeRepository.setLockscreenShadeExpansion(0f)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 4cbefa3d..ff3135a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,14 +18,12 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
@@ -35,6 +33,8 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.collect.Range
@@ -68,15 +68,15 @@
                 mocks: TestMocksModule,
             ): TestComponent
         }
+    }
 
-        fun shadeExpanded(expanded: Boolean) {
-            if (expanded) {
-                shadeRepository.setQsExpansion(1f)
-            } else {
-                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-                shadeRepository.setQsExpansion(0f)
-                shadeRepository.setLockscreenShadeExpansion(0f)
-            }
+    private fun TestComponent.shadeExpanded(expanded: Boolean) {
+        if (expanded) {
+            shadeRepository.setQsExpansion(1f)
+        } else {
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeRepository.setQsExpansion(0f)
+            shadeRepository.setLockscreenShadeExpansion(0f)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 4f56435..8afd8e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -18,13 +18,11 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
@@ -34,6 +32,8 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.collect.Range
@@ -69,15 +69,15 @@
                 mocks: TestMocksModule,
             ): TestComponent
         }
+    }
 
-        fun shadeExpanded(expanded: Boolean) {
-            if (expanded) {
-                shadeRepository.setQsExpansion(1f)
-            } else {
-                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-                shadeRepository.setQsExpansion(0f)
-                shadeRepository.setLockscreenShadeExpansion(0f)
-            }
+    private fun TestComponent.shadeExpanded(expanded: Boolean) {
+        if (expanded) {
+            shadeRepository.setQsExpansion(1f)
+        } else {
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeRepository.setQsExpansion(0f)
+            shadeRepository.setLockscreenShadeExpansion(0f)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 32acefe..5058b16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -67,11 +67,7 @@
         overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
         testScope = TestScope()
         shadeRepository = FakeShadeRepository()
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
-                set(Flags.FACE_AUTH_REFACTOR, false)
-            }
+        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
         KeyguardInteractorFactory.create(
                 featureFlags = featureFlags,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index 4f970d7..f039f53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -75,11 +75,7 @@
         keyguardRepository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
         fakeCommandQueue = FakeCommandQueue()
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
-                set(Flags.FACE_AUTH_REFACTOR, false)
-            }
+        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
         bouncerRepository = FakeKeyguardBouncerRepository()
         transitionRepository = FakeKeyguardTransitionRepository()
         shadeRepository = FakeShadeRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index 30e4866..c1805db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -83,11 +83,7 @@
         testScope = TestScope()
         transitionRepository = FakeKeyguardTransitionRepository()
         shadeRepository = FakeShadeRepository()
-        featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
-                set(Flags.FACE_AUTH_REFACTOR, false)
-            }
+        featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
         KeyguardInteractorFactory.create(
                 featureFlags = featureFlags,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2dd0af7..d89491c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -30,6 +30,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.LockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.back.domain.interactor.BackActionInteractor
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -51,7 +52,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.ALTERNATE_BOUNCER_VIEW
 import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
 import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
 import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
@@ -97,7 +97,6 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
@@ -112,8 +111,9 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -198,7 +198,7 @@
         featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true)
         featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        featureFlagsClassic.set(ALTERNATE_BOUNCER_VIEW, false)
+        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
 
         mCommunalRepository = FakeCommunalRepository()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 4b62906..9c8816c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -29,7 +29,6 @@
 import com.android.keyguard.LockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.back.domain.interactor.BackActionInteractor
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -60,7 +59,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.log.BouncerLogger
-import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.statusbar.DragDownHelper
@@ -114,8 +112,6 @@
     @Mock private lateinit var centralSurfaces: CentralSurfaces
     @Mock private lateinit var dozeServiceHost: DozeServiceHost
     @Mock private lateinit var dozeScrimController: DozeScrimController
-    @Mock private lateinit var backActionInteractor: BackActionInteractor
-    @Mock private lateinit var powerInteractor: PowerInteractor
     @Mock private lateinit var dockManager: DockManager
     @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
     @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
@@ -192,7 +188,7 @@
         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
+        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         testScope = TestScope()
         controller =
             NotificationShadeWindowViewController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index 09700e1..61e4370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -22,13 +22,11 @@
 import android.content.pm.UserInfo
 import android.os.UserManager
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -45,6 +43,8 @@
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
index f3c875e..92eb6ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
@@ -19,13 +19,11 @@
 import android.content.pm.UserInfo
 import android.os.UserManager
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -35,6 +33,8 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.power.data.repository.FakePowerRepository
 import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.phone.DozeParameters
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index 470e0c6..729f3f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -19,13 +19,11 @@
 import android.content.pm.UserInfo
 import android.os.UserManager
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -35,6 +33,8 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.power.data.repository.FakePowerRepository
 import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 43922e9..3efcf7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,8 +5,8 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
+import com.android.systemui.TestMocksModule
 import com.android.systemui.ExpandHelper
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollectorFake
@@ -611,9 +611,9 @@
         @Component.Factory
         interface Factory {
             fun create(
-                @BindsInstance test: SysuiTestCase,
-                featureFlags: FakeFeatureFlagsClassicModule,
-                mocks: TestMocksModule,
+                    @BindsInstance test: SysuiTestCase,
+                    featureFlags: FakeFeatureFlagsClassicModule,
+                    mocks: TestMocksModule,
             ): TestComponent
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
index d479937..f3094cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -17,13 +17,13 @@
 package com.android.systemui.statusbar.notification.data.repository
 
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
index 707026e..b775079 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
@@ -16,8 +16,8 @@
 
 import android.app.StatusBarManager
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index bb6f1b6..bb3113a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -14,20 +14,17 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
 import com.google.common.truth.Truth.assertThat
 import dagger.BindsInstance
 import dagger.Component
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 
 @SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 05deb1c..0341035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -17,14 +17,14 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index c2c33de..68761ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -18,14 +18,12 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
 import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
@@ -39,6 +37,8 @@
 import com.android.systemui.power.data.repository.FakePowerRepository
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 87e9735..c2a1519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -20,14 +20,12 @@
 import android.graphics.drawable.Icon
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
 import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
 import com.android.systemui.flags.Flags
@@ -42,6 +40,8 @@
 import com.android.systemui.power.data.repository.FakePowerRepository
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 7423c2d..917569c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -19,16 +19,16 @@
 import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index db8f217..9c70c82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,13 +19,11 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
 import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
@@ -39,6 +37,8 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.user.domain.UserDomainLayerModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 164325a..e61b4f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -50,7 +49,6 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
@@ -130,14 +128,11 @@
     @Mock
     private BiometricUnlockInteractor mBiometricUnlockInteractor;
     private final FakeSystemClock mSystemClock = new FakeSystemClock();
-    private FakeFeatureFlags mFeatureFlags;
     private BiometricUnlockController mBiometricUnlockController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
         when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true);
@@ -165,7 +160,6 @@
                 mAuthController, mStatusBarStateController,
                 mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
                 mSystemClock,
-                mFeatureFlags,
                 mDeviceEntryHapticsInteractor,
                 () -> mSelectedUserInteractor,
                 mBiometricUnlockInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 251718d..6570724 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -352,8 +352,8 @@
         mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
         // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
-        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
 
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 46b3996..225ddb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -178,7 +178,7 @@
         mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
         mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
         mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
-        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
+        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
 
         when(mNotificationShadeWindowController.getWindowRootView())
                 .thenReturn(mNotificationShadeWindowView);
@@ -771,7 +771,7 @@
         mStatusBarKeyguardViewManager.addCallback(mCallback);
 
         // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
-        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
         // THEN the touch is not acted upon
@@ -781,7 +781,7 @@
     @Test
     public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
         // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
-        mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
         // THEN the touch is not intercepted
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 78e7971..99e62ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -19,13 +19,13 @@
 import android.app.NotificationManager.Policy
 import android.provider.Settings
 import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
 import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository
 import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
index 360aa0f..de310b4 100644
--- a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android
+package com.android.systemui
 
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/SysUITestModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
index d8e7cd6..d0c1267 100644
--- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
@@ -13,15 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android
+package com.android.systemui
 
 import android.content.Context
 import android.content.res.Resources
 import android.testing.TestableContext
 import android.testing.TestableResources
-import com.android.systemui.FakeSystemUiModule
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.SysuiTestableContext
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.broadcast.FakeBroadcastDispatcher
 import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/TestMocksModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index fd50f15..37a4f61 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android
+package com.android.systemui
 
 import android.app.ActivityManager
 import android.app.admin.DevicePolicyManager
@@ -24,7 +24,6 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardViewController
-import com.android.systemui.GuestResumeSessionReceiver
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index 0c821ea..3aee889 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -40,6 +41,7 @@
         smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
         tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
         appWidgetHost: AppWidgetHost = mock(),
+        editWidgetsActivityStarter: EditWidgetsActivityStarter = mock(),
     ): WithDependencies {
         val withDeps =
             CommunalTutorialInteractorFactory.create(
@@ -57,6 +59,7 @@
             withDeps.keyguardInteractor,
             withDeps.communalTutorialInteractor,
             appWidgetHost,
+            editWidgetsActivityStarter,
             CommunalInteractor(
                 communalRepository,
                 widgetRepository,
@@ -64,6 +67,7 @@
                 smartspaceRepository,
                 withDeps.communalTutorialInteractor,
                 appWidgetHost,
+                editWidgetsActivityStarter,
             ),
         )
     }
@@ -78,6 +82,7 @@
         val keyguardInteractor: KeyguardInteractor,
         val tutorialInteractor: CommunalTutorialInteractor,
         val appWidgetHost: AppWidgetHost,
+        val editWidgetsActivityStarter: EditWidgetsActivityStarter,
         val communalInteractor: CommunalInteractor,
     )
 }
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index b086406..acb5911 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -19,5 +19,16 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.backup-sources"],
     libs: ["services.core"],
-    static_libs: ["app-compat-annotations"],
+    static_libs: ["app-compat-annotations", "backup_flags_lib"],
+}
+
+aconfig_declarations {
+    name: "backup_flags",
+    package: "com.android.server.backup",
+    srcs: ["flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "backup_flags_lib",
+    aconfig_declarations: "backup_flags",
 }
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
new file mode 100644
index 0000000..d695d36
--- /dev/null
+++ b/services/backup/flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.server.backup"
+
+flag {
+    name: "enable_skipping_restore_launched_apps"
+    namespace: "onboarding"
+    description: "Enforce behavior determined by BackupTransport implementation on whether to skip "
+            "restore for apps that have been launched."
+    bug: "308401499"
+    is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 70d7fac..9f7b627 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -24,6 +24,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.backup.BackupAgent;
 import android.app.backup.BackupAnnotations.BackupDestination;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IRestoreObserver;
@@ -32,11 +33,15 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.backup.Flags;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
@@ -296,12 +301,26 @@
         return -1;
     }
 
-    private BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
+    @VisibleForTesting
+    BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
         // TODO(b/182986784): Remove device name comparison once a designated field for operation
         //  type is added to RestoreSet object.
         int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device)
                 ? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD;
-        return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
+
+        if (!Flags.enableSkippingRestoreLaunchedApps()) {
+            return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
+        }
+
+        boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags
+                & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0;
+
+        return new BackupEligibilityRules(mBackupManagerService.getPackageManager(),
+                LocalServices.getService(PackageManagerInternal.class),
+                mUserId,
+                mBackupManagerService.getContext(),
+                backupDestination,
+                skipRestoreForLaunchedApps);
     }
 
     public synchronized int restorePackage(String packageName, IRestoreObserver observer,
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index bbec79d..96a873e 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -63,6 +63,7 @@
 import com.android.server.backup.BackupAndRestoreFeatureFlags;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.BackupUtils;
+import com.android.server.backup.Flags;
 import com.android.server.backup.OperationStorage;
 import com.android.server.backup.OperationStorage.OpType;
 import com.android.server.backup.PackageManagerBackupAgent;
@@ -263,7 +264,14 @@
                         continue;
                     }
 
-                    if (backupEligibilityRules.appIsEligibleForBackup(info.applicationInfo)) {
+
+                    ApplicationInfo applicationInfo = info.applicationInfo;
+                    if (backupEligibilityRules.appIsEligibleForBackup(applicationInfo)) {
+                        if (Flags.enableSkippingRestoreLaunchedApps()
+                            && !backupEligibilityRules.isAppEligibleForRestore(applicationInfo)) {
+                            continue;
+                        }
+
                         mAcceptSet.add(info);
                     }
                 } catch (NameNotFoundException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 7c47f1e..f24a3c1 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -80,6 +80,7 @@
     private final int mUserId;
     private boolean mIsProfileUser = false;
     @BackupDestination  private final int mBackupDestination;
+    private final boolean mSkipRestoreForLaunchedApps;
 
     /**
      * When  this change is enabled, {@code adb backup}  is automatically turned on for apps
@@ -112,12 +113,23 @@
             int userId,
             Context context,
             @BackupDestination int backupDestination) {
+        this(packageManager, packageManagerInternal, userId, context, backupDestination,
+                /* skipRestoreForLaunchedApps */ false);
+    }
+
+    public BackupEligibilityRules(PackageManager packageManager,
+            PackageManagerInternal packageManagerInternal,
+            int userId,
+            Context context,
+            @BackupDestination int backupDestination,
+            boolean skipRestoreForLaunchedApps) {
         mPackageManager = packageManager;
         mPackageManagerInternal = packageManagerInternal;
         mUserId = userId;
         mBackupDestination = backupDestination;
         UserManager userManager = context.getSystemService(UserManager.class);
         mIsProfileUser = userManager.isProfile();
+        mSkipRestoreForLaunchedApps = skipRestoreForLaunchedApps;
     }
 
     /**
@@ -132,6 +144,9 @@
      *     <li>it is the special shared-storage backup package used for 'adb backup'
      * </ol>
      *
+     * These eligibility conditions are also checked before restore, in case the backup happened on
+     * a device / from the version of the app where these rules were not enforced.
+     *
      * However, the above eligibility rules are ignored for non-system apps in in case of
      * device-to-device migration, see {@link BackupDestination}.
      */
@@ -283,6 +298,27 @@
         }
     }
 
+    /**
+     * Determine if data restore should be run for the given package.
+     *
+     * <p>This is used in combination with {@link #appIsEligibleForBackup(ApplicationInfo)} that
+     * checks whether the backup being restored should have happened in the first place.</p>
+     */
+    public boolean isAppEligibleForRestore(ApplicationInfo app) {
+        if (!mSkipRestoreForLaunchedApps) {
+            return true;
+        }
+
+        // If an app implemented a BackupAgent, they are expected to handle being restored even
+        // after first launch and avoid conflicts between existing app data and restored data.
+        if (app.backupAgentName != null) {
+            return true;
+        }
+
+        // Otherwise only restore an app if it hasn't been launched before.
+        return !mPackageManagerInternal.wasPackageEverLaunched(app.packageName, mUserId);
+    }
+
     /** Avoid backups of 'disabled' apps. */
     @VisibleForTesting
     boolean appIsDisabled(
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 22693ab..49457fb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -199,7 +199,7 @@
         "biometrics_flags_lib",
         "am_flags_lib",
         "com_android_wm_shell_flags_lib",
-        "android.app.flags-aconfig-java"
+        "service-jobscheduler-deviceidle.flags-aconfig-java",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 8046dbf..11f4e5f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1607,6 +1607,19 @@
         final long secondToken = Binder.clearCallingIdentity();
         try {
             final int displayId;
+            final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+                    packageName, callingUid, virtualDisplayConfig);
+
+            if (virtualDisplayConfig.isHomeSupported()) {
+                if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+                    Slog.w(TAG, "Display created with home support but lacks "
+                            + "VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the home support request.");
+                } else {
+                    mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId,
+                            Display.TYPE_VIRTUAL, true);
+                }
+            }
+
             synchronized (mSyncRoot) {
                 displayId =
                         createVirtualDisplayLocked(
@@ -1614,6 +1627,7 @@
                                 projection,
                                 callingUid,
                                 packageName,
+                                displayUniqueId,
                                 virtualDevice,
                                 surface,
                                 flags,
@@ -1625,6 +1639,13 @@
                 }
             }
 
+            if (displayId == Display.INVALID_DISPLAY && virtualDisplayConfig.isHomeSupported()
+                    && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+                // Failed to create the virtual display, so we should clean up the WM settings
+                // because it won't receive the onDisplayRemoved callback.
+                mWindowManagerInternal.clearDisplaySettings(displayUniqueId, Display.TYPE_VIRTUAL);
+            }
+
             // Build a session describing the MediaProjection instance, if there is one. A session
             // for a VirtualDisplay or physical display mirroring is handled in DisplayContent.
             ContentRecordingSession session = null;
@@ -1698,6 +1719,7 @@
             IMediaProjection projection,
             int callingUid,
             String packageName,
+            String uniqueId,
             IVirtualDevice virtualDevice,
             Surface surface,
             int flags,
@@ -1710,10 +1732,9 @@
             return -1;
         }
 
-
         Slog.d(TAG, "Virtual Display: creating DisplayDevice with VirtualDisplayAdapter");
         DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
-                callback, projection, callingUid, packageName, surface, flags,
+                callback, projection, callingUid, packageName, uniqueId, surface, flags,
                 virtualDisplayConfig);
         if (device == null) {
             Slog.w(TAG, "Virtual Display: VirtualDisplayAdapter failed to create DisplayDevice");
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index b002587..90e32a6 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -64,7 +64,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
-import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * A display adapter that provides virtual displays on behalf of applications.
@@ -72,15 +72,17 @@
  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
  * </p>
  */
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public class VirtualDisplayAdapter extends DisplayAdapter {
     static final String TAG = "VirtualDisplayAdapter";
-    static final boolean DEBUG = false;
 
     // Unique id prefix for virtual displays
     @VisibleForTesting
     static final String UNIQUE_ID_PREFIX = "virtual:";
 
+    // Unique id suffix for virtual displays
+    private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0);
+
     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();
     private final Handler mHandler;
     private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
@@ -111,8 +113,8 @@
     }
 
     public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
-            IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface,
-            int flags, VirtualDisplayConfig virtualDisplayConfig) {
+            IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId,
+            Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) {
         IBinder appToken = callback.asBinder();
         if (mVirtualDisplayDevices.containsKey(appToken)) {
             Slog.wtfStack(TAG,
@@ -125,23 +127,13 @@
 
         IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure,
                 virtualDisplayConfig.getRequestedRefreshRate());
-        final String baseUniqueId =
-                UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
-        final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
-        String uniqueId = virtualDisplayConfig.getUniqueId();
-        if (uniqueId == null) {
-            uniqueId = baseUniqueId + uniqueIndex;
-        } else {
-            uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
-        }
         MediaProjectionCallback mediaProjectionCallback =  null;
         if (projection != null) {
             mediaProjectionCallback = new MediaProjectionCallback(appToken);
         }
         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
-                ownerUid, ownerPackageName, surface, flags,
-                new Callback(callback, mHandler), projection, mediaProjectionCallback,
-                uniqueId, uniqueIndex, virtualDisplayConfig);
+                ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+                projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig);
 
         mVirtualDisplayDevices.put(appToken, device);
 
@@ -219,26 +211,20 @@
     }
 
     /**
-     * Returns the next unique index for the uniqueIdPrefix
+     * Generates a virtual display's unique identifier.
+     *
+     * <p>It is always prefixed with "virtual:package-name". If the provided config explicitly
+     * specifies a unique ID, then it's simply appended. Otherwise, the UID, display name and a
+     * unique index are appended.</p>
+     *
+     * <p>The unique index is incremented for every virtual display unique ID generation and serves
+     * for differentiating between displays with the same name created by the same owner.</p>
      */
-    private int getNextUniqueIndex(String uniqueIdPrefix) {
-        if (mVirtualDisplayDevices.isEmpty()) {
-            return 0;
-        }
-
-        int nextUniqueIndex = 0;
-        Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator();
-        while (it.hasNext()) {
-            VirtualDisplayDevice device = it.next();
-            if (device.getUniqueId().startsWith(uniqueIdPrefix)
-                    && device.mUniqueIndex >= nextUniqueIndex) {
-                // Increment the next unique index to be greater than ones we have already ran
-                // across for displays that have the same unique Id prefix.
-                nextUniqueIndex = device.mUniqueIndex + 1;
-            }
-        }
-
-        return nextUniqueIndex;
+    static String generateDisplayUniqueId(String packageName, int uid,
+            VirtualDisplayConfig config) {
+        return UNIQUE_ID_PREFIX + packageName + ((config.getUniqueId() != null)
+                ? (":" + config.getUniqueId())
+                : ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement()));
     }
 
     private void handleBinderDiedLocked(IBinder appToken) {
@@ -278,7 +264,6 @@
         private int mDisplayState;
         private boolean mStopped;
         private int mPendingChanges;
-        private int mUniqueIndex;
         private Display.Mode mMode;
         private boolean mIsDisplayOn;
         private int mDisplayIdToMirror;
@@ -287,7 +272,7 @@
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName, Surface surface, int flags,
                 Callback callback, IMediaProjection projection,
-                IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
+                IMediaProjectionCallback mediaProjectionCallback, String uniqueId,
                 VirtualDisplayConfig virtualDisplayConfig) {
             super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
             mAppToken = appToken;
@@ -306,7 +291,6 @@
             mMediaProjectionCallback = mediaProjectionCallback;
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
-            mUniqueIndex = uniqueIndex;
             mIsDisplayOn = surface != null;
             mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
             mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index 3f02266..f48178c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -124,7 +124,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
+            return mWindowManagerInternal.isHomeSupportedOnDisplay(displayId);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7f80807..4924810 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -184,6 +184,7 @@
 import android.graphics.Region.Op;
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.VirtualDisplayConfig;
 import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.Debug;
@@ -2191,7 +2192,7 @@
      * @see DisplayWindowPolicyController#getCustomHomeComponent() ()
      */
     @Nullable ComponentName getCustomHomeComponent() {
-        if (!supportsSystemDecorations() || mDwpcHelper == null) {
+        if (!isHomeSupported() || mDwpcHelper == null) {
             return null;
         }
         return mDwpcHelper.getCustomHomeComponent();
@@ -5772,6 +5773,17 @@
                 && isTrusted();
     }
 
+    /**
+     * Checks if this display is configured and allowed to show home activity and wallpaper.
+     *
+     * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}
+     * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p>
+     */
+    boolean isHomeSupported() {
+        return (mWmService.mDisplayWindowSettings.isHomeSupportedLocked(this) && isTrusted())
+                || supportsSystemDecorations();
+    }
+
     SurfaceControl getWindowingLayer() {
         return mWindowingLayer;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index e1753d7..7a95c2d 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -237,6 +237,37 @@
         mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
     }
 
+    boolean isHomeSupportedLocked(@NonNull DisplayContent dc) {
+        if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            // Default display should show home.
+            return true;
+        }
+
+        final DisplayInfo displayInfo = dc.getDisplayInfo();
+        final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+        return settings.mIsHomeSupported != null
+                ? settings.mIsHomeSupported
+                : shouldShowSystemDecorsLocked(dc);
+    }
+
+    void setHomeSupportedOnDisplayLocked(@NonNull String displayUniqueId, int displayType,
+            boolean supported) {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.uniqueId = displayUniqueId;
+        displayInfo.type = displayType;
+        final SettingsProvider.SettingsEntry overrideSettings =
+                mSettingsProvider.getOverrideSettings(displayInfo);
+        overrideSettings.mIsHomeSupported = supported;
+        mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+    }
+
+    void clearDisplaySettings(@NonNull String displayUniqueId, int displayType) {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.uniqueId = displayUniqueId;
+        displayInfo.type = displayType;
+        mSettingsProvider.clearDisplaySettings(displayInfo);
+    }
+
     @DisplayImePolicy
     int getImePolicyLocked(@NonNull DisplayContent dc) {
         if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
@@ -382,11 +413,18 @@
         void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides);
 
         /**
-         * Called when a display is removed to cleanup.
+         * Called when a display is removed to cleanup. Note that for non-virtual displays the
+         * relevant settings entry will be kept, if non-empty.
          */
         void onDisplayRemoved(@NonNull DisplayInfo info);
 
         /**
+         * Explicitly removes all settings entory for the given {@link DisplayInfo}, even if it is
+         * not empty.
+         */
+        void clearDisplaySettings(@NonNull DisplayInfo info);
+
+        /**
          * Settings for a display.
          */
         class SettingsEntry {
@@ -411,6 +449,8 @@
             @Nullable
             Boolean mShouldShowSystemDecors;
             @Nullable
+            Boolean mIsHomeSupported;
+            @Nullable
             Integer mImePolicy;
             @Nullable
             Integer mFixedToUserRotation;
@@ -479,6 +519,10 @@
                     mShouldShowSystemDecors = other.mShouldShowSystemDecors;
                     changed = true;
                 }
+                if (!Objects.equals(other.mIsHomeSupported, mIsHomeSupported)) {
+                    mIsHomeSupported = other.mIsHomeSupported;
+                    changed = true;
+                }
                 if (!Objects.equals(other.mImePolicy, mImePolicy)) {
                     mImePolicy = other.mImePolicy;
                     changed = true;
@@ -561,6 +605,11 @@
                     mShouldShowSystemDecors = delta.mShouldShowSystemDecors;
                     changed = true;
                 }
+                if (delta.mIsHomeSupported != null && !Objects.equals(
+                        delta.mIsHomeSupported, mIsHomeSupported)) {
+                    mIsHomeSupported = delta.mIsHomeSupported;
+                    changed = true;
+                }
                 if (delta.mImePolicy != null
                         && !Objects.equals(delta.mImePolicy, mImePolicy)) {
                     mImePolicy = delta.mImePolicy;
@@ -599,6 +648,7 @@
                         && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
                         && mShouldShowWithInsecureKeyguard == null
                         && mShouldShowSystemDecors == null
+                        && mIsHomeSupported == null
                         && mImePolicy == null
                         && mFixedToUserRotation == null
                         && mIgnoreOrientationRequest == null
@@ -622,6 +672,7 @@
                         && Objects.equals(mShouldShowWithInsecureKeyguard,
                                 that.mShouldShowWithInsecureKeyguard)
                         && Objects.equals(mShouldShowSystemDecors, that.mShouldShowSystemDecors)
+                        && Objects.equals(mIsHomeSupported, that.mIsHomeSupported)
                         && Objects.equals(mImePolicy, that.mImePolicy)
                         && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
                         && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
@@ -633,9 +684,9 @@
             public int hashCode() {
                 return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
                         mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
-                        mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
-                        mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
-                        mDontMoveToTop);
+                        mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported,
+                        mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest,
+                        mIgnoreDisplayCutout, mDontMoveToTop);
             }
 
             @Override
@@ -651,6 +702,7 @@
                         + ", mRemoveContentMode=" + mRemoveContentMode
                         + ", mShouldShowWithInsecureKeyguard=" + mShouldShowWithInsecureKeyguard
                         + ", mShouldShowSystemDecors=" + mShouldShowSystemDecors
+                        + ", mIsHomeSupported=" + mIsHomeSupported
                         + ", mShouldShowIme=" + mImePolicy
                         + ", mFixedToUserRotation=" + mFixedToUserRotation
                         + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index ea668fa..c79565a 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -164,6 +164,11 @@
         mOverrideSettings.onDisplayRemoved(info);
     }
 
+    @Override
+    public void clearDisplaySettings(@NonNull DisplayInfo info) {
+        mOverrideSettings.clearDisplaySettings(info);
+    }
+
     @VisibleForTesting
     int getOverrideSettingsSize() {
         return mOverrideSettings.mSettings.size();
@@ -291,6 +296,12 @@
             }
         }
 
+        void clearDisplaySettings(@NonNull DisplayInfo info) {
+            final String identifier = getIdentifier(info);
+            mSettings.remove(identifier);
+            mVirtualDisplayIdentifiers.remove(identifier);
+        }
+
         private void writeSettings() {
             final FileData fileData = new FileData();
             fileData.mIdentifierType = mIdentifierType;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c8fd16d..997b608 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -393,8 +393,12 @@
      */
     void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) {
         final boolean clear = activity == null;
+        final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null;
         mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
         mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer);
+        if (clear && wasActive) {
+            setUpdateInputWindowsNeededLw();
+        }
     }
 
     private static <T> T getWeak(WeakReference<T> ref) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5227a52..fe2c250 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1696,8 +1696,8 @@
         }
 
         final DisplayContent display = taskDisplayArea.getDisplayContent();
-        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
-            // Can't launch home on display that doesn't support system decorations.
+        if (display == null || display.isRemoved() || !display.isHomeSupported()) {
+            // Can't launch home on display that doesn't support home.
             return false;
         }
 
@@ -3126,10 +3126,11 @@
         if (preferredFocusableRootTask != null) {
             return preferredFocusableRootTask;
         }
-        if (preferredDisplayArea.mDisplayContent.supportsSystemDecorations()) {
+
+        if (preferredDisplayArea.mDisplayContent.isHomeSupported()) {
             // Stop looking for focusable root task on other displays because the preferred display
-            // supports system decorations. Home activity would be launched on the same display if
-            // no focusable root task found.
+            // supports home. Home activity would be launched on the same display if no focusable
+            // root task found.
             return null;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index f0a6654..c57983c 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1767,7 +1767,7 @@
      * Exposes the home task capability of the TaskDisplayArea
      */
     boolean canHostHomeTask() {
-        return mDisplayContent.supportsSystemDecorations() && mCanHostHomeTask;
+        return mDisplayContent.isHomeSupported() && mCanHostHomeTask;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f700944..caa57bb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1344,6 +1344,7 @@
             final DisplayContent dc =
                     mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
             dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
+            dc.getInputMonitor().updateInputWindowsLw(false /* force */);
         }
         if (mTransientLaunches != null) {
             for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9f1bccb..92bd00e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -28,6 +28,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.VirtualDisplayConfig;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Message;
@@ -752,9 +753,31 @@
     public abstract Context getTopFocusedDisplayUiContext();
 
     /**
-     * Checks if this display is configured and allowed to show system decorations.
+     * Sets whether the relevant display content can host the relevant home activity and wallpaper.
+     *
+     * @param displayUniqueId The unique ID of the display. Note that the display may not yet be
+     *   created, but whenever it is, this property will be applied.
+     * @param displayType The type of the display, e.g. {@link Display#TYPE_VIRTUAL}.
+     * @param supported Whether home and wallpaper are supported on this display.
      */
-    public abstract boolean shouldShowSystemDecorOnDisplay(int displayId);
+    public abstract void setHomeSupportedOnDisplay(
+            @NonNull String displayUniqueId, int displayType, boolean supported);
+
+    /**
+     * Checks if this display is configured and allowed to show home activity and wallpaper.
+     *
+     * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}
+     * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p>
+     */
+    public abstract boolean isHomeSupportedOnDisplay(int displayId);
+
+    /**
+     * Removes any settings relevant to the given display.
+     *
+     * <p>This may be used when a property is set for a display unique ID before the display
+     * creation but the actual display creation failed for some reason.</p>
+     */
+    public abstract void clearDisplaySettings(@NonNull String displayUniqueId, int displayType);
 
     /**
      * Indicates the policy for how the display should show IME.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 757d6d6..809e2d0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8269,9 +8269,41 @@
         }
 
         @Override
-        public boolean shouldShowSystemDecorOnDisplay(int displayId) {
+        public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType,
+                boolean supported) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mGlobalLock) {
+                    mDisplayWindowSettings.setHomeSupportedOnDisplayLocked(
+                            displayUniqueId, displayType, supported);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+        @Override
+        public boolean isHomeSupportedOnDisplay(int displayId) {
             synchronized (mGlobalLock) {
-                return WindowManagerService.this.shouldShowSystemDecors(displayId);
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                if (displayContent == null) {
+                    ProtoLog.w(WM_ERROR, "Attempted to get home support flag of a display that "
+                            + "does not exist: %d", displayId);
+                    return false;
+                }
+                return displayContent.isHomeSupported();
+            }
+        }
+
+        @Override
+        public void clearDisplaySettings(String displayUniqueId, int displayType) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mGlobalLock) {
+                    mDisplayWindowSettings.clearDisplaySettings(displayUniqueId, displayType);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
         }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3775ac94..0bf4654 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -196,7 +196,8 @@
     @Rule(order = 1)
     public Expect expect = Expect.create();
     @Rule
-    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public SetFlagsRule mSetFlagsRule =
+            new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
 
     private Context mContext;
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 8bbacc4..73e7ba0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -77,7 +77,7 @@
 
         DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
                 /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
-                /* surface= */ null, /* flags= */ 0, config);
+                /* uniqueId= */ "uniqueId", /* surface= */ null, /* flags= */ 0, config);
 
         assertNotNull(result);
     }
@@ -89,12 +89,12 @@
         VirtualDisplayConfig config2 = new VirtualDisplayConfig.Builder("test2", /* width= */ 1,
                 /* height= */ 1, /* densityDpi= */ 1).build();
         mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null,
-                /* ownerUid= */ 10, /* packageName= */ "testpackage", /* surface= */ null,
-                /* flags= */ 0, config1);
+                /* ownerUid= */ 10, /* packageName= */ "testpackage", /* uniqueId= */ "uniqueId1",
+                /* surface= */ null, /* flags= */ 0, config1);
 
         DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
                 /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
-                /* surface= */ null, /* flags= */ 0, config2);
+                /* uniqueId= */ "uniqueId2", /* surface= */ null, /* flags= */ 0, config2);
 
         assertNull(result);
     }
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 063af57..113511e 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -46,6 +46,7 @@
         "androidx.test.espresso.core",
         "androidx.test.espresso.contrib",
         "androidx.test.ext.truth",
+        "backup_flags_lib",
         "flag-junit",
         "frameworks-base-testutils",
         "hamcrest-library",
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
new file mode 100644
index 0000000..0006d0a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.restore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.RestoreSet;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.backup.Flags;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.utils.BackupEligibilityRules;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActiveRestoreSessionTest {
+    private static final String TEST_APP_NAME = "test_app";
+
+    private ActiveRestoreSession mRestoreSession;
+    private ApplicationInfo mTestApp;
+
+    @Mock
+    private UserBackupManagerService mBackupManagerService;
+    @Mock
+    private BackupEligibilityRules mBackupEligibilityRules;
+    @Mock
+    private Context mContext;
+    @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
+    private TransportManager mTransportManager;
+    @Mock private UserManager mUserManager;
+
+    @Rule
+    public final SetFlagsRule mFlagsRule = new SetFlagsRule();
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule
+            .Builder(/* testClassInstance */ this)
+            .mockStatic(LocalServices.class)
+            .afterSessionFinished(
+                    () -> LocalServices.removeServiceForTest(PackageManagerInternal.class)
+            ).build();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(/* testClass */ this);
+        when(mBackupEligibilityRules.isAppEligibleForRestore(any())).thenReturn(true);
+        when(mBackupManagerService.getEligibilityRulesForOperation(anyInt())).thenReturn(
+                mBackupEligibilityRules);
+        when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+        when(mBackupManagerService.getContext()).thenReturn(mContext);
+        when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
+        when(LocalServices.getService(PackageManagerInternal.class)).thenReturn(
+                mPackageManagerInternal);
+
+        mRestoreSession = new ActiveRestoreSession(mBackupManagerService,
+                /* packageName */ null,
+                /* transportName */ "",
+                mBackupEligibilityRules);
+        mTestApp = new ApplicationInfo();
+        mTestApp.packageName = TEST_APP_NAME;
+    }
+
+    @Test
+    public void testGetBackupEligibilityRules_skipRestoreFlagOn_skipsLaunchedAppRestore() {
+        mFlagsRule.enableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS);
+        RestoreSet restoreSet = new RestoreSet(
+                /* name */ null,
+                /* device */ null,
+                /* token */ 0,
+                /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS);
+        when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt()))
+                .thenReturn(true);
+
+        BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules(
+                restoreSet);
+
+        assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isFalse();
+    }
+
+    @Test
+    public void testGetBackupEligibilityRules_skipRestoreFlagOff_allowsAppRestore() {
+        mFlagsRule.disableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS);
+        RestoreSet restoreSet = new RestoreSet(
+                /* name */ null,
+                /* device */ null,
+                /* token */ 0,
+                /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS);
+        when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt()))
+                .thenReturn(true);
+
+        BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules(
+                restoreSet);
+
+        assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isTrue();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 0306655..290b260 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -860,6 +860,66 @@
         assertThat(result).isFalse();
     }
 
+    @Test
+    public void isAppEligibleForRestore_hasBeenLaunched_returnsFalse() {
+        when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+                .thenReturn(true);
+        ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+                /* backupAgentName */ null);
+        BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+                mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+                /* skipRestoreForLaunchedApps */ true);
+
+        boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+        assertThat(isEligible).isFalse();
+    }
+
+    @Test
+    public void isAppEligibleForRestore_hasNotBeenLaunched_returnsTrue() {
+        when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+                .thenReturn(false);
+        ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+                /* backupAgentName */ null);
+        BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+                mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+                /* skipRestoreForLaunchedApps */ false);
+
+        boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+        assertThat(isEligible).isTrue();
+    }
+
+    @Test
+    public void isAppEligibleForRestore_launchedButHasBackupAgent_returnsTrue() {
+        when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+                .thenReturn(true);
+        ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+                /* backupAgentName */ "BackupAgent");
+        BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+                mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+                /* skipRestoreForLaunchedApps */ false);
+
+        boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+        assertThat(isEligible).isTrue();
+    }
+
+    @Test
+    public void isAppEligibleForRestore_doNotSkipRestoreForLaunched_returnsTrue() {
+        when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+                .thenReturn(true);
+        ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+                /* backupAgentName */ null);
+        BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+                mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+                /* skipRestoreForLaunchedApps */ false);
+
+        boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+        assertThat(isEligible).isTrue();
+    }
+
     private BackupEligibilityRules getBackupEligibilityRules(
             @BackupDestination int backupDestination) {
         return new BackupEligibilityRules(mPackageManager, mMockPackageManagerInternal, mUserId,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 147a44f..fafc035 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -190,7 +190,7 @@
         final WindowManagerService wms = mWm;
         final DisplayContent displayContent = mock(DisplayContent.class);
         doReturn(true).when(displayContent).isTrusted();
-        doReturn(true).when(displayContent).supportsSystemDecorations();
+        doReturn(true).when(displayContent).isHomeSupported();
         final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms);
         final TaskDisplayArea taskDisplayAreaWithHome = new TaskDisplayArea(displayContent, wms,
                 "Tasks1", FEATURE_DEFAULT_TASK_CONTAINER);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index e54b8e5..e2524a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -496,6 +496,19 @@
                 mPrivateDisplay.getDisplayInfo());
     }
 
+    @Test
+    public void testClearDisplaySettings() {
+        spyOn(mWm.mDisplayWindowSettings);
+        spyOn(mWm.mDisplayWindowSettingsProvider);
+
+        WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+        DisplayInfo info = mPrivateDisplay.getDisplayInfo();
+        wmInternal.clearDisplaySettings(info.uniqueId, info.type);
+
+        verify(mWm.mDisplayWindowSettings).clearDisplaySettings(info.uniqueId, info.type);
+        verify(mWm.mDisplayWindowSettingsProvider).clearDisplaySettings(info);
+    }
+
     public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider {
         Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>();
 
@@ -530,5 +543,10 @@
         public void onDisplayRemoved(@NonNull DisplayInfo info) {
             mOverrideSettingsCache.remove(info);
         }
+
+        @Override
+        public void clearDisplaySettings(@NonNull DisplayInfo info) {
+            mOverrideSettingsCache.remove(info);
+        }
     }
 }